"""Exemplo de integração pela API REST: o sistema de uma loja consulta o catálogo, os
clientes e os últimos pedidos da empresa. Com GRAVAR=1 ele também cadastra um cliente
de exemplo (ou reaproveita o que já existe com o mesmo CPF) e lança um pedido para ele
com o primeiro produto do catálogo.

Só a biblioteca padrão do Python 3 — nada para instalar.

Com o usuário da integração:

    API_BASE=https://app.suaempresa.com.br \\
    API_USUARIO=integracao@suaempresa.com.br \\
    API_SENHA='senha do usuário da integração' \\
    python3 exemplo.py

Com uma conexão OAuth2, no lugar de usuário e senha: API_TOKEN=<token> API_ORG=<empresa>.
API_ORG também escolhe a empresa no login quando o usuário tem acesso a mais de uma.
"""

import json
import os
import sys
import urllib.error
import urllib.request

# CPF válido reservado para o cliente de exemplo: rodar de novo reaproveita o cadastro.
CPF_DO_EXEMPLO = "11144477735"

BASE = os.environ.get("API_BASE", "").rstrip("/")
cabecalhos = {"Content-Type": "application/json"}


def chama(metodo, rota, corpo=None):
    dados = json.dumps(corpo).encode() if corpo is not None else None
    req = urllib.request.Request(BASE + rota, data=dados, method=metodo, headers=cabecalhos)
    try:
        with urllib.request.urlopen(req) as resposta:
            return json.loads(resposta.read() or b"{}")
    except urllib.error.HTTPError as erro:
        # O corpo traz o motivo, ex.: {"code":9,"message":"o acesso por API exige o módulo..."}
        sys.exit(f"{metodo} {rota} falhou com {erro.code}: {erro.read().decode()}")


def entra():
    """Monta a credencial das chamadas e devolve o nome de quem entrou."""
    if os.environ.get("API_TOKEN"):
        cabecalhos["Authorization"] = "Bearer " + os.environ["API_TOKEN"]
        cabecalhos["Org"] = os.environ["API_ORG"]
        return "conexão OAuth2"
    login = chama("POST", "/api/users/login", {
        "username": os.environ["API_USUARIO"],
        "password": os.environ["API_SENHA"],
        "orgId": os.environ.get("API_ORG", ""),
    })
    cabecalhos["Token"] = login["token"]
    cabecalhos["Org"] = os.environ.get("API_ORG") or login["currentOrg"]["id"]
    return login["name"]


def cliente_de_exemplo():
    """Devolve o cliente do CPF de exemplo, cadastrando-o na primeira vez."""
    achados = chama("POST", "/api/pessoas/list", {"cpfCnpjs": [CPF_DO_EXEMPLO]}).get("personList", [])
    if achados:
        return achados[0]
    return chama("POST", "/api/pessoas", {"person": {
        "name": "Cliente de exemplo da integração",
        "cpfCnpj": CPF_DO_EXEMPLO,
        "tags": [{"value": "customer"}],
    }})["person"]


def lanca_pedido(comprador, item):
    """Abre o pedido e inclui o item. O item vai pela rota de produtos do pedido, como na
    tela: é ela que traz nome, unidade e preço do cadastro e calcula os totais."""
    aberto = chama("POST", "/api/pedidos", {"pedido": {
        "tipo": "ped",
        "pessoa": {"id": comprador["id"], "nome": comprador["name"]},
        "obs": "Pedido de exemplo lançado pela API",
    }})["pedido"]
    chama("POST", f"/api/pedidos/{aberto['id']}/produtos", {
        "productId": item["id"],
        "product": {"produtoId": item["id"], "quantidade": 2},
    })
    return chama("GET", f"/api/pedidos/{aberto['id']}")["pedido"]


if not BASE or not (os.environ.get("API_TOKEN") or (os.environ.get("API_USUARIO") and os.environ.get("API_SENHA"))):
    sys.exit("Informe API_BASE e o acesso (API_USUARIO e API_SENHA, ou API_TOKEN e API_ORG).")

print(f"Conectado como {entra()} na empresa {cabecalhos['Org']}")

# Listagens vão por POST: o filtro não cabe no endereço.
catalogo = chama("POST", "/api/produtos/list", {"pageSize": 5}).get("produtoList", [])
print("\nProdutos:")
for p in catalogo:
    print(f"  {p['nome']:<45} R$ {p.get('valorUnitario', 0):>10.2f}")

clientes = chama("POST", "/api/pessoas/list", {"tipo": "customer", "pageSize": 5}).get("personList", [])
print("\nClientes:")
for c in clientes:
    print(f"  {c['name']:<45} {c.get('cpfCnpj', '')}")

ultimos = chama("POST", "/api/pedidos/list", {
    "types": ["ped"],
    "filter": {"limit": 5, "orderBy": [{"fieldName": "createdAt", "direction": "DESC"}]},
}).get("pedidoList", [])
print("\nÚltimos pedidos:")
for p in ultimos:
    nome = p.get("pessoa", {}).get("nome", "")
    print(f"  nº {p.get('numero', 0):<6} {nome:<38} R$ {p.get('valorTotal', 0):>10.2f}  {p.get('situacao', '')}")

if os.environ.get("GRAVAR") != "1":
    print("\nPara cadastrar um cliente e lançar um pedido de exemplo, rode de novo com GRAVAR=1.")
elif not catalogo:
    print("\nCadastre ao menos um produto para lançar o pedido de exemplo.")
else:
    lancado = lanca_pedido(cliente_de_exemplo(), catalogo[0])
    print(f"\nPedido nº {lancado['numero']} lançado para {lancado['pessoa']['nome']}: "
          f"R$ {lancado.get('valorTotal', 0):.2f} ({lancado['situacao']})")
