"""Candidate-check endpoints (v1 public API).

Given a candidate name (fuzzy) or NIF (exact), returns every judicial
process where that person appears as a party AND a monitored competitor
is a counterparty. Each process is tagged with an `alert_level`
computed server-side — see the module docstring of
`app/services/candidate_lookup.py` for the rules.
"""
from typing import Annotated, Any

from fastapi import APIRouter, Depends, Query, Request
from pydantic import BaseModel, Field
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.candidate_lookup import check_by_name, check_by_nif

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


class MonitoredCompanyBlock(BaseModel):
    nif: str
    legal_name: str | None
    monitoring_type: str
    role: str | None = Field(description="Role of the monitored company in the same process (réu/autor/…)")


class ProcessHit(BaseModel):
    process_number: str
    tribunal: str | None
    source: str = Field(description="distribuicao | cire")
    date_filed: str | None
    candidate_role: str | None
    monitored_company: MonitoredCompanyBlock
    alert_level: str = Field(description="high | medium | low")
    alert_reason: str


class MatchSummary(BaseModel):
    processes_total: int
    high: int
    medium: int
    low: int
    unique_competitors: int


class CandidateMatch(BaseModel):
    party_name: str
    party_nif: str | None
    confidence: float = Field(description="pg_trgm similarity 0..1; 1.0 for exact NIF match")
    processes: list[ProcessHit]
    summary: MatchSummary
    top_alert: str | None = Field(description="Highest alert_level across the processes (or null)")


class CheckResponse(BaseModel):
    query: dict[str, Any]
    searched_at: str
    total_matches: int
    matches: list[CandidateMatch]


@router.get(
    "/check",
    response_model=CheckResponse,
    summary="Cruza um nome de candidato contra processos judiciais + concorrentes",
    description=(
        "Procura fuzzy (pg_trgm) por `name` em `process_parties`, filtra aos "
        "processos onde também aparece uma empresa com `monitoring_type='competitor'`, "
        "e devolve um ou mais *matches* agrupados por (nome, NIF). Cada processo "
        "vem com um `alert_level`: `high` (candidato processa concorrente), "
        "`medium` (concorrente processa candidato) ou `low` (candidato é credor "
        "em insolvência de concorrente)."
    ),
    responses={
        200: {
            "description": "Matches agrupados por identidade",
            "content": {
                "application/json": {
                    "example": {
                        "query": {"name": "Bernardo Almeida", "min_similarity": 0.4, "limit": 50},
                        "searched_at": "2026-04-21T20:00:00Z",
                        "total_matches": 1,
                        "matches": [{
                            "party_name": "BERNARDO CRISTIANO TEIXEIRA DE ALMEIDA",
                            "party_nif": "234567890",
                            "confidence": 0.85,
                            "processes": [{
                                "process_number": "123/24.0TBLSB",
                                "tribunal": "Tribunal Judicial de Lisboa",
                                "source": "distribuicao",
                                "date_filed": "2024-03-10",
                                "candidate_role": "autor",
                                "monitored_company": {
                                    "nif": "509835333",
                                    "legal_name": "BLACKELIT SECURITY LDA",
                                    "monitoring_type": "competitor",
                                    "role": "réu",
                                },
                                "alert_level": "high",
                                "alert_reason": "candidate_sues_competitor",
                            }],
                            "summary": {
                                "processes_total": 1, "high": 1, "medium": 0, "low": 0,
                                "unique_competitors": 1,
                            },
                            "top_alert": "high",
                        }],
                    }
                }
            },
        },
        401: {"description": "API key inválida / em falta"},
        429: {"description": "Rate limit (60/minute) excedido"},
    },
)
@limiter.limit("60/minute")
async def candidates_check(
    request: Request,
    session: Annotated[AsyncSession, Depends(get_session)],
    _key: Annotated[dict, Depends(require_api_key)],
    name: Annotated[str, Query(min_length=3, max_length=200, description="Nome do candidato (fuzzy)")],
    min_similarity: Annotated[float, Query(ge=0.1, le=1.0, description="Corte mínimo de similaridade trigram; default 0.4")] = 0.4,
    limit: Annotated[int, Query(ge=1, le=200, description="Máx de rows retornadas pela query SQL; default 50")] = 50,
) -> CheckResponse:
    return CheckResponse(**await check_by_name(session, name, min_similarity, limit))


@router.get(
    "/check-by-nif",
    response_model=CheckResponse,
    summary="Cruzamento exacto por NIF (se o CV o tiver)",
    description=(
        "Variante do `/check` quando o candidato tem NIF conhecido. "
        "Match exacto, sem pg_trgm — `confidence` devolvido é sempre 1.0."
    ),
)
@limiter.limit("60/minute")
async def candidates_check_by_nif(
    request: Request,
    session: Annotated[AsyncSession, Depends(get_session)],
    _key: Annotated[dict, Depends(require_api_key)],
    nif: Annotated[str, Query(pattern=r"^\d{9}$", description="9 dígitos")],
    limit: Annotated[int, Query(ge=1, le=200)] = 50,
) -> CheckResponse:
    return CheckResponse(**await check_by_nif(session, nif, limit))
