from  generic.gn_request   import   Request # 
from  generic.gn_bancos    import   gn_bancos #
from datetime import datetime

import urllib3, json, time

from  time                import sleep

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)


#https://api.sandbox.bb.com.br/cobrancas/v2 - Servidor de Homologação
#client_id       = "eyJpZCI6IjZhYTEyZDMtZmU5NS00NDQxLTg1ZTgiLCJjb2RpZ29QdWJsaWNhZG9yIjowLCJjb2RpZ29Tb2Z0d2FyZSI6Mzc0MjQsInNlcXVlbmNpYWxJbnN0YWxhY2FvIjoxfQ"
#client_secret   = "eyJpZCI6ImI1IiwiY29kaWdvUHVibGljYWRvciI6MCwiY29kaWdvU29mdHdhcmUiOjM3NDI0LCJzZXF1ZW5jaWFsSW5zdGFsYWNhbyI6MSwic2VxdWVuY2lhbENyZWRlbmNpYWwiOjEsImFtYmllbnRlIjoiaG9tb2xvZ2FjYW8iLCJpYXQiOjE2NTQ3Nzk3NzYzMDJ9"

class cobranca_bb(gn_bancos):
    
    def __init__(self, argv:list):
        self.response = None
        self.db = False
        self.output_count = 0
        self.suffix = "generic_" 
        self.unparsed_data = {}
        self.setup(argv)
        
        self.rote_handler()
        client_id       =   self.get_token('client_id')
        client_secret   =   self.get_token('client_secret')
        
        environment = '.hm' if self.environment_config.get('ambiente') == 'H' else ''
        urls   = self.get_url_environment(environment=environment)
        auth   = self.generateOAuth2Token(urls.get('oAuth2'), client_id, client_secret )
        
        self.cobranca_bb_request = Request(
                base_url = urls.get('base_url')
            ,   params   = {
                **self.get_token('gw-dev-app-key')
                #'gw-dev-app-key': 'd27b677902ffabb01369e17d00050a56b991a5b9'
            },
            verify= False,
            headers={
                'Content-Type': 'application/json',
                'Authorization': f"{auth.get('token_type')} {auth.get('access_token')}"
            }
        )
        super(cobranca_bb, self, ).__init__(argv)
        
        
        print('Fim')
        
    
    def get_url_environment(self, environment='.hm'):
        base = f'{environment}.bb.com.br'
        return {
            "oAuth2"    :f"https://oauth{base}/oauth/token",
            "base_url"  :f"https://api{base}/cobrancas/v2/"
        }
    
    
    def generateOAuth2Token(self, url, client_id, client_secret):
        """Api do banco do brasil requer autenticacao OAuth2
        Args:
            url (str): endpoint de autenticacao 
            client_id (str): cliente id fornecido dentro do portal BB
            client_secret (str): cliente secret fornecido dentro do portal BB
        """
        import base64
        from urllib3.exceptions import NewConnectionError, MaxRetryError
        
        Authorization = "Basic " + base64.b64encode(
            bytes('{client_id}:{client_secret}'.format(
                    **client_id, **client_secret
                    ), encoding='utf8'
                )
        ).decode("utf-8")
        
        OAuth2 = Request(
            method='POST',
            verify=False,
            headers={
                "Authorization": Authorization,
                "Content-Type": "application/x-www-form-urlencoded",
            },
            data={
                "grant_type": "client_credentials",
                "scope": "cobrancas.boletos-requisicao cobrancas.boletos-info"
            }
        ).doRequest(url=url+"/")
        
        if not OAuth2.ok: 
            error_msg = f"HTTP {OAuth2.status_code}: {OAuth2.text}"
            print(f"Erro na autenticação OAuth2: {error_msg}")
            self.write_file(f"ER|{error_msg}|", self.saida_cobol, flag="w+", encoding='iso-8859-1')
            exit(1)
        
        print("Autenticação OAuth2 realizada com sucesso")
        return OAuth2.json()
    
    def post_route_handler_boletos(self):
        from time import sleep

        #""" Rota de inclusao e alteracao de boleto tratamento de envio"""
        data = self.txt_to_dict()
        for request in data.get('C'):
            self.unparsed_data  = request
            request_structure   = self.atualWs.get('request')
            
            data_parsed  = self.parser(request, request_structure)
            #data_parsed  = self.parser(request, request_structure)
            self.handle_send(data_parsed)

            sleep(4)
                

    def beforeSend_boletos_id(self, data):
        nosso_nro = self.unparsed_data.get('nosso_nro')['valor']

        self.current_rote = "boletos/{nosso_nro}".format(nosso_nro=nosso_nro)

        return data
        
    def handle_output_boletos(self, data):
        #"""Tratamento de retorno dos metodos PATCH e POST de boletodos"""
        output = "|".join([ str(value) for key,value in data.items()]) + "\n"

        if(output):
            flag = "w+" if self.output_count == 0 else "a+"
            self.write_file(output, self.saida_cobol ,flag= flag, encoding='iso-8859-1' )
            self.output_count = +1
        
    
    # """ Rota para alteracao de boletodos tratamento de envio"""
    def patch_route_handler_boletos_id(self):
        self.post_route_handler_boletos()
        
    
    def handle_output_boletos_id(self, data):
        self.handle_output_boletos(data)
        
        
    # """ Rota para baixa de boletodos tratamento de envio"""
    def post_route_handler_boletos_id_baixar(self):
        self.unparsed_data  = self.rote_data['query']
        self.handle_send(self.rote_data['query'])
        
    def handle_output_boletos_id_baixar(self, data):
         self.handle_output_boletos(data)
        
    """ def db_data_boletos(self, data):
        rote_filter = self.rote_data.get('filter')
        if(rote_filter):
            return list(filter(lambda item: int(item.get('nro_remessa')) == int(rote_filter.get('remessa')), data))
        
        return None 
        
    def db_data_boletos_id(self, data):
        return self.db_data_boletos(data)
    """
    
    def format_date_bb(self, value):
        """ Metodo para formatacao de dadas para o BB"""
        if value:
            date = datetime.strptime(value,"%Y-%m-%d")
            return  datetime.strftime(date,"%d.%m.%Y")
            
        return ""
        
    
    def custom_flag_status(self, value, key):
        return "OK" if not value else "ER"
    
    def custom_desc_status(self, value, key):
        return value
        
    #"Metodos com valores padrao"
    def custom_default_value(self, data, item):
        default_values = {
                "numeroTituloCliente": "nosso_nro"           
            ,   "numeroCarteira": "nro_carteira"        
            ,   "numeroVariacaoCarteira": "variacao_carteira"   
            ,   "numeroTituloBeneficiario": "nro_titulo_benef"    
        }

        try:
            return data or self.unparsed_data.get(default_values.get(item.get('from',''),''),'').get('valor')    
        except Exception as e:
            import json, unidecode

            others_fields = "{nosso_nro}||{variacao_carteira}|{nro_titulo_benef}".format(
                nosso_nro = self.unparsed_data.get('nosso_nro', {}).get('valor', '')
                , nro_titulo_benef = self.unparsed_data.get('nro_titulo_benef', {}).get('valor', '')
                , variacao_carteira = self.unparsed_data.get('variacao_carteira', {}).get('valor', '')
            )

            if self.api_request.req.status_code in [400, 500]:
                resposta = self.api_request.req.text
                dados = json.loads(resposta)
                mensagens = ','.join([erro["mensagem"] for erro in dados.get("erros", [])])
                mensagens = unidecode.unidecode(mensagens)

                self.write_file(f"ER|{mensagens}|{others_fields}", self.saida_cobol ,flag= "w+", encoding='iso-8859-1')
                exit()

            if self.api_request.req.status_code == 401:
                resposta = self.api_request.req.text
                dados = json.loads(resposta)
                mensagens = dados.get("message")
                mensagens = unidecode.unidecode(mensagens)

                self.write_file(f"ER|{mensagens}|{others_fields}", self.saida_cobol ,flag= "w+", encoding='iso-8859-1')
                exit()

            if self.api_request.req.status_code == 503:
                self.write_file(f"ER|SERVIÇO INDISPONÍVEL|{others_fields}", self.saida_cobol ,flag= "w+", encoding='iso-8859-1')
                exit()
            

        
        return data or self.unparsed_data.get(default_values.get(item.get('from',''),''),'').get('valor')
        
        
    def custom_default_value_patch_boletos(self, data, item):
        default_values = {
            "nosso_nro"           : 'nosso_nro/valor',
            "nro_titulo_benef"    : 'nro_titulo_benef/valor',
            "cod_instrucao"       : 'cod_instrucao/valor',
        }
        return data or self.jsonValueByPath(default_values.get(item.get('from','')), self.unparsed_data) #self.file_data.get(default_values.get(key.get('from',''),''),'')
        
    
    def custom_default_value_post_boletos_baixar(self, data, item):
        default_values = {
            "numeroConveio"           : 'numeroConveio',
        }
        return data or self.jsonValueByPath(default_values.get(item.get('from','')), self.unparsed_data) #self.file_data.get(default_values.get(key.get('from',''),''),'')
        
        
    
    def pagination_rules(self, response, data):
        #'pedidos/?status=2&alterado_apos=2021-07-12 15:41:29'
        if response.ok:
            body = self.tryJson(response)
            if body.get('indicadorContinuidade') == 'S':
                query = self.rote_data.get('query')
                query['indice'] = body.get('proximoIndice')
                params = self.mountRouteParams(query)
                return f"boletos?{params}"
            return None
        return None
        
    def afterGet_boletos(self, data):
        return data.get('boletos',[])
        
    def beforeSend_boletos(self, data):
        default_values = {
                "numero"                    : 'numeroTituloCliente'
            ,   "numeroCarteira"            : 'numeroCarteira'
            ,   "numeroVariacaoCarteira"    : 'numeroVariacaoCarteira'
            ,   "numeroTituloBeneficiario"  : 'numeroTituloBeneficiario'
            
        }
        
        return data or self.current_data.get(default_values.get(item.get('from',''),''),'')
        
        
    #TRATAMENTO DO GET
    def boletos_handler(self, boletos):
        text = ''
        flag = "w+"
        if not boletos and not self.response.ok:
            status = self.response.status_code
            erros = (self.tryJson(self.response) or {}).get('erros', [])
            erros = (self.get_index(erros,0) or {}).get('textoMensagem', 'houve um erro durante a execucao')

            if "Não existem boletos a serem listados." in erros:
                text = "AL|Não existem boletos a serem listados.|"
            else:
                text = f"ER|{status}-{erros}|"
        
        else:
            try:
                boletos = boletos[-1000:]
                texts = []
                for index, boleto in enumerate(boletos):
                    #is_new_line = "\n" if (index + 1) != total else ""
                    flag = "w+" if not index else "a+"
                    texts.append("OK||" + ("|".join(str(dado) for dado in boleto.values())))
                
                text = "\n".join(texts)
            except Exception as er:
                text = f"ER|{str(er)}|"
            
            
        self.write_file(text , self.saida_cobol ,flag= flag, encoding='iso-8859-1')
    
    def rote_filter_boletos(self,):
        query = self.rote_data.get('query')
        if query:
            return self.mountRouteParams(query)
            
        return None
        
        
    
    def rote_filter_boletos_id(self,):
        return self.rote_filter_boletos()
        
    
    #"Tratamento de retorno do GET"
    def boletos_id_handler(self, boleto):
        if self.response.status_code == 404:
            self.write_file("OK|Boleto nao registrado", self.saida_cobol ,flag= "w+", encoding='iso-8859-1' )
            return
        
        if not self.response.ok:
            error   = "ER|{}: {}|".format(self.response.status_code, self.response.text)

            self.write_file(error, self.saida_cobol ,flag= "w+", encoding='iso-8859-1' )
            return
        
        text = "OK||" + ("|".join(str(dado) for dado in boleto.values()))

        consulta_qrcode = self.cobranca_bb_request.doRequest(
            method='GET',
            rote="boletos/{nosso_nro}/pix?numeroConvenio={numeroConvenio}".format(
                nosso_nro=self.rote_data['path']['id'], 
                numeroConvenio=self.rote_data['query']['numeroConvenio']
            )
        )

        if consulta_qrcode.ok:
            dados_boleto_pix = consulta_qrcode.json()
            text += "|{qrCodeUrl}|{qrCodeTxId}|{qrCodeEmv}".format(
                qrCodeUrl = dados_boleto_pix.get('qrCode.url', '')
                , qrCodeTxId = dados_boleto_pix.get('qrCode.txId', '')
                , qrCodeEmv = dados_boleto_pix.get('qrCode.emv', '')
            )
        else: 
            text += "|||"

        self.write_file(text + "\n", self.saida_cobol ,flag= "w+", encoding='iso-8859-1' )
        
        
    
    def format_rote_boletos_id(self, rote):
        path_params = self.rote_data.get('path')
        if path_params:
            return rote.format(**path_params)
        
        return None
    
    def throttling(self, **params) :
        while True:
            req    =  self.api_request.doRequest(**params)

            if not req.ok : 
                log_data = {
                    "request": {
                        "url": f"{getattr(req, 'url', None)}" ,
                        "method": f"{getattr(req.request, 'method', None) if hasattr(req, 'request') else None}",
                        "headers": dict(getattr(req.request, 'headers', {})) if hasattr(req, 'request') else None,
                        "body": f"{getattr(req.request, 'body', None) if hasattr(req, 'request') else None}",
                    },
                    "response": {
                        "status_code": f"{getattr(req, 'status_code', None)}",
                        "headers": dict(getattr(req, 'headers', {})),
                        "body": f"{getattr(req, 'text', None)}",
                    }
                }

                arquivo_log = self.saida_cobol.replace('.txt', '_log.json')

                with open(arquivo_log, "a", encoding="utf-8") as f:
                    import json
                    dados = json.dumps(log_data, ensure_ascii=False, indent=2)
                    f.write(f"{dados}\n")
            
            try:
                error = json.loads(req.text)

                if error['error'] == "Bad Request: Rate limit exceeded, retry in 1 second":
                    print(req)
            except:
                pass
            
            throttling_time = self.call("throttling_rules",req)

            if not throttling_time :
                break
            
            print(throttling_time)
            
        return req 
        
    #! NÃO SERA PRECISO GERAR O QRCODE POR AQUI
    """ def gerarCrc16(self, payload):
        crc16 = crcmod.mkCrcFun(poly=0x11021, initCrc=0xFFFF, rev=False, xorOut=0x0000)
        self.crc16Code = hex(crc16(str(payload).encode('utf-8')))
        self.crc16Code_formatado = str(self.crc16Code).replace('0x', '').upper()
        self.payload_completa = f'{payload}{self.crc16Code_formatado}'
        self.gerarQrCode(self.payload_completa)
        
    
    def gerarQrCode(self, payload):
        import qrcode
        self.qrcode = qrcode.make(payload)
        self.qrcode.save('logs/pixqrcode.png')
        
        return print(payload) """