"""Pesquisa global.

Três famílias, porque procurar um nome pode significar três coisas diferentes:

1. **Empresas monitorizadas** — o que se quer quando se escreve o nome de um
   cliente ou concorrente: a ficha dele.
2. **Entidades do registo comercial** — pessoas e empresas do índice nacional,
   incluindo as que não monitorizamos. É o que responde a "quem é este sócio".
3. **Partes de processos judiciais** — o que esta pesquisa fazia, e só isso.

Era essa a razão pela qual **procurar um cliente não devolvia nada**: os clientes
estão fora dos scrapers judiciais, portanto não aparecem em `process_parties`, e
não havia mais nenhum sítio onde procurar. O subtítulo da página até o admitia
("procura nomes ou entidades dentro de processos"), mas quem escreve o nome de um
cliente não tem por que saber isso.

As três consultas são independentes e cada uma traz o seu limite, para uma família
com muitos resultados não empurrar as outras para fora.
"""
from typing import Annotated, Any

from fastapi import APIRouter, Depends, Query
from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncSession

from app.auth.deps import CurrentUser, current_user
from app.db import get_session

router = APIRouter(prefix="/search", tags=["search"])


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.get("")
async def global_search(
    session: Annotated[AsyncSession, Depends(get_session)],
    _user: Annotated[CurrentUser, Depends(current_user)],
    query: str = Query(..., min_length=3, max_length=200),
    limit: int = Query(50, ge=1, le=500),
    min_similarity: float = Query(0.25, ge=0.1, le=1.0),
) -> dict[str, Any]:
    q = query.strip()
    nif = _norm_nif(q)
    per_family = max(5, limit // 2)

    # 1. Empresas monitorizadas. Por NIF é exacto; por nome usa o índice trigram
    # `ix_companies_name_trgm` que já existe. O ILIKE apanha o caso de se escrever
    # uma parte curta e distintiva do nome, onde o trigram é fraco.
    companies = (
        await session.execute(
            text(
                """
                SELECT c.id::text, c.nif, c.legal_name, c.monitoring_type, c.active,
                       c.concelho, c.risk_score,
                       GREATEST(similarity(c.legal_name, :q),
                                CASE WHEN c.nif = :nif THEN 1.0 ELSE 0 END,
                                CASE WHEN c.legal_name ILIKE :like THEN 0.6 ELSE 0 END) AS sim
                  FROM companies c
                 WHERE c.nif = :nif
                    OR c.legal_name ILIKE :like
                    OR (c.legal_name % :q AND similarity(c.legal_name, :q) >= :mins)
                 ORDER BY sim DESC, c.legal_name
                 LIMIT :lim
                """
            ),
            {"q": q, "nif": nif or "", "like": f"%{q}%", "mins": min_similarity,
             "lim": per_family},
        )
    ).mappings().all()

    # 2. Entidades do registo comercial — pessoas incluídas. `entity_incident_summary`
    # traz o tipo de monitorização e os incidentes, para o resultado já dizer se
    # aquela pessoa tem uma insolvência a correr.
    entities = (
        await session.execute(
            text(
                """
                SELECT e.nif, e.name, e.kind, e.concelho, e.degree_current,
                       i.monitoring_type, i.insolvency_open, i.cire_ann_as_debtor,
                       GREATEST(similarity(e.name_norm, :qn),
                                CASE WHEN e.nif = :nif THEN 1.0 ELSE 0 END) AS sim
                  FROM registry_entities e
                  LEFT JOIN entity_incident_summary i ON i.nif = e.nif
                 WHERE e.nif = :nif
                    OR (e.name_norm % :qn AND similarity(e.name_norm, :qn) >= :mins)
                 ORDER BY sim DESC, e.degree_current DESC
                 LIMIT :lim
                """
            ),
            {"qn": q.lower(), "nif": nif or "", "mins": min_similarity, "lim": per_family},
        )
    ).mappings().all()

    # 3. Partes de processos judiciais — o que a pesquisa já fazia.
    parties = (
        await session.execute(
            text(
                """
                SELECT pp.name AS party_name, pp.nif AS party_nif, pp.role AS party_role,
                       p.id::text AS process_id, p.process_number, p.tribunal,
                       p.source, p.date_filed,
                       c.id::text AS company_id, c.legal_name AS company_name,
                       similarity(pp.name, :q) AS sim
                  FROM process_parties pp
                  JOIN processes p ON p.id = pp.process_id
                  JOIN companies c ON c.id = p.company_id
                 WHERE pp.name % :q AND similarity(pp.name, :q) >= :mins
                 ORDER BY sim DESC, p.date_filed DESC
                 LIMIT :lim
                """
            ),
            {"q": q, "mins": min_similarity, "lim": per_family},
        )
    ).mappings().all()

    return {
        "query": q,
        "empresas": [dict(r) for r in companies],
        "entidades": [dict(r) for r in entities],
        "partes": [dict(r) for r in parties],
        "total": len(companies) + len(entities) + len(parties),
    }
