
import re, json
from   generic.gn_api       import   gn_api # 
from   generic.gn_request   import   Request # 
import datetime

import time, requests
from urllib.parse import urlparse, parse_qs

from pyvirtualdisplay import Display
import undetected_chromedriver as uc

class bling(gn_api):
    def __init__(self, argv:list):
        self.db = False
        self.pagina_atual = 1
    
        setup   = self.setup(argv)
        self.rote_handler()
        
        #self.ApplicationToken  = setup[0][  'NMTOKEN1API']#002e6fc5f068d6323874
        #self.CompanyToken      = setup[0]['NMTOKEN1DADOS']#b42241b9-0f49-4177-814d-ba3d30883bb3
        
        ClientSecret           = self.get_token_data('ClientSecret')
        ClientID               = self.get_token_data('ClientID')
        Usuario                = self.get_token_data('Usuario')
        Senha                  = self.get_token_data('Senha')
        RedirectURI            = self.get_token_data('RedirectURI')
        self.DepositoPadrao    = self.get_token_data('DepositoPadrao')
        access_token  = None
        refresh_token = None

        refreshToken = self.getRefreshToken()

        if refreshToken is None: 
            new_access_token, new_refresh_token = self.blingToken(ClientID, ClientSecret, Usuario, Senha, RedirectURI)

            access_token = new_access_token
            refresh_token = new_refresh_token

            self.saveRefreshToken(refresh_token)
        else: 
            novo_access_token, novo_refresh_token = self.blingTokenWithRefresh(ClientID, ClientSecret, refreshToken)

            access_token = novo_access_token
            refresh_token = novo_refresh_token

            if not access_token and not refresh_token:
                new_access_token, new_refresh_token = self.blingToken(ClientID, ClientSecret, Usuario, Senha, RedirectURI)

                access_token = new_access_token
                refresh_token = new_refresh_token
            
            self.saveRefreshToken(refresh_token)

        if not access_token and not refresh_token:
            self.createLog(671, "Erro ao obter os tokens de acesso. Verifique as credenciais e a configuracao do navegador.")
            exit(1)
        
        self.bling_request = Request(
            base_url = 'https://api.bling.com.br/Api/v3/',
            headers={
                'Content-Type'     : 'application/json',
                'Accept'           : 'application/json',
                'Authorization'    : f'Bearer {access_token}'
            }
        ) 
        
        super(bling, self, ).__init__()
        
        print('Fim')

    def deleteRefreshToken(self):
        try:
            query_delete = """
                DELETE FROM 
                    `TBTOKEN` 
                WHERE 
                    `lkgrupo` = '{nmlkgrupo}' AND 
                    `empresa` = {empresa} AND 
                    `filial` = {filial} AND 
                    `nome_api` = '{nome_api}' AND 
                    `tipo_token` = 'T' AND 
                    `nome_token` = 'RefreshTokenAtualizado'
            """.format(
                nmlkgrupo=self.nmlkgrupo.upper(),
                empresa=int(self.cdempresa),
                filial=int(self.cdfilial),
                nome_api=self.apiName
            )

            delete_row = self.getDb().executesql(query_delete)

            self.createLog("999", "Refresh Token deletado com sucesso.")

        except Exception as e:
            self.createLog("999", f"Erro ao deletar o Refresh Token: {e}")
            print(e)

    def saveRefreshToken(self, refresh_token):

        try:
            query_check_exist = """
                SELECT COUNT(*) AS count
                FROM `TBTOKEN`
                WHERE 
                    `lkgrupo` = '{nmlkgrupo}' AND 
                    `empresa` = {empresa} AND 
                    `filial` = {filial} AND 
                    `nome_api` = '{nome_api}' AND 
                    `tipo_token` = 'T' AND 
                    `nome_token` = 'RefreshTokenAtualizado' 
            """.format(
                nmlkgrupo=self.nmlkgrupo.upper(), 
                empresa=int(self.cdempresa), 
                filial=int(self.cdfilial), 
                nome_api=self.apiName
            )

            check_exist = self.getDb().executesql(query_check_exist)

            if check_exist[0]._rows[0]['count'] == 0:

                query_insert_if_no_exist = """
                    INSERT INTO `TBTOKEN` (
                        `lkgrupo`, 
                        `empresa`, 
                        `filial`, 
                        `nome_api`, 
                        `tipo_token`, 
                        `nome_token`, 
                        `valor_token`, 
                        `url_token`
                    ) VALUES (
                        '{nmlkgrupo}', 
                        {empresa}, 
                        {filial}, 
                        '{nome_api}', 
                        '{tipo_token}', 
                        '{nome_token}', 
                        '{valor_token}', 
                        '{url_token}'
                    )
                """.format(
                    nmlkgrupo=self.nmlkgrupo.upper(), 
                    empresa=int(self.cdempresa), 
                    filial=int(self.cdfilial), 
                    nome_api=self.apiName, 
                    tipo_token="T", 
                    nome_token="RefreshTokenAtualizado", 
                    valor_token=refresh_token, 
                    url_token="https://developer.bling.com.br/api/bling"
                )

                insert_row = self.getDb().executesql(query_insert_if_no_exist)

                self.createLog("999", "Refresh Token inserido com sucesso.")

            else:

                query_update = """
                    UPDATE `TBTOKEN` 
                    SET `valor_token` = '{refresh_token}' 
                    WHERE 
                        `lkgrupo` = '{nmlkgrupo}' AND 
                        `empresa` = {empresa} AND 
                        `filial` = {filial} AND 
                        `nome_api` = '{nome_api}' AND 
                        `tipo_token` = 'T' AND
                        `nome_token` = 'RefreshTokenAtualizado'
                """.format(
                    refresh_token=refresh_token,
                    nmlkgrupo=self.nmlkgrupo.upper(),
                    empresa=int(self.cdempresa),
                    filial=int(self.cdfilial),
                    nome_api=self.apiName
                )

                update_row = self.getDb().executesql(query_update)

                self.createLog("999", "Refresh Token atualizado com sucesso.")
        except Exception as e:
            self.createLog("999", f"Erro ao salvar o Refresh Token: {e}")
            print(e)

    def getRefreshToken(self):
        try:
            query = """
                SELECT `valor_token` 
                FROM `TBTOKEN` 
                WHERE 
                    `lkgrupo` = '{nmlkgrupo}' AND 
                    `empresa` = {empresa} AND 
                    `filial` = {filial} AND 
                    `nome_api` = '{nome_api}' AND 
                    `tipo_token` = 'T' AND 
                    `nome_token` = 'RefreshTokenAtualizado'
            """.format(
                nmlkgrupo=self.nmlkgrupo.upper(), 
                empresa=int(self.cdempresa), 
                filial=int(self.cdfilial), 
                nome_api=self.apiName
            )

            result = self.getDb().executesql(query)

            if result and len(result) > 0:
                return result[0]._rows[0]['valor_token']
            else:
                return None
        except Exception as e:
            self.createLog("999", f"Erro ao obter o Refresh Token: {e}")
            return None

    def blingToken(self, client_id, client_secret, usuario, senha, redirect_uri):
        import requests
        from requests.auth import HTTPBasicAuth
        
        gerador = GeradorCode(client_id, redirect_uri, usuario, senha)
        code = gerador.gerar_code()

        if not code:
            self.createLog("999", "Erro ao gerar o código de autorização. Verifique as credenciais e a configuração do navegador.")
            exit(1)

        url = "https://www.bling.com.br/Api/v3/oauth/token"

        payload = {
            "grant_type": "authorization_code",
            "code":  code,
            "redirect_uri":  redirect_uri,
        }

        client_id = client_id
        client_secret = client_secret
        
        print("\nEnviando requisição...")
        
        try:
            response = requests.post(
                url,
                data=payload,
                auth=HTTPBasicAuth(client_id, client_secret),
                headers={
                    "Content-Type": "application/x-www-form-urlencoded",
                    "Accept": "1.0",
                }
            )
            
            if response.status_code == 200:
                tokens = response.json()
                
                return tokens.get('access_token', 'N/A'), tokens.get('refresh_token', 'N/A')
            else:
                return None, None
                
        except Exception as e:
            print(f"\n❌ Erro ao fazer requisição: {e}")
            return None
    
    def blingTokenWithRefresh(self, client_id, client_secret, refresh_token):
        import base64
        import requests


        url = "https://www.bling.com.br/Api/v3/oauth/token"
        
        # Authorization: Basic base64(client_id:client_secret)
        auth = f"{client_id}:{client_secret}"
        auth_b64 = base64.b64encode(auth.encode()).decode()

        headers = {
            "Content-Type": "application/x-www-form-urlencoded",
            "Authorization": f"Basic {auth_b64}",
        }

        data = {
            "grant_type": "refresh_token",
            "refresh_token": refresh_token
        }

        response = requests.post(url, headers=headers, data=data)

        if response.status_code == 200:
            tokens = response.json()
            novo_access_token = tokens['access_token']
            novo_refresh_token = tokens.get('refresh_token', refresh_token)

            return novo_access_token, novo_refresh_token
        else:

            self.deleteRefreshToken()

            return None, None
        

    #####    ######   ####     ######   ####      ####     ####             ##  ##   ######   ######   ##
    ##  ##   ##       ## ##      ##     ## ##    ##  ##   ##                ##  ##     ##       ##     ##
    ##  ##   #####    ##  ##     ##     ##  ##   ##  ##    ####             ##  ##     ##       ##     ##
    #####    ##       ##  ##     ##     ##  ##   ##  ##       ##            ##  ##     ##       ##     ##
    ##       ##       ## ##      ##     ## ##    ##  ##       ##            ##  ##     ##       ##     ##
    ##       ######   ####     ######   ####      ####     ####             ######     ##     ######   ######

    def get_order_items(self, order) :
        return [ self.extractValues(item, "itenscarrinho_fields") for item in order['itens']]
        
    def get_order_data(self, order) :
        req = self.bling_request.doRequest(
            rote="pedido/vendas/{id}".format(id=order['id'])
        )

        if req.status_code == 200:

            self.current_order = self.tryJson(req)

            return [ self.extractValues(self.current_order, 'pedido_fields') ]

        return False
    
    def get_order_volumes(self, order) :
        return [ self.extractValues(item, "volumescarrinho_fields") for item in order['data']['transporte']['volumes']]
    
    def get_order_etiquetas(self, order) :
        import requests, os

        req = self.bling_request.doRequest(
            rote="logisticas/etiquetas?idsVendas[]={id}&formato=PDF".format(id=order['data']['id'])
        )

        if req.status_code == 200:

            etiquetas = req.json()['data']
            etiquetas_cobol = []

            for etiqueta in etiquetas:
                pdf_etiqueta = requests.get(etiqueta['link'])
                
                arquivo_saida = "/var/tmp/etiqueta_bling_{}.pdf".format(etiqueta['id'])

                with open(arquivo_saida, 'wb') as file:
                    file.write(pdf_etiqueta.content)
                    
                    # Salva o arquivo com permissões adequadas
                    os.chmod(arquivo_saida, 0o777)  # Define permissões de leitura e escrita para o proprietário, e leitura para outros usuários

                etiquetas_cobol.append({
                    "id"  : etiqueta['id'],
                    "caminho": "/var/tmp/",
                    "arquivo": "etiqueta_bling_{}.pdf".format(etiqueta['id']),
                })

            return [ self.extractValues(item, "etiquetascarrinho_fields") for item in etiquetas_cobol]
        
        else: 
            print("Erro ao obter etiquetas:", req.status_code, req.text)

        return False
    
    def afterGet_pedidos_vendas(self, data):
        return data['data']
    
    def rote_filter_pedidos_vendas(self):
        
        sql =  """
            SELECT 
                DTULTIMAALTERACAO 
            FROM 
                TBRELACIONAID
            WHERE 
                1=1 
            AND 
                NMTABELA = '{table}'
            AND 
                NMNOMEAPI = 'bling'
            ORDER BY 
                DTULTIMAALTERACAO DESC 
            
            LIMIT 1;
        """.format(table=self.atualWs['table'])
        
        fetch = self.getDb().select(sql)
        
        if len(fetch) :
            alterado_apos = fetch[0]['DTULTIMAALTERACAO']
        else :
            alterado_apos = "2025-07-01"
            
        
        self.createLog("000",alterado_apos)
        return  self.mountRouteParams(
            {
                    #"idsSituacoes[]" : '21' # Em digitacao

                #    "idsSituacoes[]" : '9' # Atendido
                    # "numero": "131453"
                #,   "dataEmissaoInicial" : f"{alterado_apos} 00:00:00"
            }
        )
    
      ##     ##       ######       ####    ######     ##     ######   ##  ##    ####             #####    ######   ####
     ####    ##         ##        ##         ##      ####      ##     ##  ##   ##                ##  ##   ##       ## ##
    ##  ##   ##         ##         ####      ##     ##  ##     ##     ##  ##    ####             ##  ##   #####    ##  ##
    ##  ##   ##         ##            ##     ##     ##  ##     ##     ##  ##       ##            #####    ##       ##  ##
    ######   ##         ##            ##     ##     ######     ##     ##  ##       ##            ##       ##       ## ##
    ##  ##   ######     ##         ####      ##     ##  ##     ##     ######    ####             ##       ######   ####
    
    def beforeSend_pedidos_vendas_pedido_id_situacoes_situacao_id(self, data): 
        self.current_method = "PATCH"

        return []
    
    def afterSend_pedidos_vendas_pedido_id_situacoes_situacao_id(self, data) :
        self.getDb().delete(
                    "TBPEDIDOSSTATUS"
            ,   ["IDTBPEDIDOSSTATUS={}".format(self.current_data['softdib_id'] )]
            )
        
        if self.current_data['situacao_id'] == "12": 
            self.getDb().delete(
                    "TBPEDIDOSRETORNO"
            ,   [
                        "CDPEDIDOEXTERNO={}".format(self.current_data['pedido_id'] )
                    ,   "NMAPI='{}'".format(self.apiName)
                ]
            )
    
    def handle_output_pedidos_vendas_pedido_id_situacoes_situacao_id(self, data):
        return
    
    ##  ##   #####             #####    #####     ####    ####     ##  ##   ######    ####     ####
    ##  ##   ##  ##            ##  ##   ##  ##   ##  ##   ## ##    ##  ##     ##     ##  ##   ##
    ##  ##   ##  ##            ##  ##   ##  ##   ##  ##   ##  ##   ##  ##     ##     ##  ##    ####
    ##  ##   #####             #####    #####    ##  ##   ##  ##   ##  ##     ##     ##  ##       ##
    ##  ##   ##                ##       ## ##    ##  ##   ## ##    ##  ##     ##     ##  ##       ##
    ######   ##                ##       ##  ##    ####    ####     ######     ##      ####     ####
    
    def beforeSend_put_produtos(self, data):

        blingId = self.select("""
            SELECT 
                IDAPI 
            FROM 
                TBRELACIONAID 
            WHERE 
                1 = 1
                AND IDSOFTDIB = '{codigo}'
                AND NMNOMEAPI = 'bling'
                AND NMTABELA = 'TBPRODUTO'
        """.format(
            codigo=self.current_data['codigo']
        ))

        self.current_method = "PATCH"
        self.current_rote = f"produtos/{blingId[0]['IDAPI']}" if blingId else "produtos"
        return data
    
     ####    ######   ##  ##    ####             #####    #####     ####    ####     ##  ##   ######    ####     ####
    ##         ##     ### ##   ##  ##            ##  ##   ##  ##   ##  ##   ## ##    ##  ##     ##     ##  ##   ##
     ####      ##     ######   ##                ##  ##   ##  ##   ##  ##   ##  ##   ##  ##     ##     ##  ##    ####
        ##     ##     ######   ##                #####    #####    ##  ##   ##  ##   ##  ##     ##     ##  ##       ##
        ##     ##     ## ###   ##  ##            ##       ## ##    ##  ##   ## ##    ##  ##     ##     ##  ##       ##
     ####    ######   ##  ##    ####             ##       ##  ##    ####    ####     ######     ##      ####     ####

    
    def format_rote_sincronizar_produtos(self, rote):
        return "produtos"
    
    def afterGet_sincronizar_produtos(self, data):
        return data['data']

    def sincronizar_produtos_handler(self, produtos):

        for produto in produtos:

            id_produto = produto.get('id')
            codigo     = produto.get('codigo', '')

            

            if codigo == '':
                self.createLog("435", "Produto: {} - {}: Sem código para fazer relaciona ID".format(id_produto, codigo)) 
                continue

            produto_db = self.select(f""" 
                SELECT 
                    CDPRODUTO 
                FROM 
                    TBPRODUTO 
                WHERE 
                    1 = 1
                    AND CDEMPRESA = {int(self.cdempresa)}
                    AND CDFILIAL  =  {int(self.cdfilial)}
                    AND CDPRODUTO = '{codigo}'
            """)

            if not produto_db:
                self.createLog("436", "Produto: {} - {}: Produto não encontrado na base de dados".format(id_produto, codigo)) 
                continue

            exist_relacionaId = self.select(f"""
                SELECT
                    COUNT(*) AS count
                FROM
                    TBRELACIONAID
                WHERE
                    NMNOMEAPI = 'bling'
                    AND NMTABELA = 'TBPRODUTO'
                    AND IDAPI = '{id_produto}'
            """)

            if produto_db and exist_relacionaId[0]['count'] == 0:
                self.relatement({
                    "NMNOMEAPI"         : "bling",
                    "NMTABELA"          : "TBPRODUTO",
                    "IDAPI"             : id_produto,
                    "IDSOFTDIB"         : produto_db[0]['CDPRODUTO'],
                    "DTULTIMAALTERACAO" : datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                    "HASHMD5"           : "INTEGRADO"
                })

                self.createLog("200", "Produto: {} - {}: Relacionamento criado com sucesso".format(id_produto, codigo))

            # Buscar o nosso ID baseado no SKU



        # print(produtos)

    
    #--------------------------------------------------------#
    #--------------- TRATAMENTO CUSTOM SQL  -----------------#
    #--------------------------------------------------------#
    def quantidade_estoque(self, data):
        return "( SELECT IF(TBPRODUTO.NMUNIDADE IN ('MT','M2'), TBPRODUTO.VLQTDEATUAL * 100, TBPRODUTO.VLQTDEATUAL ) )"
        
    def quantidade_em_estoque(self, data):
        return "( SELECT 2 )"
        
    def quantidade_sem_estoque(self, data):
        return "( SELECT IF(TBPRODUTO.VLTEMPOREPOSICAO > 0, TBPRODUTO.VLTEMPOREPOSICAO, -1) )"
        
    def produto_id(self, data):
        return "( SELECT IF(TBPRODUTO.CDIDAPI > 0, TBPRODUTO.CDIDAPI, 'None') )"
    
    def valor_produto(self, data):
        field = data.get('dbfield')
        return f"(SELECT IF(TBPRODUTO.NMUNIDADE IN ('MT','M2'), {field} / 100, {field} ) )"
        #if 'VLPROMOCIONAL' in field:
        #    sql = f" (SELECT IF({sql} > 0, {sql}, 'null' ))"
        #    
        #return sql

    def forma_envio_frete_papapa(self, value, props):
        fretes = {
            "0": "1",
            "1": "2",
            "2": "4",
            "3": "5",
            "4": "6"
        }
        
        return fretes.get(str(value), "9")
        
    def order_id(self, field):
        return self.current_order['data']['id'] 
    
    def cliente_tipo(self, field) :
        return """(SELECT IF({}='J', 'PJ', 'PF') )""".format(field['dbfield'])
        
    
    def imagens_produto_principal(self, field) :
        return """(SELECT IF({}='S', 'true', 'false') )""".format(field['dbfield'])
        
    
    def situacao_pedido_pedido_id(self, field) :
        
        pass
    
    def beforeSend_contatos(self, data): 
        if data['tipo'] == "J": 
            del data['rg']

        if data['inscricaoMunicipal'] == "": 
            del data['inscricaoMunicipal']

        if data['ie'] == "":
            del data['ie']

        return data

    def vendedores_handler(self, vendedores):

        self.getDb().delete( "TBRELACIONAID",["NMTABELA='TBVENDEDOR' AND NMNOMEAPI ='bling'"])
                
        for vendedor in vendedores['data'] :
            
            vendedor_name = vendedor.get('contato').get('nome','').split(' | ')[0]
            vendedor_id   = vendedor.get('contato').get('id')
            table_name    = self.atualWs.get('table')#['table']
            
            
            if vendedor.get('excluido') :
                self.getDb().delete( "TBRELACIONAID",[f"IDAPI='{vendedor_id}'","NMNOMEAPI='bling'"]) #.format( vendedor_id=vendedor_id ) ] )
                continue
            
            sql = """ 
                    SELECT
                        CDVENDEDOR
                    FROM
                        TBVENDEDOR
                    WHERE
                        1 = 1
                        AND CDVENDEDOR = '{vendedor}'
                        AND CDEMPRESA  = '{empresa}'
                        AND CDFILIAL   = '{filial}'
                    ;
                    
            """.format( vendedor = vendedor_name.strip()
                       ,empresa  =  self.cdempresa
                       ,filial   =  self.cdfilial  )

            
            
            idsoftdib   = self.getDb().select(sql)
            
            if len(idsoftdib) > 0:
                vendedor = {
                        "NMNOMEAPI"         : "bling" ,
                        "NMTABELA"          : table_name , 
                        "IDAPI"             : vendedor_id , 
                        "IDSOFTDIB"         : idsoftdib[0]['CDVENDEDOR'] , 
                        "DTULTIMAALTERACAO" : "now()"                            
                }
                
                self.relatement(vendedor)
                
                """ exists = self.exists(
                        "IDAPI"
                    ,   "TBRELACIONAID"
                    ,   [
                            f"IDAPI='{vendedor_id}'"#.format(vendedor_id=vendedor_id)
                        ,   f" NMTABELA='{table_name}'"#.format(table_name=table_name)
                    ])
                    
                if not exists :
                    
                    
                    self.getDb().insert( "TBRELACIONAID", dataFields) """
                    
                
        
    
    #--------------------------------------------------------#
    #--------------- TRATAMENTO BEFORESEND  -----------------#
    #--------------------------------------------------------#
    
    def beforeSend_categorias_produtos(self, data) :
        
        if self.current_data['subgrupo'] == 0 :
            if 'categoriaPai' in data :
                del data['categoriaPai']
            
        else :
            data['categoriaPai']['id'] = self.translate(id = "{}.{}".format(self.current_data['grupo'], '0'), table = "TBGRUPO")
        
        return data
        
    def beforeSend_produtos(self, data) :
        return data
    
    def beforeSend_estoques(self, data) :
        if self.current_method == "POST":
            data['deposito']['id'] = int(self.get_token_data("Deposito"))

        if self.current_method == "PUT":
            self.current_method = "POST" # Bling mudou o verbo para POST

            data['deposito']['id'] = int(self.get_token_data("Deposito"))
            data['observacoes'] = "Atualização de estoque via API"
            data['data'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") 

            # T143233
            self.current_rote = "estoques"

        return data
    
    def produtos_lojas_handler(self, lojas):

        if self.api_request.req.ok:

            print(self.api_request.req.text)

    # T141097
    def reprocessamento_nfe(self):
        import requests, os

        notas_fiscais_com_erro = self.select("""
            SELECT
                IDAPI
            FROM
                TBRELACIONAID
            WHERE
                NMNOMEAPI = 'bling'
                AND NMTABELA = '{table}'
                AND HASHMD5 = 'REPROCESSAR'
        """.format(table=self.atualWs['table']))

        for nota in notas_fiscais_com_erro:

            detalhes_nota = self.bling_request.doRequest(
                rote="{}/{}".format(self.atualWs['rote'], nota['IDAPI']),
                method="GET"
            )

            if detalhes_nota.status_code == 200:
                detalhes = detalhes_nota.json()['data']

                # é um URL
                xml_nota = detalhes['xml']

                # Notas sem valor fiscal fica com o xml zerado.
                if xml_nota == "":
                    self.createLog(404, "{} - Nota sem XML, pulando...".format(nota['IDAPI']))
                    continue

                # Faz a consulta do XML
                xml_nota = requests.get(xml_nota)
                
                # T141099
                if not xml_nota.ok:
                    self.createLog(404, "{} - Erro ao baixar o XML: {}".format(nota['IDAPI'], xml_nota.status_code))
                    continue
                
                arquivo_saida = "{}/{}_{}-nfe.xml".format(self.rote_data['params']['upload'], detalhes['loja']['id'], detalhes['chaveAcesso'])

                with open(arquivo_saida, 'wb') as file:
                    file.write(xml_nota.content)

                os.chmod(arquivo_saida, 0o777)  # Define permissões de leitura e escrita para o proprietário, e leitura para outros usuários
                
                relatement = {
                    "NMNOMEAPI"         : "bling",
                    "NMTABELA"          : self.atualWs['table'], 
                    "IDAPI"             : detalhes['id'], 
                    "IDSOFTDIB"         : detalhes['id'], 
                    "DTULTIMAALTERACAO" : detalhes['dataEmissao'],
                    "HASHMD5"           : "INTEGRADO"                
                }
                
                self.relatement(relatement)
        

    def nfe_handler(self, notas):
        import requests, os

        # Reprocessa notas com erro de download de XML
        self.reprocessamento_nfe()
        
        pasta_salvar_nfe = self.rote_data['params']['upload']
        saida_cobol = []

        # for nota in filter(lambda nota: nota['id'] in [23109715668], notas): # - GreatPets
        for nota in notas:

            check_exist_relacionaId = self.select("SELECT COUNT(*) AS count FROM TBRELACIONAID WHERE NMNOMEAPI = 'bling' AND NMTABELA = '{}' AND IDAPI = {}".format(self.atualWs['table'], nota['id']))

            if check_exist_relacionaId[0]['count'] > 0:
                self.createLog("777", "{} - Nota já existe no relacionamento, pulando...".format(nota['id']))
                continue

            detalhes_nota = self.bling_request.doRequest(
                rote="{}/{}".format(self.atualWs['rote'], nota['id']),
                method="GET",
            )

            if detalhes_nota.status_code == 200:
                detalhes = detalhes_nota.json()['data']

                # é um URL
                xml_nota = detalhes['xml']

                # Notas sem valor fiscal fica com o xml zerado.
                if xml_nota == "":
                    self.createLog(404, "{} - Nota sem XML, pulando...".format(nota['id']))

                    # T141097
                    self.relatement({
                        "NMNOMEAPI"         : "bling",
                        "NMTABELA"          : self.atualWs['table'], 
                        "IDAPI"             : nota['id'], 
                        "IDSOFTDIB"         : "NAOTEMXML", 
                        "DTULTIMAALTERACAO" : datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                        "HASHMD5"           : "REPROCESSAR"
                    })

                    continue

                # Faz a consulta do XML
                xml_nota = requests.get(xml_nota)

                # T141099
                if not xml_nota.ok:
                    self.createLog(404, "{} - Erro ao baixar o XML: {}".format(nota['id'], xml_nota.status_code))

                    # T141097
                    self.relatement({
                        "NMNOMEAPI"         : "bling",
                        "NMTABELA"          : self.atualWs['table'], 
                        "IDAPI"             : nota['id'], 
                        "IDSOFTDIB"         : "ERRODOWNLOADXML", 
                        "DTULTIMAALTERACAO" : datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                        "HASHMD5"           : "REPROCESSAR"
                    })

                    continue
                
                arquivo_saida = "{}/{}_{}-nfe.xml".format(pasta_salvar_nfe, detalhes['loja']['id'], detalhes['chaveAcesso'])

                with open(arquivo_saida, 'wb') as file:
                    file.write(xml_nota.content)
                    saida_cobol.append("{id}|{numero}|{dataEmissao}|{chaveAcesso}|{serie}|{valorNota}|{valorFrete}|{numeroPedidoLoja}|{chaveAcesso}-nfe.xml".format(**detalhes))

                os.chmod(arquivo_saida, 0o777)  # Define permissões de leitura e escrita para o proprietário, e leitura para outros usuários
                
                self.relatement({
                    "NMNOMEAPI"         : "bling",
                    "NMTABELA"          : self.atualWs['table'], 
                    "IDAPI"             : detalhes['id'], 
                    "IDSOFTDIB"         : detalhes['id'], 
                    "DTULTIMAALTERACAO" : detalhes['dataEmissao'],
                    "HASHMD5"           : "INTEGRADO"                
                })

        if len(saida_cobol) > 0:
            with open(self.saida_cobol, 'w') as file:
                file.write("\n".join(saida_cobol))

    def depositos_handler(self, depositos):
        saida_cobol = ""

        if len(depositos) > 0:
            id_geral = next((deposito["id"] for deposito in depositos if deposito["descricao"] == "Geral"), None)

            saida_cobol = "OK||{}".format(id_geral)
        else:
            saida_cobol = "ER|Nenhum depósito encontrado"

        with open(self.saida_cobol, 'w') as file:
            
            file.write(saida_cobol)

    def format_rote_logisticas_etiquetas(self, rote):
        numero_pedido = self.rote_data['path']['pedido']

        pedido = self.select(f"""
            SELECT
                IDAPI, IDSOFTDIB
            FROM
                TBRELACIONAID
            WHERE
                NMNOMEAPI = 'bling'
                AND NMTABELA = 'TBPEDIDOSRETORNO'
                AND IDSOFTDIB = '{int(numero_pedido)}'
        """)

        if len(pedido) == 0:
            self.createLog("999", "Pedido {} não encontrado no relacionamento de IDs.".format(self.rote_data['path']['pedido']))
            return None
        
        pedido_id = pedido[0]['IDAPI']

        self.pedido_bling_id = pedido_id

        rote = f"{rote}?idsVendas[]={pedido_id}&formato=PDF"

        return rote

    def beforeSend_logisticas_etiquetas(self, data): 
        return data
    
    def afterGet_logisticas_etiquetas(self, etiquetas): 
        return etiquetas['data']
    
    def logisticas_etiquetas_handler(self, etiquetas):
        import os, requests

        if self.response.status_code != 200:
            try:
                error_json = json.loads(self.response.text)
                if "error" in error_json:
                    error = error_json["error"]
                    mensagem = error.get("message", "")
                    descricao = error.get("description", "")
                    conteudo_saida = f"ER|{mensagem} - {descricao}"
                    self.write_file(conteudo_saida, self.saida_cobol, 'w+')
            except Exception:
                self.write_file(f"ER|{self.response.text}", self.saida_cobol, 'w+')

            return

        for etiqueta in etiquetas:
            arquivo_etiqueta = "/var/tmp/etiqueta_bling_{}.pdf".format(etiqueta['id'])
            pdf_etiqueta = requests.get(etiqueta['link'])
            output = 1

            arquivo_saida = self.rote_data['path']['arquivo_saida']

            with open(arquivo_etiqueta, 'wb') as file:
                file.write(pdf_etiqueta.content)
                
                # Salva o arquivo com permissões adequadas
                os.chmod(arquivo_etiqueta, 0o777)  # Define permissões de leitura e escrita para o proprietário, e leitura para outros usuários

            empresa = self.cdempresa
            filial  = self.cdfilial
            pedido_id = self.pedido_bling_id
            id_etiqueta = etiqueta['id']
            caminho = "/var/tmp/"
            arquivo = "etiqueta_bling_{}.pdf".format(etiqueta['id'])

            conteudo_saida = "{};{};{};{};{};{};\n".format(
                empresa, 
                filial, 
                pedido_id, 
                id_etiqueta, 
                caminho, 
                arquivo
            )

            flag = output == 1 and 'w+' or 'a+'
            self.write_file(conteudo_saida, arquivo_saida, flag)

            output += 1

        self.write_file("OK|Etiquetas salvas", self.saida_cobol, 'w+')

        return 
    
    # def atualizar_estoque(self, data):
    def before_handle_db_data_(self, data):
        data = []
        print(data)
        return ""
        
    def put_route_pedidos_vendas_pedido_situacoes_situacao(self, data):

        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 format_rote_shipment_order_volume_pedido_volume(self, rote):
        return rote.format(
            pedido=self.current_data['pedido_id'],
            situacao=self.current_data['situacao']
        )
    
    #--------------------------------------------------------#
    #------------------ TRATAMENTO RESPONSE  ----------------#
    #--------------------------------------------------------#
    
    def response_code_200(self, req) :
        return super().response_code_201(req)
    
    def response_code_204(self, req) :
        return super().response_code_201(req)
        
    def response_code_400(self, req) :
        return super().response_code_400(req)
        
        
    #--------------------------------------------------------#
    #--------------- TRATAMENTO FILTROS DE ROTA -------------#
    #--------------------------------------------------------#
    
    def rote_filter_nfe(self):
        
        sql =  """
            SELECT 
                DTULTIMAALTERACAO 
            FROM 
                TBRELACIONAID
            WHERE 
                1=1 
            AND 
                NMTABELA = '{table}'
            ORDER BY 
                DTULTIMAALTERACAO DESC 
            LIMIT 1;
        """.format(table=self.atualWs['table'])
        
        fetch = self.getDb().select(sql)
        
        if len(fetch) :
            alterado_apos = fetch[0]['DTULTIMAALTERACAO']
        else:
            alterado_apos = "2025-08-01 00:00:00" # definir uma data junto ao cliente

        alterado_apos = "2025-12-01 00:00:00" # definir uma data junto ao cliente
            
        return  self.mountRouteParams(
            {
                    #"idsSituacoes[]" : '6' 
                    # "numero": '000629'
                    "dataEmissaoInicial" : alterado_apos
                ,   "dataEmissaoFinal" :  datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            }
        )
    
    def pagination_rules(self, response, data):
        from time import sleep

        sleep(1)  # Evita problemas de limite de requisições
        
        url = response.url.replace("https://api.bling.com.br/Api/v3/", "")

        if response.ok:
            total_page = len(data)

            if len(data) == 0:
                return None

            if total_page == 100:
                self.pagina_atual += 1

                if self.pagina_atual > 1:
                    if "?" in url:
                        url = url.split("&pagina")[0]
                    else:
                        url = url.split("?pagina")[0]
                
                if "?" in url:
                    return f"{url}&pagina={self.pagina_atual}"
                else:
                    return f"{url}?pagina={self.pagina_atual}"
            
            return None
        
        return None

    def afterGet_nfe(self, data):
        return data['data']

    def afterGet_depositos(self, data):
        return data['data']
    
    
    def beforeSend_nfe_enviar(self, data): 
        """
            Metodo para tratar o envio de NFE
        """
        self.current_rote = "nfe"


        req = self.api_request.doRequest( 
            rote=f"pedidos/vendas/{self.current_data['pedido_id']}",
            method="GET",
        )

        if req.ok:
            dados_pedido = req.json()['data']

            # data['contato'] = dados_pedido['contato']
            data['itens'] = dados_pedido['itens']

            return data
        else: 
            self.createLog("999", "Erro ao obter os dados do pedido {}: {}".format(self.current_data['pedido_id'], req.text))
            return data

    #--------------------------------------------------------#
    #---------- TRATAMENTO AFTERSEND  & BEFORESEND ----------#
    #--------------------------------------------------------#
    
    def beforeSend_situacao_pedido_pedido_id(self, data) :
        codigo = data.get('codigo')
        if (codigo and codigo == 'pedido_cancelado') :
            self.atualWs['send'] = False
            
            
        return data
        
    
    def afterSend_situacao_pedido_pedido_id(self, data) :
        
        self.getDb().delete(
                    "TBPEDIDOSSTATUS"
            ,   ["IDTBPEDIDOSSTATUS={}".format(self.current_data['softdib_id'] )]
            )
        codigo = data.get('codigo')
        if (codigo and codigo == 'pedido_cancelado') :
            self.getDb().delete(
                    "TBPEDIDOSRETORNO"
            ,   [
                        "CDPEDIDOEXTERNO={}".format(self.current_data['pedido_id'] )
                    ,   "NMAPI='{}'".format(self.apiName)
                ]
            )
        
    
    #--- ----------------------------------------------------#
    #--------------- TRATAMENTO ITEMS/PEDIDO ----------------#
    #--------------------------------------------------------#
    
    def get_order_items(self, order):
        return [ self.extractValues(item, "itenscarrinho_fields") for item in order['data'].get('itens',[])]
        
    
    def get_order_data(self, order) :
        
        req = self.bling_request.doRequest(
            rote="pedidos/vendas/{id}".format(id=order['id'])
        )
        
        if req.status_code == 200 :
            self.current_order =  self.tryJson(req)
            data = [ self.extractValues(self.current_order['data'], 'pedido_fields') ]

            if not data[0]['ENDERECO_NRO_ENT'].isdigit():

                endereco = data[0]['ENDERECO_NRO_ENT'] 
                complemento = data[0]['COMPLEMENTO_ENT']

                numb = ''.join(re.findall(r'\d+', endereco))  # Captura apenas os números
                # comp = ''.join(re.findall(r'[^\d]+', endereco)).strip()  # Captura os caracteres não numéricos e remove espaços extras

                data[0]['ENDERECO_NRO_ENT'] = numb
                data[0]['COMPLEMENTO_ENT']  = "{} - {}".format(endereco, complemento)

            return data
        
        if req.status_code != 200:
            print(req.text)            
            
        return False
        
    # returne false caso queira sair
    def afterGet_pedido_search(self, data) :
        data = data.get('objects')
        return data
        
    
    def sku_to_codigo_produto(self, value) :
        # salvo codigo para usar em outras fucoes
        self.codigo_produto = value
        
        #code_pieces = self.codigo_produto.split('-')
        # Segundo o suporte sempre que existir um P tera um C no SKU
        # Por isso sempre testamos apenas o c
        
        
        #if (len(code_pieces) > 1 and re.match(r'^([cp]\d{1,3}){1,2}$',code_pieces[-1])):
        #    value = "-".join(code_pieces[:-1])
        value = value.upper()
        return re.sub(r'(-C([2-9]|1[0-2]?)(P([2-9]|1[0-2]?))?)$','',value)
        
    
    def carrinho_quantidade(self, value) :
        value = 0
        if re.match(r'.*-(C([2-9]|1[0]?)(P([2-9]|1[0]?))?)$', self.codigo_produto.upper()):
            value = 100
            
        return str(value)
    
    def send_handler(self, data) :
        """
            Metodo para manipular o envio
        """
        
        # o data pode ser alterado aqui dentro
        self.prepare_send(data)
        
        # Faz um tratamento no dados antes de enviar
        func_rote = self.fixed_rote()
        func = f"beforeSend_{func_rote}"#.format(func_rote)
        # Tratamento de dados antes de enviar
        beforeSend = self.call(func, data, is_required= self.atualWs.get('need_beforeSend',False))

        if beforeSend is not None:
            data = beforeSend

        #if self.hasAttr(func, self) :
        #    data = getattr(self, func)( data )
        #print("stop")
        if hasattr(self, 'ignore_request') and self.ignore_request == True:
            self.ignore_request = False
            return
        
        self.send( data )
        
        # Executa apoas enviar - cada rota tem a sua especifica - deve-se criar dentro da arquivo da api
        
        if not self.atualWs['send'] or self.send_response.status_code in range(200,300) :
            func = f"afterSend_{func_rote}"#format(func_rote)
            # Tratamento de dados depois de enviar
            #if self.hasAttr(func, self) :
            #    getattr(self, func)( data )
            self.call(func, data )
            
        self.current_data = {}
        
    def only_numbers(self, data):
        return  re.sub(r'[^\d]','',data)
    
    
    
    def consultar_condpag(self, condpag_id):
        from unidecode import unidecode

        if not condpag_id:
            return '001'
        
        forma_pagamento = None

        for forma in self.formas_pagamentos:
            if forma['id'] == condpag_id:
                forma_pagamento = forma
                break

        if not forma_pagamento:
            return '001'
        
            
        nome_cond = unidecode(forma_pagamento['descricao']).upper()

        consultar_condpag_query = """
            SELECT
                CDCONDPGTO
            FROM
                TBCONDPGTO
            WHERE
                1 = 1
                AND CDEMPRESA = '{empresa}'
                AND CDFILIAL = '{filial}'
                AND UPPER(DSCONDPGTO) = '{nome}'
        """.format(
            empresa=int(self.cdempresa),
            filial=int(self.cdfilial),
            nome=nome_cond.strip()
        )

        consultar_db = self.select(consultar_condpag_query)

        if consultar_db:
            return consultar_db[0]['CDCONDPGTO']
        
        return '001'
    
    def consultar_transportadora(self, transportadora_id):
        time.sleep(.5)  # Evita problemas de limite de requisições

        tranportadora = self.bling_request.doRequest(rote=f"contatos/{transportadora_id}")

        if tranportadora.status_code == 200:

            dados_transportadora = tranportadora.json().get('data', {})
            numeroDocumento = dados_transportadora.get('cnpj', dados_transportadora.get('numeroDocumento', ''))

            if not numeroDocumento:
                self.createLog("999", "Transportadora {} não possui CNPJ ou CPF".format(transportadora_id))
                return 'T99999'
            
            consulta_transportadora_query = """
                SELECT
                    CDTRANSPORTADOR
                FROM
                    TBTRANSPORTADOR
                WHERE
                    1 = 1
                    AND CDEMPRESA = '{empresa}'
                    AND CDFILIAL = '{filial}'
                    AND VLCPFCNPJ = '{cpfcnpj}'   
            """.format(
                empresa=self.cdempresa,
                filial=self.cdfilial,
                cpfcnpj=numeroDocumento
            )

            transportadora_db = self.select(consulta_transportadora_query)

            if transportadora_db:
                return transportadora_db[0]['CDTRANSPORTADOR']
            else: 

                dados_transportadora = tranportadora.json().get('data', {})
                nome = dados_transportadora.get('nome', '')
                numeroDocumento = dados_transportadora.get('numeroDocumento', '')
                tipo = dados_transportadora.get('tipo', '')
                ie = dados_transportadora.get('ie', '')
                endereco = dados_transportadora.get('endereco', {})
                email = dados_transportadora.get('email', '')

                endereco_geral = endereco.get('geral', {})
                endereco = endereco_geral.get('endereco', '')
                cep = endereco_geral.get('cep', '')
                bairro = endereco_geral.get('bairro', '')
                municipio = endereco_geral.get('municipio', '')
                uf = endereco_geral.get('uf', '')
                numero = endereco_geral.get('numero', '')
                complemento = endereco_geral.get('complemento', '')

                return self.salvar_transportadora_softdib({
                    'RAZAO_SOCIAL': nome,
                    'NOME_FANTASIA': nome,
                    'TIPO_PESSOA': tipo,
                    'CNPJ_CPF': numeroDocumento,
                    'RG_IE': ie,
                    'ENDERECO': endereco,
                    'ENDERECO_NRO': numero,
                    'COMPLEMENTO': complemento,
                    'CEP': cep,
                    'BAIRRO':  bairro,
                    'CIDADE': municipio,
                    'ESTADO': uf,
                    'EMAIL': email
                })
            
        else: 
            self.createLog("999", "Erro ao consultar transportadora {}: {}".format(transportadora_id, tranportadora.text))
            return 'T99999'

    def throttling_rules(self, req) : 
        if int(req.status_code) == 429:
            return 5

        return 0

    # Apenas para recuperar o id externo do cliente
    def customer_data(self, data) :
        
        params = {
            "method" : "GET",
            "rote": f"contatos/{self.current_order['data']['contato']['id']}"
        }
        
        response = self.throttling(**params) 
        if response.ok :
            dados_cliente = response.json()['data']

            telefone = dados_cliente.get('telefone', '')
            if telefone:
                telefone = self.only_numbers(telefone)

            # data["RAZAO_SOCIAL"    ] = dados_cliente['']
            # data["RG_IE"           ] = dados_cliente['']
            # data["NOME_FANTASIA"   ] = dados_cliente['']
            # data["SEXO"            ] = dados_cliente['']
            # data["CNPJ_CPF"        ] = dados_cliente['']
            # data["TIPO_PESSOA"     ] = dados_cliente['']
            data["EMAIL"           ] = dados_cliente['email']
            data["DDD"             ] = telefone[:2]
            data["TELEFONE"        ] = telefone[2:]

            endereco_principal = dados_cliente['endereco']['geral']

            if(endereco_principal):
                data["ENDERECO"      ] = endereco_principal.get("endereco",'')
                data["ENDERECO_NRO"  ] = endereco_principal.get("numero",'')
                data["COMPLEMENTO"   ] = endereco_principal.get("complemento",'')
                data["CEP"           ] = endereco_principal.get("cep",'')
                data["BAIRRO"        ] = endereco_principal.get("bairro",'')
                data["CIDADE"        ] = endereco_principal.get("municipio",'')
                data["ESTADO"        ] = endereco_principal.get("uf",'')
                
            
            rg_ie = dados_cliente['ie'] or dados_cliente['rg'] or ''
            data['RG_IE'] = self.only_numbers(rg_ie)

            #    Tratamento para endereços    #
            # ----- Endereço do cliente ----- #

            if not data['ENDERECO_NRO'].isdigit():
                endereco = data['ENDERECO_NRO'] 
                complemento = data['COMPLEMENTO']

                numb = ''.join(re.findall(r'\d+', endereco))  # Captura apenas os números
                # comp = ''.join(re.findall(r'[^\d]+', endereco)).strip()  # Captura os caracteres não numéricos e remove espaços extras

                data['ENDERECO_NRO'] = numb
                data['COMPLEMENTO']  = "{} - {}".format(endereco, complemento)
            
        #data['IDCLIENTEEXTERNO'] = self.current_order['cliente']['id']
        return data
        
        
    
    def tipo_pessoa(self, value) :
        return "J" if self.current_order['cliente'].get('cnpj') else "F"
    
    def only_cnpj_cpf(self, value):
        """
        Remove caracteres não numéricos de CNPJ ou CPF
        """
        return re.sub(r'\D', '', value) if value else ''
        
    
    def previsao_entrega_pedido(self, value):
        if value:
            return max(map(lambda v: v.get('disponibilidade', 2), value))
            
        return 2
    
    def delete_produtos(self, rote):
        rote_alterar_situacao = "{}/situacoes".format(rote)
        
        data = {
            "situacao" : "E"
        }
        
        put_situacao = self.throttling(**{
            "rote":rote_alterar_situacao, 
            "method": "PATCH", 
            "data": json.dumps(data)
        }) 
        
        if put_situacao.status_code != 204 :
            self.createLog("677", "Erro ao alterar a situacao do registro para E (deletado) na API do Bling. Rote: {}, Status Code: {}".format(rote, put_situacao.status_code))
            return False
                
        # Forca excluido, ja que o proposito eh excluir
        params = {
                "rote"   :   rote
            ,   "method" :   "DELETE"
        }
        
        delete = self.throttling(**params)
        
        if delete.status_code > 299 :
            self.response_log(delete)
        
        return delete.status_code == 204
    
    def delete_categorias_produtos(self, rote):
        # data = {
        #     "situacao" : "E"
        # }
        
        # put_situacao = self.throttling(**{
        #     "rote":rote, 
        #     "method": "PUT", 
        #     "data": json.dumps(data)
        # }) 
        
        # if put_situacao.status_code != 204 :
        #     self.createLog("677", "Erro ao alterar a situacao do registro para E (deletado) na API do Bling. Rote: {}, Status Code: {}".format(rote, put_situacao.status_code))
        #     return False
                
        # Forca excluido, ja que o proposito eh excluir
        params = {
                "rote"   :   rote
            ,   "method" :   "DELETE"
        }
        
        delete = self.throttling(**params)
        
        if delete.status_code > 299 :
            self.response_log(delete)
        
        return delete.status_code == 204


    def deleteFromApi(self, **kargs) :
        
        rote = "{rote}/{id}".format(
                rote = self.atualWs['rote']
            ,   id   = kargs['bling_id']
        )

        delete_from_rote = self.call("delete_" + self.fixed_rote(), rote)
        
        if delete_from_rote:
            return delete_from_rote
        else:
            self.createLog("999", "A função delete da rota: {} não foi implementada.".format(self.fixed_rote()))
            return False
    
    """ def custom_control_id_produto_estoque_produto_id(self, response):
        response        = self.tryJson(response)
        return response.get('produto', '').split('/')[-1] """
        
        
    def status_paid(self, order):
        import base64
        
        pagarme = None
        auth = None
        token = self.get_token_data('KEY_PAG')

        secretKey = f"{token}:"
        
        auth = base64.b64encode(secretKey.encode()).decode()

        pagarme = Request(
            base_url="https://api.pagar.me/core/v5/charges",
            headers={
                'Authorization': f'Basic {auth}'
            }
        )

        req = pagarme.doRequest(
            rote="/?code={}".format(order['data']['numero'])
        )

        if req.status_code == 200:
            response = json.loads(req.text)

            self.pagarme_current_order = response

            pagos = [item for item in response['data'] if item.get('status') == "paid"]

            if len(pagos) == 0:
                return 'not_paid', '', ''
            
            ultimo_pago = pagos[-1]

            if ultimo_pago.get('payment_method') == "pix":
                try:
                    return ultimo_pago['status'], ultimo_pago['last_transaction']['pix_provider_tid'], ""
                except:
                    return ultimo_pago['status'], "", ""

            try:
                acquirer_tid = ultimo_pago['last_transaction']['acquirer_tid']
            except:
                acquirer_tid = ''

            try:
                acquirer_nsu = ultimo_pago['last_transaction']['acquirer_nsu']
            except:
                acquirer_nsu = ''

            return ultimo_pago['status'], acquirer_tid, acquirer_nsu
        else:
            return 'not_paid', '', ''
    
    
    def __del__(self):
        self.createLog("999","Final da exportacao")
    
class GeradorCode:
    def __init__(self, client_id, redirect_uri, usuario, senha):
        self.client_id = client_id
        self.redirect_uri = redirect_uri
        self.usuario = usuario
        self.senha = senha
        self.driver = None
        
    def iniciar_navegador(self, headless=False):
        """Inicializa o navegador Chrome"""
        # Setup retry parameters
        max_retries = 10
        retry_count = 0
        wait_time = 10  # seconds between retries

        while retry_count < max_retries:
            try:
                chrome_options = uc.ChromeOptions()
                
                chrome_options.add_argument("--no-sandbox")
                chrome_options.add_argument("--disable-dev-shm-usage")

                display = Display(visible=0, size=(1920, 1080))
                display.start()
                
                self.driver = uc.Chrome(version_main=130, options=chrome_options)
                # If we reach here without exception, break the loop
                break
            
            except Exception as e:
                retry_count += 1
                print(f"Attempt {retry_count}/{max_retries} failed: {str(e)}")
                
                # Close display if it was created
                try:
                    if 'display' in locals():
                        display.stop()
                except:
                    pass
                    
                if retry_count >= max_retries:
                    print("Maximum retry attempts reached. Could not initialize Chrome.")
                    raise  # Re-raise the last exception
                
                print(f"Retrying in {wait_time} seconds...")
                time.sleep(wait_time)

        return self.driver
        
    def gerar_code(self, timeout=60):
        """
        Acessa a URL de autorização, aguarda redirecionamento e obtém o código
        
        Args:
            url_autorizacao: URL inicial do fluxo de autorização
            credenciais: Dicionário com credenciais (ex: {'usuario': 'email@exemplo.com', 'senha': '123456'})
            timeout: Tempo máximo de espera pelo redirecionamento em segundos
        
        Returns:
            O código de autorização ou None se falhar
        """
        if not self.driver:
            self.iniciar_navegador()
            
        try:
            url_autorizacao = f"https://www.bling.com.br/Api/v3/oauth/authorize?response_type=code&client_id={self.client_id}&state=123456"
            
            # Acessar a URL de autorização
            print(f"Acessando URL de autorização: {url_autorizacao}")
            self.driver.get(url_autorizacao)

            print("Preenchendo senha")
            self._realizar_login()
            
            # Esperar pelo redirecionamento (aguarda até a URL mudar para o redirect_uri)
            print(f"Aguardando redirecionamento para: {self.redirect_uri}")
            inicio = time.time()

            while not self.driver.current_url.startswith(self.redirect_uri):
                if time.time() - inicio > timeout:
                    print("Tempo esgotado aguardando o redirecionamento!")
                    return None
                time.sleep(0.5)
            
            # Extrair o código da URL de redirecionamento
            url_redirecionada = self.driver.current_url
            print(f"Redirecionado para: {url_redirecionada}")
            query_params = parse_qs(urlparse(url_redirecionada).query)
            
            if 'code' in query_params:
                code = query_params['code'][0]
                print(f"Código obtido: {code}")
                return code
            else:
                print(f"Código não encontrado na URL: {url_redirecionada}")
                print(f"Parâmetros encontrados: {query_params}")
                return None
                
        except Exception as e:
            print(f"Erro ao tentar obter o código: {e}")
            return None
        finally:
            self._fechar_navegador()

    def _realizar_login(self):
        """
        Realiza o login no formulário da página de autorização do Bling
        """
        try:
            print("Preenchendo formulário de login...")
            
            # Encontrar os campos de login e senha pelo atributo name
            campo_usuario = self.driver.find_element("name", "login")
            campo_senha = self.driver.find_element("name", "password")
            
            # Preencher os campos
            usuario = self.usuario # 'marcus.guidio@_greatpets'
            senha = self.senha # 'Gre@tPets=DIB8'
            
            campo_usuario.clear()
            campo_usuario.send_keys(usuario)
            
            campo_senha.clear()
            campo_senha.send_keys(senha)
            
            # Encontrar o botão de submit - é o botão com classe "bling-button call-to-action"
            botao_login = self.driver.find_element("css selector", "button.bling-button.call-to-action")
            
            print("Clicando no botão de login...")
            botao_login.click()
            
            # Aguardar processamento do login
            time.sleep(4)
            
            # Verificar se há mensagem de erro
            try:
                mensagem_erro = self.driver.find_element("css selector", ".warning-message:not([style*='display: none'])")
                if mensagem_erro.is_displayed():
                    texto_erro = mensagem_erro.find_element("css selector", "p").text
                    print(f"Erro no login: {texto_erro}")
                    return False
            except:
                # Sem mensagem de erro é um bom sinal
                pass
                
            # Se chegou até aqui, provavelmente o login foi bem-sucedido
            print("Login realizado com sucesso")
            return True
            
        except Exception as e:
            print(f"Erro ao tentar fazer login: {e}")
            import traceback
            traceback.print_exc()
            return False
    
    def _fechar_navegador(self):
        """Fecha o navegador se estiver aberto"""
        if self.driver:
            self.driver.quit()
            self.driver = None

    
