# Exemplo de endereço de entrega do Softeum Pedidos, em Python 3.9 ou mais, só com a biblioteca padrão.
#
# Rodar:   SOFTEUM_SEGREDO=whsec_... python3 receptor.py
# Testar:  node simular-aviso.mjs http://localhost:8080/softeum/pedidos whsec_... --pedido
#
# Em produção o endereço precisa ser HTTPS público: deixe este processo atrás do proxy ou balanceador
# que já faz o HTTPS do ERP. Em Flask/Django/FastAPI a lógica é a mesma: use o corpo BRUTO
# (request.get_data(), request.body, await request.body()), nunca o JSON já convertido.

import hashlib
import hmac
import json
import os
import sys
import time
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer

SEGREDO = os.environ.get("SOFTEUM_SEGREDO")
PORTA = int(os.environ.get("PORTA", "8080"))
TOLERANCIA_SEGUNDOS = 300

if not SEGREDO:
    sys.exit("Defina SOFTEUM_SEGREDO com o segredo do painel (Integração → Como o ERP recebe → Aviso no endereço do ERP).")

# pedido.id → número do pedido no ERP. No ERP de verdade: coluna do pedido de venda com UNIQUE.
lancados: dict[str, str] = {}


def assinatura_valida(cabecalho, corpo_bruto: bytes, segredo: str, agora=None) -> bool:
    """Confere o cabeçalho Softeum-Assinatura contra o corpo exatamente como chegou."""
    if not cabecalho:
        return False
    partes = dict(p.strip().split("=", 1) for p in cabecalho.split(",") if "=" in p)
    try:
        t = int(partes["t"])
    except (KeyError, ValueError):
        return False
    agora = int(time.time()) if agora is None else agora
    if abs(agora - t) > TOLERANCIA_SEGUNDOS or "v1" not in partes:
        return False
    esperado = hmac.new(segredo.encode(), f"{t}.".encode() + corpo_bruto, hashlib.sha256).hexdigest()
    return hmac.compare_digest(esperado, partes["v1"])


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."""
    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 + len(lancados) + 1)


class Receptor(BaseHTTPRequestHandler):
    def responder(self, status: int, corpo: dict):
        dados = json.dumps(corpo).encode()
        self.send_response(status)
        self.send_header("Content-Type", "application/json")
        self.send_header("Content-Length", str(len(dados)))
        self.end_headers()
        self.wfile.write(dados)

    def do_POST(self):
        # 1. O corpo BRUTO: a assinatura é calculada sobre os bytes que chegaram.
        corpo = self.rfile.read(int(self.headers.get("Content-Length", 0)))
        if not assinatura_valida(self.headers.get("Softeum-Assinatura"), corpo, SEGREDO):
            return self.responder(401, {"erro": "Assinatura inválida."})
        try:
            aviso = json.loads(corpo)
        except ValueError:
            return self.responder(400, {"erro": "JSON inválido."})

        # 2. Aviso de teste e tipos que o ERP ainda não conhece: só 2xx, nada lançado.
        if aviso.get("tipo") != "pedido.aprovado":
            return self.responder(200, {})

        # 3. Pedido repetido: não lança de novo, devolve o mesmo número.
        pedido = aviso["pedido"]
        if pedido["id"] in lancados:
            return self.responder(200, {"numero_erp": lancados[pedido["id"]]})

        # 4. Lança e devolve o número: o pedido fica "confirmado" no Softeum na hora.
        try:
            numero_erp = lancar_no_erp(pedido)
        except Exception as erro:  # fora de 2xx, o Softeum tenta de novo depois
            print("Não deu para lançar:", erro, file=sys.stderr)
            return self.responder(503, {"erro": "Não deu para lançar agora."})
        lancados[pedido["id"]] = numero_erp
        return self.responder(200, {"numero_erp": numero_erp})


if __name__ == "__main__":
    print(f"Esperando avisos do Softeum em http://localhost:{PORTA}")
    ThreadingHTTPServer(("", PORTA), Receptor).serve_forever()
