import time, uuid
from  generic.gn_request   import   Request # 
from  generic.gn_itau    import   gn_itau #
from datetime import datetime
from decimal import Decimal, ROUND_HALF_UP
import json
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)


class cobranca_itau(gn_itau):
    
    def __init__(self, argv:list):
        self.response = None
        self.db = False
        self.output_count = 0
        self.suffix = "app_generic" 
        self.unparsed_data = {}
        self.setup(argv)
        
        self.rote_handler()
        
        clientID                    = self.get_token_data('ClientID')
        clientSecret                = self.get_token_data('ClientSecret')
        caminhoCertificado          = self.get_token_data('CaminhoCertificado')
        caminhoCertificadoChave     = self.get_token_data('CaminhoCertificadoChave')
        url                         = self.environment_config.get('url')

        if self.rote == "boletos" and self.method == "GET":
            url = "https://secure.api.cloud.itau.com.br/boletoscash/v2/"
        
        if self.rote == "instrucao" and self.method == "PATCH":
            url = "https://api.itau.com.br/cash_management/v2/boletos/"

        if self.rote == "pix":
            url = "https://secure.api.itau/pix_recebimentos/v2/"


        self.environment = 'homolog' if self.environment_config.get('ambiente') == 'H' else ''
        
        auth = self.generateToken(clientID, clientSecret, caminhoCertificado, caminhoCertificadoChave)
        
        cert = ( caminhoCertificado, caminhoCertificadoChave )
        
        self.cobranca_itau_request = Request(
                base_url = url
            ,   params   = {}
            ,   verify= False
            ,   headers={
                    # 'X-Application-Key': clientID,
                    'x-itau-apikey': clientID,
                    'x-itau-correlationID': str(uuid.uuid4()),
                    'Authorization': f"Bearer {auth.get('access_token')}",
                    'content-type': 'application/json'
                }
            , cert = cert
        )
         
        super(cobranca_itau, self, ).__init__(argv)
        
        
        print('Fim')

    def refreshToken(self):

        clientID                    = self.get_token_data('ClientID')
        clientSecret                = self.get_token_data('ClientSecret')
        caminhoCertificado          = self.get_token_data('CaminhoCertificado')
        caminhoCertificadoChave     = self.get_token_data('CaminhoCertificadoChave')
        url                         = self.environment_config.get('url')

        if self.rote == "boletos" and self.method == "GET":
            url = "https://secure.api.cloud.itau.com.br/boletoscash/v2/"
        
        if self.rote == "instrucao" and self.method == "PATCH":
            url = "https://api.itau.com.br/cash_management/v2/boletos/"

        self.environment = 'homolog' if self.environment_config.get('ambiente') == 'H' else ''
        
        auth = self.generateToken(clientID, clientSecret, caminhoCertificado, caminhoCertificadoChave)
        
        cert = ( caminhoCertificado, caminhoCertificadoChave )
        
        self.cobranca_itau_request = Request(
                base_url = url
            ,   params   = {}
            ,   verify= False
            ,   headers={
                    # 'X-Application-Key': clientID,
                    'x-itau-apikey': clientID,
                    'x-itau-correlationID': str(uuid.uuid4()),
                    'Authorization': f"Bearer {auth.get('access_token')}",
                    'content-type': 'application/json'
                }
            , cert = cert
        )
        
    def generateToken(self, client_id, client_secret, certificado, chave_certificado):
        cert = ( certificado, chave_certificado )
        
        OAuth2 = Request(
                base_url = "https://sts.itau.com.br"
            ,   verify= False
            ,   data={
                    "grant_type" : "client_credentials",
                    "client_id" : client_id,
                    "client_secret" : client_secret,
                }
            , cert = cert
        ).doRequest(url="https://sts.itau.com.br/api/oauth/token");
        
        if not OAuth2.ok: 
            self.createLog('APIERROR',f"ERRO ao criar token - Necessario criacao de novo token {OAuth2.text}")
            exit(1)
        
        return OAuth2.json()
    
    #    ####   ######    ######    ###    ######            ######    #####   ####     #######   # #####  #####'
    #   ##  ##   ##  ##     ##     ## ##    ##  ##            ##  ##  ### ###   ##       ##   #  ## ## ## ### ###'
    #  ##        ##  ##     ##    ##   ##   ##  ##            ##  ##  ##   ##   ##       ##         ##    ##   ##'
    #  ##        #####      ##    ##   ##   #####             #####   ##   ##   ##       ####       ##    ##   ##'
    #  ##        ## ##      ##    #######   ## ##             ##  ##  ##   ##   ##       ##         ##    ##   ##'
    #   ##  ##   ## ##      ##    ##   ##   ## ##             ##  ##  ### ###   ##  ##   ##   #     ##    ### ###'
    #    ####   #### ##   ######  ##   ##  #### ##           ######    #####   #######  #######    ####    #####'
                
    def post_route_handler_boletos_pix(self):
        #""" 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)
            self.dados_requisicao = request
            #data_parsed  = self.parser(request, request_structure)
            self.handle_send(data_parsed)

    def beforeSend_boletos_pix(self, data):
        dados_individuais_boleto = data['dado_boleto']['dados_individuais_boleto']
        
        data['dado_boleto']['dados_individuais_boleto'] = []
        data['dado_boleto']['dados_individuais_boleto'].append(dados_individuais_boleto)

        if 'negativacao' in data['dado_boleto']:
            if data['dado_boleto']['negativacao']['negativacao'] == 'false':
                del data['dado_boleto']['negativacao']


        if 'desconto' in data['dado_boleto']:
            codigo = data['dado_boleto']['desconto']['codigo_tipo_desconto']

            descontos = data['dado_boleto']['desconto']['descontos']

            data['dado_boleto']['desconto']['descontos'] = []
            data['dado_boleto']['desconto']['descontos'].append(descontos)

            if codigo == "00":
                del data['dado_boleto']['desconto']

        if 'juros' in data['dado_boleto']:
            codigo = data['dado_boleto']['juros']['codigo_tipo_juros']

            if codigo == "00":
                del data['dado_boleto']['juros']

        if 'multa' in data['dado_boleto']:
            if data['dado_boleto']['multa']['codigo_tipo_multa'] == "03":
                del data['dado_boleto']['multa']['percentual_multa']

        if 'instrucao_cobranca' in data['dado_boleto']:
            instrucao = data['dado_boleto']['instrucao_cobranca']
            ic_dig_util = self.unparsed_data.get('ic_dig_util', {}).get('valor', '')

            if ic_dig_util:
                instrucao['dia_util'] = False if ic_dig_util == 'false' else True

            data['dado_boleto']['instrucao_cobranca'] = []
            data['dado_boleto']['instrucao_cobranca'].append(instrucao)

    def handle_output_boletos_pix(self, data):
        #"""Tratamento de retorno dos metodos PATCH e POST de boletodos"""
        import unidecode 
        
        status = "OK" if self.api_request.req.status_code == 200 else "ER"
        desc_status = "Enviado com sucesso" 
        extra_err_status = ""
        
        if self.api_request.req.status_code != 200:
            retorno = self.api_request.req.text
            desc_status = "Verificar dados da solicitação"

            if self.api_request.req.status_code in [400, 422]:

                dados = json.loads(self.api_request.req.text)

                desc_status = f"{desc_status}: {dados.get('mensagem', '')}"
                
                mensagens = []

                if dados.get("campos", []):
                    mensagens = [campo["mensagem"] for campo in dados["campos"]]

                extra_err_status = " - {}" .format(",".join(mensagens))
            else:
                try:
                    extra_err_status = " - {}".format(retorno)
                except:
                    extra_err_status = ""
            
        nossoNumero = self.dados_requisicao.get('b_nosso_nr')['valor']
        tituloBenef = self.dados_requisicao.get('tit_benef')['valor']
        
        structure = f"{status}|{unidecode.unidecode(desc_status)}{unidecode.unidecode(extra_err_status)}|{nossoNumero}|||{tituloBenef}||LINHA_DIGITAVEL|COD_BARRA_NUM||||QR_TXID|QR_EMV||\n"
        
        if status == "OK": 
            dados = json.loads(self.api_request.req.text)
            boleto = dados['data']['dado_boleto']['dados_individuais_boleto'][0]
            qrcode = dados['data']['dados_qrcode']

            structure = structure.replace("LINHA_DIGITAVEL", boleto['numero_linha_digitavel'])
            structure = structure.replace("COD_BARRA_NUM", boleto['codigo_barras'])
            structure = structure.replace("QR_TXID", qrcode['txid'])
            structure = structure.replace("QR_EMV", qrcode['emv'])
        else: 
            structure = structure.replace("LINHA_DIGITAVEL", "")
            structure = structure.replace("COD_BARRA_NUM", "")
            structure = structure.replace("QR_TXID", "")
            structure = structure.replace("QR_EMV", "")
            
        flag = "w+" if self.output_count == 0 else "a+"
        self.write_file(structure, self.saida_cobol ,flag= flag, encoding='iso-8859-1' )
        self.output_count += 1
        

    #    ####    #####   ##   ##   #####   ##   ##  ####      # #####   ###'
    #   ##  ##  ### ###  ###  ##  ##   ##  ##   ##   ##      ## ## ##  ## ##'
    #  ##       ##   ##  #### ##  ##       ##   ##   ##         ##    ##   ##'
    #  ##       ##   ##  #######   #####   ##   ##   ##         ##    ##   ##'
    #  ##       ##   ##  ## ####       ##  ##   ##   ##         ##    #######'
    #   ##  ##  ### ###  ##  ###  ##   ##  ##   ##   ##  ##     ##    ##   ##'
    #    ####    #####   ##   ##   #####    #####   #######    ####   ##   ##'


        
    def beforeSend_boletos(self, data):
        rota_atual = self.atualWs['rote']
        
        dados = self.unparsed_data
        
        nosso_nro = dados.get('nosso_nr')['valor']
        id_benef = dados.get('id_benef')['valor']
        carteira = dados.get('carteira')['valor']
        
        rota_atual = "{}?id_beneficiario={}&nosso_numero={}&codigo_carteira={}&view=specific".format(rota_atual, id_benef, nosso_nro, carteira)
        
        self.current_rote = rota_atual
    
    def boletos_handler(self, boletos):
        
        data = self.txt_to_dict()
        for index, request in enumerate(data.get('C')):
            self.unparsed_data  = request

            self.handle_send({})

            # Bloco de tratamento para o 429
            if index > 0 and index % 50 == 0:
                print(":batchSize |> Atingiu o limite de 50 requests, fazendo um sleep de 30secs")

                if index > 0 and index % 100 == 0:
                    print(": batchSize |> Gerando um token novo para evitar o erro 401/403")
                    self.refreshToken()

                time.sleep(30)

            
                
                   
    def handle_output_boletos(self, data):
        
        status = "OK" if self.api_request.req.status_code == 200 else "ER"
        desc_status = "Consultado com sucesso" 
        
        if self.api_request.req.status_code != 200:
            retorno = json.loads(self.api_request.req.text)
            desc_status = f"{self.api_request.req.status_code} - {retorno['message']}" 
        
        estrutura = f"{status}|{desc_status}||||||"
            
        if self.api_request.req.ok and self.api_request.req.status_code == 200:
            req_json = json.loads(self.api_request.req.text)

            if len(req_json['data']) == 0:
                self.createLog(404, f"BOLETO NAO ENCONTRADO NO BANCO - {self.current_rote}")
            else:
                dados = req_json['data'][-1]['dado_boleto']
                boleto = dados['dados_individuais_boleto'][0]

                data_vencimento = datetime.strptime(boleto['data_vencimento'], '%Y-%m-%d')

                estrutura = f"{status}|{desc_status}|{boleto['numero_nosso_numero']}|{data_vencimento.strftime('%Y%m%d')}|{boleto['valor_titulo']}"

                if 'pagamentos_cobranca' in dados:
                    pagamento = dados['pagamentos_cobranca'][0]
                    data_inclusao_pagamento = datetime.strptime(pagamento['data_inclusao_pagamento'], '%Y-%m-%dT%H:%M:%S')

                    estrutura = f"{estrutura}|{boleto['situacao_geral_boleto']}|{data_inclusao_pagamento.strftime('%Y%m%d')}|{pagamento['valor_pago_total_cobranca']}|{pagamento['valor_pago_desconto_cobranca']}|{pagamento['valor_pago_multa_cobranca']}|{pagamento['valor_pago_juro_cobranca']}|{pagamento['valor_pago_abatimento_cobranca']}"
                elif 'baixa' in dados: 
                    pagamento = dados['baixa']
                    data_inclusao_alteracao_baixa = datetime.strptime(pagamento['data_inclusao_alteracao_baixa'], '%Y-%m-%dT%H:%M:%S')
                    
                    estrutura = f"{estrutura}|{pagamento['motivo_baixa']}|{data_inclusao_alteracao_baixa.strftime('%Y%m%d')}|{boleto['valor_titulo']}"
                else:
                    estrutura = f"{estrutura}|{boleto['situacao_geral_boleto']}||"
        
        flag = "w+" if self.output_count == 0 else "a+"
        self.write_file(f"{estrutura}\n", self.saida_cobol ,flag= flag, encoding='iso-8859-1' )
        self.output_count += 1


        #    ####    #####   ##   ##   #####   ##   ##  ####      # #####   ###'
    #   ##  ##  ### ###  ###  ##  ##   ##  ##   ##   ##      ## ## ##  ## ##'
    #  ##       ##   ##  #### ##  ##       ##   ##   ##         ##    ##   ##'
    #  ##       ##   ##  #######   #####   ##   ##   ##         ##    ##   ##'
    #  ##       ##   ##  ## ####       ##  ##   ##   ##         ##    #######'
    #   ##  ##  ### ###  ##  ###  ##   ##  ##   ##   ##  ##     ##    ##   ##'
    #    ####    #####   ##   ##   #####    #####   #######    ####   ##   ##'


        
    def beforeSend_pix(self, data):
        rota_atual = self.atualWs['rote']
        
        dados = self.rote_data['path']
        
        txid = dados.get('txid', '')
        
        rota_atual = "cob/{}".format(txid)
        
        self.current_rote = rota_atual
    
    def pix_handler(self, boletos):
        self.handle_send({})
                   
    def handle_output_pix(self, data):
        
        status = "OK" if self.api_request.req.status_code == 200 else "ER"
        desc_status = "Consultado com sucesso" 
        
        if self.api_request.req.status_code != 200:
            retorno = json.loads(self.api_request.req.text)
            desc_status = f"{self.api_request.req.status_code} - {retorno['message']}" 
        
        estrutura = f"{status}|{desc_status}||||||"
            
        if self.api_request.req.ok and self.api_request.req.status_code == 200:
            req_json = json.loads(self.api_request.req.text)

            try:
                existe_inf_pix = req_json.get('pix', None)

                if not existe_inf_pix:
                    estrutura = f"ER|495 - Informacoes do PIX nao encontrada (e2eid)|||||||||"
                    flag = "w+" if self.output_count == 0 else "a+"
                    self.write_file(f"{estrutura}\n", self.saida_cobol ,flag= flag, encoding='iso-8859-1' )
                    self.output_count += 1
                    return

                e2eid = req_json['pix'][0]['endToEndId']

                detalhada = self.cobranca_itau_request.doRequest(
                    rote=f"pix/{e2eid}"
                )

                if detalhada.ok and detalhada.status_code == 200:

                    dados = json.loads(detalhada.text)

                    # 15 WC-APISQL-RET-PIX-341-FLAG-ST     PIC  X(002).   
                    flag = 'OK'
                    # 15 WC-APISQL-RET-PIX-341-DESC-ST     PIC  X(200).   
                    desc_status = "Consultado com sucesso"
                    # 15 WC-APISQL-RET-PIX-341-VLR-PGTO    PIC  9(014)V99.
                    valor_pagamento = dados.get('valor', '')
                    # 20 WC-APISQL-RET-PIX-341-VLR-ORIG PIC  9(014)V99.
                    valor_original = dados.get('componentesValor', {}).get('original', {}).get('valor', '')
                    # 20 WC-APISQL-RET-PIX-341-SAQUE    PIC  9(014)V99.
                    valor_saque    = dados.get('componentesValor', {}).get('saque', {}).get('valor', '')
                    # 20 WC-APISQL-RET-PIX-341-TROCO    PIC  9(014)V99.
                    valor_troco    = dados.get('componentesValor', {}).get('troco', {}).get('valor', '')
                    # 20 WC-APISQL-RET-PIX-341-JUROS    PIC  9(014)V99.
                    valor_juros       = dados.get('componentesValor', {}).get('juros', {}).get('valor', '')
                    # 20 WC-APISQL-RET-PIX-341-MULTA    PIC  9(014)V99.
                    valor_multa       = dados.get('componentesValor', {}).get('multa', {}).get('valor', '')
                    # 20 WC-APISQL-RET-PIX-341-DESCONTO PIC  9(014)V99.
                    valor_desconto    = dados.get('componentesValor', {}).get('desconto', {}).get('valor', '')
                    # 20 WC-APISQL-RET-PIX-341-ABATIMEN PIC  9(014)V99.
                    valor_abatimento  = dados.get('componentesValor', {}).get('abatimento', {}).get('valor', '')

                    estrutura = f"{flag}|{desc_status}|{valor_pagamento}|{valor_original}|{valor_saque}|{valor_troco}|{valor_juros}|{valor_multa}|{valor_desconto}|{valor_abatimento}|"

                else:
                    estrutura = f"ER|Erro ao consultar detalhes do PIX|||||||||"
            except Exception as e:
                erro_msg = str(e) if str(e) else "Erro desconhecido"
                estrutura = f"ER|Erro ao consultar detalhes do PIX {str(erro_msg)}|||||||||"
        
        flag = "w+" if self.output_count == 0 else "a+"
        self.write_file(f"{estrutura}\n", self.saida_cobol ,flag= flag, encoding='iso-8859-1' )
        self.output_count += 1



    #   ######  ##   ##   #####    # ##### ######   ##   ##    ####     ###     #####'
    #     ##    ###  ##  ##   ##  ## ## ##  ##  ##  ##   ##   ##  ##   ## ##   ### ###'
    #     ##    #### ##  ##          ##     ##  ##  ##   ##  ##       ##   ##  ##   ##'
    #     ##    #######   #####      ##     #####   ##   ##  ##       ##   ##  ##   ##'
    #     ##    ## ####       ##     ##     ## ##   ##   ##  ##       #######  ##   ##'
    #     ##    ##  ###  ##   ##     ##     ## ##   ##   ##   ##  ##  ##   ##  ### ###'
    #   ######  ##   ##   #####     ####   #### ##   #####     ####   ##   ##   #####'

    def patch_route_handler_instrucao(self):
        #""" 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)
            self.dados_requisicao = request
            
            self.handle_send(data_parsed)

        
    def beforeSend_instrucao(self, data):
        rota_atual = "{boleto}/{operacao}"
        
        op = self.unparsed_data['operacao']['valor']
        boleto_id = self.unparsed_data['id_boleto']['valor']

        instrucao = ""

        if op == '02':
            instrucao = 'baixa'
        if op == "06":
            instrucao = 'data_vencimento'
        if op == "04" or op == "05":
            instrucao = "abatimento"
        if op == "09" or op == "10" or op == '18':
            instrucao = "protesto"

        rota_atual = rota_atual.format(operacao=instrucao, boleto=boleto_id)
        
        self.current_rote = rota_atual
    
    def instrucao_handler(self, boletos):
        
        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)
            self.dados_requisicao = request
            
            self.handle_send(data_parsed)
                   
    def handle_output_instrucao(self, data):

        tit_bef = self.unparsed_data['tit_benef']['valor']

        if self.api_request.req.status_code == 422 or self.api_request.req.status_code == 400:
            retorno = json.loads(self.api_request.req.text)
            desc_status = f"{retorno['codigo']} - {retorno['campos'][0]['mensagem']}"
            
            estrutura = f"ER|{desc_status}|{tit_bef}"

            flag = "w+" if self.output_count == 0 else "a+"
            self.write_file(f"{estrutura}\n", self.saida_cobol ,flag= flag, encoding='iso-8859-1' )
            self.output_count += 1

        else:

            status = {
                204: "Alteração/Instrução realizada com sucesso",
                201: "O recurso foi criado de forma síncrona",
                400: "parâmetros incorretos",
                401: "Não autorizado",
                403: "Acesso proibido",
                404: "Recurso inexistente",
                405: "Método não permitido",
                422: "Dados informados estão fora do escopo definido para o campo",
                428: "Pré-requisito necessário",
                500: "Erro inesperado",
                501: "Não implementado",
                503: "Serviço indisponível",
            }

            flag_status = "OK" if self.api_request.req.status_code == 204 else "ER"

            estrutura = f"{flag_status}|{status[self.api_request.req.status_code]}|{tit_bef}"

            flag = "w+" if self.output_count == 0 else "a+"
            self.write_file(f"{estrutura}\n", self.saida_cobol ,flag= flag, encoding='iso-8859-1' )
            self.output_count += 1
    
    def custom_flag_status(self, value, key):
        return "OK" if not value else "ER"
    
    def convert_to_boolean(self, value):
        return True if value == "S" else False
    
    def modo_debug(self, value):
        return value
    
    def to_string(self, value):
        if value == '':
            return

        if value == 0.0:
            return "0"

        return "{:.2f}".format(value)

    def format_value(self, value, data):
        valor_formatado = int(Decimal(str(value)) * 100)

        return f'{valor_formatado:017d}'
    
    def format_percent(self, value, data):
        valor_formatado = int(Decimal(str(value)) * 100000)

        return f'{valor_formatado:012d}'
    
    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