"""Watchlist de insolvências (v1 public API).

Alimentada pelo Sabichão: ao criar um cliente, o NIF é empurrado para cá e a
entidade passa a ser vigiada nos anúncios de insolvência do CITIUS.

Duas garantias que moldam o desenho:

* **Nunca se remove.** Um cliente desactivado continua a ser um devedor cujo
  processo de insolvência interessa acompanhar. O endpoint só faz upsert.
* **Idempotente e tolerante.** O Sabichão chama isto no fluxo de criação de
  cliente; se falhar, o cliente tem de ser criado na mesma. Do lado de cá isso
  traduz-se em aceitar repetições sem erro e em normalizar o NIF (o Sabichão
  guarda-os com espaços, "517 607 891").
"""
import logging
from typing import Annotated, Any

from fastapi import APIRouter, Depends, Request
from pydantic import BaseModel, Field
from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncSession

from app.auth.api_key_auth import require_api_key
from app.db import get_session
from app.rate_limit import limiter
from app.services.insolvency_matching import rematch_nif

logger = logging.getLogger(__name__)

router = APIRouter(prefix="/watchlist", tags=["public-api"])


class WatchlistItem(BaseModel):
    nif: str = Field(description="NIF/NIPC; aceita espaços e pontuação")
    name: str | None = None
    external_ref: str | None = Field(
        default=None, description="Referência do sistema de origem (nº de contrato)"
    )


class WatchlistSync(BaseModel):
    items: list[WatchlistItem]


class WatchlistResult(BaseModel):
    recebidos: int
    aceites: int
    ignorados: list[str] = Field(description="NIFs rejeitados por não terem 9 dígitos")
    hits_retroactivos: int = Field(
        description="Anúncios já arquivados que passaram a dar match com estes NIFs"
    )


def _norm_nif(raw: str | None) -> str | None:
    if not raw:
        return None
    digits = "".join(ch for ch in raw if ch.isdigit())
    return digits if len(digits) == 9 else None


@router.post("", response_model=WatchlistResult)
@limiter.limit("60/minute")
async def sync_watchlist(
    request: Request,
    payload: WatchlistSync,
    session: Annotated[AsyncSession, Depends(get_session)],
    key: Annotated[dict[str, Any], Depends(require_api_key)],
) -> WatchlistResult:
    """Regista (ou actualiza) entidades a vigiar. Aceita um ou muitos.

    Cada NIF novo é imediatamente cruzado contra **todo** o arquivo de anúncios
    já guardado — é o que dá sentido a varrer o país inteiro: um cliente
    adicionado hoje pode já ter sido declarado insolvente na semana passada.
    """
    aceites = 0
    ignorados: list[str] = []
    hits = 0

    for item in payload.items:
        nif = _norm_nif(item.nif)
        if not nif:
            ignorados.append(item.nif)
            continue
        await session.execute(
            text(
                """
                INSERT INTO insolvency_watchlist (nif, name, source, external_ref)
                VALUES (:nif, :name, 'sabichao', :ref)
                ON CONFLICT (nif) DO UPDATE SET
                    name = COALESCE(EXCLUDED.name, insolvency_watchlist.name),
                    external_ref = COALESCE(
                        EXCLUDED.external_ref, insolvency_watchlist.external_ref),
                    active = true,
                    updated_at = now()
                """
            ),
            {"nif": nif, "name": item.name, "ref": item.external_ref},
        )
        # a empresa passa a entidade monitorizada de pleno direito, para os
        # scrapes por NIF (publicações MJ) também a cobrirem
        await session.execute(
            text(
                """
                INSERT INTO companies (nif, legal_name, monitoring_type, monitored)
                VALUES (:nif, :name, 'client', true)
                ON CONFLICT (nif) DO NOTHING
                """
            ),
            {"nif": nif, "name": item.name or f"NIF {nif}"},
        )
        hits += await rematch_nif(session, nif)
        aceites += 1

    await session.commit()
    logger.info(
        "watchlist sync (key=%s): %d aceites, %d ignorados, %d hits retroactivos",
        key.get("label"), aceites, len(ignorados), hits,
    )
    return WatchlistResult(
        recebidos=len(payload.items),
        aceites=aceites,
        ignorados=ignorados,
        hits_retroactivos=hits,
    )
