"""
neogrid_pedidos.py
==================
Integração com a API REST Neogrid para coleta de pedidos (docType=5 / ORDERS).

Fluxo:
    1. Identifica documentos disponíveis na fila da Neogrid
    2. Captura o conteúdo de cada arquivo EDI
    3. Faz o parse do EDI ORDERS e extrai os dados do pedido
    4. Grava na TBPEDIDOSRETORNO (controle de coleta)
    5. Envia o pedido ao Softdib via API interna
    6. Confirma o recebimento na Neogrid (retira da fila)

Autenticação:
    Basic Auth — usuário (20 dígitos) + senha definida pelo parceiro.
    As credenciais são lidas do banco via self.setup(argv) → tabela de config da integração.

Dependências internas:
    generic.gn_api      → base da integração (DB, request, log, etc.)
    generic.gn_request  → wrapper HTTP

Referência da API:
    Teste:    https://ingestionlayer-teste.int.neogrid.com/rest/sidecar-rest
    Produção: https://ingestionlayer.neogrid.com/rest/sidecar-rest
"""
import traceback
import json
from datetime import datetime
from base64 import b64encode

from generic.gn_api     import gn_api
from generic.gn_request import Request


# ---------------------------------------------------------------------------
# Constantes
# ---------------------------------------------------------------------------

# DocType 5 = Pedido (ORDERS) conforme documentação Neogrid
NEOGRID_DOCTYPE_PEDIDO = 5

# Quantidade padrão de documentos por chamada (recomendação Neogrid: 50)
NEOGRID_DOC_QTY = 50

# URLs base por ambiente
NEOGRID_URLS = {
    "h"    : "https://ingestionlayer-teste.int.neogrid.com/rest/sidecar-rest/",
    "p" : "https://ingestionlayer.neogrid.com/rest/sidecar-rest/",
}


# ---------------------------------------------------------------------------
# Classe principal
# ---------------------------------------------------------------------------

