# Exemplo de busca dos pedidos pela API do Softeum Pedidos, em Python 3.9 ou mais, só com a biblioteca padrão.
# Para o ERP que não recebe o aviso (sem endereço HTTPS público): ele busca os pedidos prontos, lança e confirma.
#
# Rodar:   SOFTEUM_CHAVE=sp_live_... python3 buscar_pedidos.py
#          (agende a cada 1 a 5 minutos: cron, Agendador de Tarefas do Windows, job do ERP)
# Outro endereço (homologação): SOFTEUM_API=https://... python3 buscar_pedidos.py
# No ERP de testes (homologação): SOFTEUM_HOMOLOGACAO=1 SOFTEUM_CHAVE=sp_live_... python3 buscar_pedidos.py
#          Só com ela o pedido de teste do painel ("teste": true) é lançado. Sem ela (o ERP de produção), ele é
#          recusado com o motivo e sai da fila: o ERP de produção nunca lança um pedido de teste.

import json
import os
import random
import sys
import time
import urllib.error
import urllib.parse
import urllib.request

API = os.environ.get("SOFTEUM_API", "https://api.softeum.com.br/v1")
CHAVE = os.environ.get("SOFTEUM_CHAVE")
HOMOLOGACAO = os.environ.get("SOFTEUM_HOMOLOGACAO") == "1"

if not CHAVE:
    sys.exit("Defina SOFTEUM_CHAVE com a chave de acesso do painel (Integração → Como o ERP recebe → O ERP busca os pedidos).")


class Recusa(Exception):
    """Lance com o motivo para recusar o pedido (cliente bloqueado, condição que não existe no ERP)."""


def chamar(metodo: str, caminho: str, corpo=None):
    """Chamada à API. Erros vêm em application/problem+json (title, detail, erros[])."""
    dados = json.dumps(corpo).encode() if corpo is not None else None
    for tentativa in range(1, 5):
        pedido = urllib.request.Request(f"{API}{caminho}", data=dados, method=metodo)
        pedido.add_header("Authorization", f"Bearer {CHAVE}")
        if dados is not None:
            pedido.add_header("Content-Type", "application/json")
        try:
            with urllib.request.urlopen(pedido, timeout=30) as resposta:
                return resposta.status, json.loads(resposta.read() or b"null")
        except urllib.error.HTTPError as erro:
            # Muitas chamadas: espera o que o Retry-After pede e tenta de novo.
            if erro.code == 429 and tentativa <= 3:
                time.sleep(int(erro.headers.get("Retry-After", "10")))
                continue
            try:
                return erro.code, json.loads(erro.read() or b"null")
            except ValueError:
                return erro.code, None
    raise RuntimeError("A API continuou respondendo 429.")


def lancar_no_erp(pedido: dict) -> str:
    """Aqui entra o lançamento de verdade no ERP. Devolve o número do pedido de venda criado.
    Lance de forma idempotente: se pedido["id"] já foi lançado, devolva o número existente (UNIQUE no id)."""
    print(f"Lançando o pedido {pedido['numero_comprador']} de {pedido['comprador']['razao_social']}: "
          f"{len(pedido['itens'])} item(ns), R$ {pedido['valor_total']}")
    return str(500000 + random.randint(0, 999))


def recusar(pedido: dict, motivo: str):
    """Recusa o pedido com o motivo: ele sai de "pronto" e o painel mostra o motivo ao time."""
    status, corpo = chamar("POST", f"/pedidos/{pedido['id']}/recusa", {"motivo": motivo})
    print(f"  recusado: {motivo}" if status == 200 else f"  recusa respondeu {status}: {(corpo or {}).get('detail')}")


def rodada():
    # Cada rodada começa do início da fila de prontos (sem cursor): o pedido sai de "pronto" quando é confirmado ou
    # recusado, então o que ficou para trás aparece de novo. O cursor serve só para ler as páginas da mesma rodada.
    cursor = None
    processados = 0
    while True:
        consulta = "/pedidos?situacao=pronto&limite=100" + (f"&cursor={urllib.parse.quote(cursor)}" if cursor else "")
        status, lista = chamar("GET", consulta)
        if status != 200:
            raise RuntimeError(f"A lista respondeu {status}: {(lista or {}).get('detail', '')}")
        for pedido in lista["dados"]:
            # O pedido de teste do painel (seção 8 do guia) só entra no ERP de testes: no de produção, sai da fila recusado.
            if pedido.get("teste") is True and not HOMOLOGACAO:
                print(f"Pedido de teste {pedido['numero_comprador']}: não é lançado no ERP de produção.")
                recusar(pedido, "Pedido de teste: não lançado no ERP de produção.")
                processados += 1
                continue
            try:
                numero = lancar_no_erp(pedido)
                status, corpo = chamar("POST", f"/pedidos/{pedido['id']}/confirmacao", {"numero_erp": numero})
                print(f"  confirmado como {numero}" if status == 200 else f"  confirmação respondeu {status}: {(corpo or {}).get('detail')}")
            except Recusa as recusa:
                recusar(pedido, str(recusa))
            except Exception as erro:  # falha do ERP: o pedido continua pronto e volta na próxima rodada
                print(f"  não deu para lançar agora: {erro}", file=sys.stderr)
            processados += 1
        cursor = lista["proximo_cursor"]
        if not cursor:
            break
    print(f"Rodada terminada: {processados} pedido(s).")


if __name__ == "__main__":
    rodada()
