"""Consulta de empresa por NIF: VIES + SICAE.

Substitui `app/scrapers/ptdata.py`. O ptdata.org devolve 503 desde meados de
julho de 2026 e era de onde vinha o CAE.

Cada fonte dá o que faz melhor, em paralelo:

* **VIES** (UE, oficial, sem chave nem quota) — nome legal e morada.
* **SICAE** (consórcio IRN/AT/INE/IAPMEI, gratuito) — CAE Rev.4 principal e
  secundários. Não devolve morada.

Há ainda uma terceira fonte melhor para nome/morada/natureza jurídica: as
publicações do registo comercial (ver `mj_irn_sync`). Mas essa é por empresa
monitorizada e assíncrona; isto é o caminho rápido para quando alguém escreve
um NIF num formulário.
"""
import asyncio
import logging
from typing import Any

import httpx

from app.services.nif import is_valid_nif

logger = logging.getLogger(__name__)

VIES_URL = "https://ec.europa.eu/taxation_customs/vies/rest-api/ms/PT/vat/{nif}"
VIES_TIMEOUT = httpx.Timeout(5.0, connect=2.5)

# Cache por processo. Evita repetir a consulta durante uma rajada de adições
# (o formulário faz "Procurar" e depois "Gravar", que de outro modo consultavam
# duas vezes).
_CACHE: dict[str, dict[str, Any]] = {}
_CACHE_MAX = 512


async def _try_vies(nif: str) -> dict[str, Any] | None:
    try:
        async with httpx.AsyncClient(timeout=VIES_TIMEOUT) as c:
            r = await c.get(VIES_URL.format(nif=nif), headers={"Accept": "application/json"})
        if r.status_code != 200:
            return None
        data = r.json()
        if not data.get("isValid"):
            return None
        addr_raw = (data.get("address") or "").strip()
        # O VIES devolve "---" quando a empresa não autoriza a divulgação
        addr = " ".join(addr_raw.split()) if addr_raw and addr_raw.strip("- ") else None
        return {
            "name": (data.get("name") or "").strip() or None,
            "address": addr,
            "raw": data,
        }
    except (httpx.HTTPError, ValueError) as e:
        logger.warning("VIES lookup falhou para %s: %s", nif, e)
        return None


async def _try_sicae(nif: str) -> dict[str, Any] | None:
    from app.scrapers.sicae import lookup_by_nipc

    try:
        return await lookup_by_nipc(nif)
    except Exception as e:  # noqa: BLE001
        logger.info("SICAE indisponível para %s: %s", nif, e)
        return None


async def lookup_nif(nif: str) -> dict[str, Any]:
    """Consulta um NIF português.

    Devolve sempre — cai para `source='local'` com validação mod-11 quando
    ambas as fontes falham. Mantém a assinatura e o formato do antigo
    `ptdata.lookup_nif` para os chamadores não terem de mudar.
    """
    cached = _CACHE.get(nif)
    if cached is not None:
        return dict(cached)

    result: dict[str, Any] = {
        "nif": nif,
        "valid": is_valid_nif(nif),
        "source": "local",
        "legal_name": None,
        "trade_name": None,
        "cae": None,
        "address": None,
        "status": None,
        "raw": None,
    }
    if not result["valid"]:
        return result

    # Em paralelo: são independentes e nenhuma depende do resultado da outra.
    vies, sicae = await asyncio.gather(_try_vies(nif), _try_sicae(nif))

    raw: dict[str, Any] = {}
    sources: list[str] = []

    if vies:
        sources.append("vies")
        result["legal_name"] = vies.get("name")
        result["address"] = vies.get("address")
        raw["vies"] = vies.get("raw")

    if sicae:
        sources.append("sicae")
        # O VIES ganha para o nome legal; o SICAE preenche quando o VIES falha.
        if not result["legal_name"]:
            result["legal_name"] = sicae.get("denominacao")
        cae = sicae.get("cae")
        if cae:
            result["cae"] = cae
        raw["sicae"] = sicae

    if sources:
        result["source"] = "+".join(sources)
        result["raw"] = raw

    if len(_CACHE) >= _CACHE_MAX:
        _CACHE.pop(next(iter(_CACHE)))
    _CACHE[nif] = dict(result)
    return result


async def lookup_by_name(name: str, limit: int = 10) -> list[dict[str, Any]]:
    """Pesquisa inversa nome -> NIPC, via SICAE."""
    from app.scrapers.sicae import search_by_name

    try:
        return await search_by_name(name, limit=limit)
    except Exception as e:  # noqa: BLE001
        logger.info("pesquisa por nome %r falhou: %s", name, e)
        return []