class neogridapi(gn_api):

    def __init__(self, argv: list):
        """
        Inicializa a integração com a Neogrid.

        O método self.setup(argv) (herdado de gn_api) lê as configurações
        do banco de dados — inclusive as credenciais de autenticação —
        e popula self.environment_config.

        Após a autenticação, chama o rote_handler() que direciona
        para o método correto conforme a rota definida no map.json.
        """

        self.db           = False
        self.output_count = 0           # Controle de registros processados
        self.pedidos_confirmados = []   # IDs de documentos confirmados na Neogrid
        self.suffix       = "_app_pedidos3"  # Sufixo para logs e controle (ajuste conforme necessário)

        # Carrega configurações do banco (credenciais, ambiente, etc.)
        self.setup(argv)

        # Lê credenciais salvas no banco de configuração da integração
        # Esperado: NMTOKEN1API = username (20 dígitos)
        #           NMTOKEN1DADOS = password
        username = self.get_token_data("User")
        password = self.get_token_data("Senha")

        # Define ambiente: "teste" ou "producao" — salvo no config do banco
        ambiente = self.environment_config.get("ambiente", "teste").lower()
        base_url = NEOGRID_URLS.get(ambiente, NEOGRID_URLS["h"])

        # Gera o header de Basic Auth manualmente
        # Basic Auth = Base64(username:password)
        credencial_raw   = f"{username}:{password}"
        credencial_b64   = b64encode(credencial_raw.encode()).decode()
        auth_header      = f"Basic {credencial_b64}"

        # Instancia o cliente HTTP para a API Neogrid
        self.neogridapi_request = Request(
            base_url = base_url,
            headers  = {
                "Authorization" : auth_header,
                "Content-Type"  : "application/json",
                "Accept"        : "application/json",
            },
            params = {},
            verify = True,   # Valida SSL em produção; pode mudar para False em testes se necessário
        )

        # Chama o construtor pai (gn_api), que por sua vez invoca rote_handler()
        super(neogridapi, self).__init__()

        print("Fim")


    # -----------------------------------------------------------------------
    # ROTE HANDLER — chamado automaticamente pelo gn_api
    # -----------------------------------------------------------------------

    def pedidos_handler(self, data) :
        """
        Handler da rota 'pedidos' com método GET.
        Executa o ciclo completo: identificar → capturar → processar → confirmar.
        """

        print(":neogrid |> Iniciando coleta de pedidos...")

        # PASSO 1 — Identifica documentos disponíveis na fila
        documentos = self._identificar_documentos()

        if not documentos:
            print(":neogrid |> Nenhum pedido disponível na fila.")
            return

        print(f":neogrid |> {len(documentos)} documento(s) encontrado(s).")

        for doc in documentos:
            document_id = doc.get("documentId")
            filename    = doc.get("originalFilename", "")
            download_link = doc.get("downloadLink", "")

            # if not doc['documentId'] in [109675]:
            #     continue
            
            print(f":neogrid |> Processando documento [{document_id}] — {filename}")

            try:
                # Verifica se o pedido já foi processado anteriormente
                # Usa a TBPEDIDOSRETORNO para controle de duplicidade
                if self._pedido_ja_processado(document_id):
                    print(f":neogrid |> Documento [{document_id}] já processado. Confirmando e pulando...")
                    self.pedidos_confirmados.append(document_id)
                    continue

                # Captura o conteúdo do arquivo EDI
                order = self._capturar_documento(download_link)

                if not order:
                    print(f":neogrid |> Erro: conteúdo vazio para documento [{document_id}]")
                    continue

                order_to_send   = dict()

                try:
                    self.current_order = order
                    # Ira extrair dodos os dados referente a pedidos e aos items
                    # Ele extrai os valores relacionado ao que foi definido no map
                    order_to_send['pedido'       ]  = self.call("get_order_data" , self.current_order, is_required = True) #[ self.extractValues(order, 'pedido_fields') ]

                    # Existe um tratamento no Ideris, que se o pedido for Mercado Livre e o tipo for igual a FULL ele não irá importar o pedido
                    if order_to_send['pedido'       ][0]['CDFRETE'] == 0:
                        continue

                    if order_to_send['pedido'       ][0]['VLTOTAL'] == '':
                        continue
                
                    order_to_send['itenscarrinho']  = self.call("get_order_items", self.current_order, is_required = True) 
                    order_to_send['volumes']        = self.call("get_order_volumes", self.current_order, is_required = False) 
                    order_to_send['etiquetas']      = self.call("get_order_etiquetas", self.current_order, is_required = False) 
                    
                    #continue 
                    # funcao para verificar se o codigo do cliente existe no sistema softdib
                    if len( order_to_send['pedido'] ) and ( not order_to_send['pedido'][0]['CDCLIENTE']  or self.atualWs.get('alwaysUpdateClient')):
                        
                        cli_data, = order_to_send['pedido']
                        #cli_data['IDCLIENTEEXTERNO'] = ''
                        #if self.hasAttr("customer_data", self) :
                            # Funcao para recuperar os dados do cliente caso nao venha os dados completo para cadastro e recuperar id do cliente
                            #data = getattr(self, "customer_data")( data )
                        #else :
                        customer_data = self.call('customer_data', cli_data)
                        order_to_send['pedido'][0]['CDCLIENTE'] = self.salvar_cliente_softdib(customer_data or cli_data)
                        
                    sucesso_softdib = self.salvar_pedido_softdib(order_to_send)

                    if sucesso_softdib:
                        self.pedidos_confirmados.append(document_id)
                        print(f":neogrid |> Documento [{document_id}] processado com sucesso.")
                        
                except ZeroDivisionError:
                    print(traceback.format_exc())        
                # except Exception as error:
                #     self.createLog("999", str(error))
                #     continue
                    #print(error)
                    
                
                
                # Limpa a variavel
                self.current_order = {}


            except Exception as e:
                self.createLog(500, f"Erro ao processar documento [{document_id}]: {str(e)}")
                print(f":neogrid |> EXCEPTION no documento [{document_id}]: {e}")

        if self.pedidos_confirmados:
            self._confirmar_documentos(self.pedidos_confirmados)

    def get_order_data(self, order):
        import requests 

        consultar_cliente = self.select("SELECT * FROM TBCLIENTE WHERE VLCPFCNPJ = '{}'".format(order['order']['cabecalho']['cnpjComprador']))

        if len(consultar_cliente):
            self.current_order['order']['cabecalho']['cdcliente'] = consultar_cliente[0]['CDCLIENTE']
        else: 
            cnpj = order['order']['cabecalho']['cnpjComprador']
            consultar_cliente = requests.get("https://receitaws.com.br/v1/cnpj/{}".format(cnpj))

            if consultar_cliente.ok:
                data = consultar_cliente.json()

                telefones = self.filtrar_telefones(data.get("telefone", ""))

                dados_cliente = {
                    "RAZAO_SOCIAL"      : data.get("nome", ""),
                    "NOME_FANTASIA"     : data.get("fantasia", ""),
                    "TIPO_PESSOA"       : "J",
                    "CNPJ_CPF"          : self.only_numbers(cnpj),

                    "ENDERECO"          : data.get("logradouro", ""),
                    "ENDERECO_NRO"      : self.only_numbers(data.get("numero", "")),
                    "COMPLEMENTO"       : data.get("complemento", ""),
                    "CEP"               : self.only_numbers(data.get("cep", "")),

                    "BAIRRO"            : data.get("bairro", ""),
                    "CIDADE"            : data.get("municipio", ""),
                    "ESTADO"            : data.get("uf", ""),

                    "DDD"               : telefones[0]['DDD'] if len(telefones) > 0 else '',
                    "TELEFONE"          : telefones[0]['TELEFONE'] if len(telefones) > 0 else '',

                    "EMAIL"             : data.get("email", ""),
                }

                self.current_order['order']['cabecalho']['cdcliente'] = self.salvar_cliente_softdib(dados_cliente)

        return [ self.extractValues(self.current_order['order'], 'pedido_fields'), ]
    
    def filtrar_telefones(self, telefones):
        import re

        telefone_str = telefones

        # Grupos de captura: (DDD) e (número com hífen)
        telefones = re.findall(r'\((\d{2})\)\s?(\d{4,5}-\d{4})', telefone_str)

        lista = []

        # Resultado: [('11', '3769-5731'), ('11', '3769-5717')]
        for ddd, numero in telefones:
            lista.append({
                "DDD"      : ddd,
                "TELEFONE" : numero.replace('-', ''),
            })  

        return lista
    
    def order_id(self, value):
        return self.current_order['order']['cabecalho']['numeroPedidoComprador']

    def get_order_items(self, data):
        return [ self.extractValues(item, "itenscarrinho_fields") for item in data['order']['itens']['item'] ]
    
    def customer_data(self, cli_data):
        import requests 

        # https://receitaws.com.br/v1/cnpj/{cnpj}

        # cliente = requests.get("https://receitaws")

        consultar_cliente = self.select("SELECT * FROM TBCLIENTE WHERE CPFCNPJ = '{}'".format(cli_data['CPFCNPJ']))

        return cli_data


    # -----------------------------------------------------------------------
    # PASSO 1 — Identificar documentos disponíveis
    # -----------------------------------------------------------------------

    def _identificar_documentos(self) -> list:
        """
        Chama GET /api/v1/proxy/documents/metadata
        Retorna lista de documentos disponíveis para download.

        Query params:
            docType : 5 (ORDERS / Pedido)
            docQty  : 50 (recomendação Neogrid)
        """

        response = self.neogridapi_request.doRequest(
            rote   = "api/v1/proxy/documents/metadata",
            method = "GET",
            params = {
                "docType" : NEOGRID_DOCTYPE_PEDIDO,
                "docQty"  : NEOGRID_DOC_QTY,
            },
        )

        if not response or not response.ok:
            self.createLog(
                response.status_code if response else 0,
                f"Erro ao identificar documentos: {response.text if response else 'sem resposta'}"
            )
            return []

        try:
            return response.json()
        except Exception as e:
            self.createLog(500, f"Erro ao parsear metadados: {e}")
            return []


    # -----------------------------------------------------------------------
    # Capturar conteúdo do documento
    # -----------------------------------------------------------------------

    def _capturar_documento(self, downloadLink) -> str:
        """
        Chama GET /api/v1/proxy/document/content
        Retorna o conteúdo bruto do arquivo EDI (texto plano).

        Para arquivos > 25MB usar /api/v1/proxy/document (multipart).
        Para conteúdo paginado usar /api/v1/proxy/document/content/chunks.
        Por padrão usamos /content (adequado para EDI de pedidos que costumam ser pequenos).
        """
        import requests

        ## Opção 1: Usar a downloadLink diretamente (ela já tem os parâmetros necessários)
        response = requests.get(downloadLink)
        if not response.ok:
            self.createLog(
                response.status_code,
                f"Erro ao baixar documento: {response.text}"
            )
            return ""

        return json.loads(response.text)


    # -----------------------------------------------------------------------
    # Verificar duplicidade
    # -----------------------------------------------------------------------

    def _pedido_ja_processado(self, document_id: int) -> bool:
        """
        Verifica na TBPEDIDOSRETORNO se o documento já foi processado.
        Usa CDPEDIDOEXTERNO = document_id AND NMAPI = 'neogrid'.
        """

        return self.exists(
            "IDTBPEDIDOSRETORNO",
            "TBPEDIDOSRETORNO",
            [
                f"CDPEDIDOEXTERNO='{document_id}'",
                f"NMAPI='neogrid'",
            ]
        )


    # -----------------------------------------------------------------------
    # Confirmar recebimento na Neogrid (PATCH)
    # -----------------------------------------------------------------------

    def _confirmar_documentos(self, document_ids: list):
        """
        Chama PATCH /api/v1/proxy/documents para confirmar o recebimento.

        Após a confirmação, os documentos saem da fila da Neogrid
        e não são mais retornados nas chamadas de identificação.

        Payload:
            {
                "documents": [
                    {
                        "documentId" : <int>,
                        "status"     : "PROCESSED",
                        "message"    : "<texto livre>"
                    }
                ]
            }
        """

        payload = {
            "documents": [
                {
                    "documentId" : doc_id,
                    "status"     : "PROCESSED",
                    "message"    : f"Pedido recebido e integrado com sucesso em {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
                }
                for doc_id in document_ids
            ]
        }

        print(f":neogrid |> Confirmando {len(document_ids)} documento(s)...")

        response = self.neogridapi_request.doRequest(
            rote   = "api/v1/proxy/documents",
            method = "PATCH",
            json   = payload,
        )

        if not response or not response.ok:
            self.createLog(
                response.status_code if response else 0,
                f"Erro ao confirmar documentos: {response.text if response else 'sem resposta'}"
            )
            return

        try:
            retorno = response.json()
            for item in retorno:
                doc_id = item.get("documentId")
                status = item.get("status")
                msg    = item.get("message", "")
                print(f":neogrid |> Confirmação [{doc_id}] → {status} | {msg}")
        except Exception as e:
            self.createLog(500, f"Erro ao parsear resposta da confirmação: {e}")


    # -----------------------------------------------------------------------
    # Método auxiliar — confirmar com erro (TRANSMISSION_ERROR)
    # -----------------------------------------------------------------------

    def _confirmar_erro(self, document_id: int, mensagem: str):
        """
        Confirma um documento como TRANSMISSION_ERROR na Neogrid.
        Usar quando houve falha irreversível no processamento e
        o documento precisa sair da fila para evitar reprocessamento infinito.

        Atenção: Usar com critério — docId marcado como erro não volta para a fila.
        """

        payload = {
            "documents": [
                {
                    "documentId" : document_id,
                    "status"     : "TRANSMISSION_ERROR",
                    "message"    : mensagem,
                }
            ]
        }

        self.neogridapi_request.doRequest(
            rote   = "api/v1/proxy/documents",
            method = "PATCH",
            json   = payload,
        )