"""Pessoas do registo comercial, por empresa e por pessoa.

Passou a ler o `registry_edges`. Vinha do `publication_people`, que **não tem
noção de estado actual**: guardava uma linha por publicação e mostrava para
sempre a última em que a pessoa aparecia.

O sintoma foi visível na ficha da SEGUNOR: uma sócia com 125.000 € de 2019 ao
lado da cap table de 2026, onde ela já não consta — porque o aumento de capital
de 2026-04-23 trouxe outra estrutura. Duas listas a dizer coisas diferentes
sobre a mesma empresa, na mesma página. Ela continua gerente, e é isso que agora
se mostra.
"""
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(tags=["people"])


def _role_category(edge_type: str) -> str:
    if edge_type == "holds_quota":
        return "socio"
    if edge_type in ("audits",):
        return "fiscal"
    if edge_type in ("manages", "secretary", "liquidator"):
        return "admin"
    return "other"


def _still_in(rows: list[Any], key: str) -> set[tuple[str, str]]:
    """Que pares (contraparte, tipo de aresta) ainda têm uma aresta actual.

    A chave é o **id da entidade** e não o NIF. Havia 392 arestas cujo titular
    não tem NIF — estrangeiros e não identificados —, e com a chave no NIF todos
    partilhavam `(None, edge_type)`: bastava um deles continuar na empresa para
    os outros aparecerem como "alterado" em vez de "saiu".
    """
    return {(r[key], r["edge_type"]) for r in rows if r["is_current"]}


def _with_status(
    d: dict[str, Any], still_in: set[tuple[str, str]], key: str
) -> dict[str, Any]:
    """Três estados. `is_current = false` numa quota quer dizer que **aquele
    acto foi substituído**, não que a pessoa saiu — só saiu quem não tem
    nenhuma aresta actual daquele tipo."""
    d["role_category"] = _role_category(d["edge_type"])
    if d["is_current"]:
        d["status"] = "atual"
    elif (d[key], d["edge_type"]) in still_in:
        d["status"] = "alterado"
    else:
        d["status"] = "saiu"
    return d


@router.get("/companies/{company_id}/people")
async def company_people(
    company_id: str,
    session: Annotated[AsyncSession, Depends(get_session)],
    _user: Annotated[CurrentUser, Depends(current_user)],
    include_past: bool = Query(True, description="incluir quem já saiu"),
) -> dict[str, Any]:
    """Sócios, gerência e fiscalização de uma empresa monitorizada.

    Três estados, e a distinção entre os dois últimos importa:

    * **`atual`** — a aresta vem do acto que define a estrutura de hoje;
    * **`alterado`** — a pessoa continua na empresa, mas esta linha é de um acto
      anterior. Tipicamente a quota mudou;
    * **`saiu`** — a pessoa não tem nenhuma aresta actual desse tipo.

    `is_current = false` numa quota significa que **aquele acto foi substituído**,
    e não que a pessoa saiu. Na SEGUNOR, o José Ribeiro tem uma linha de 125.000 €
    de 2019 e outra de 155.000 € de 2026: aumentou a quota, não saiu. Só a Marisa
    saiu de facto, porque não consta da estrutura de 2026.
    """
    clause = "" if include_past else " AND r.is_current"
    rows = (
        await session.execute(
            text(
                f"""
                SELECT h.id::text AS person_id,
                       h.nif AS person_nif, h.name AS person_name, h.kind,
                       r.edge_type, r.role_label AS cargo, r.is_current,
                       sum(r.quota_amount) AS quota_amount,
                       sum(r.quota_share) AS quota_share,
                       min(r.currency) AS currency,
                       count(*) FILTER (WHERE r.edge_type = 'holds_quota') AS quotas,
                       min(r.valid_from) AS valid_from,
                       max(r.valid_to) AS valid_to,
                       max(r.cease_cause) AS cease_cause,
                       max(a.act_date) AS act_date, max(a.act_type) AS act_type,
                       max(lc.id::text) AS linked_company_id,
                       max(lc.legal_name) AS linked_company_name,
                       max(lc.monitoring_type) AS linked_monitoring_type,
                       (SELECT count(DISTINCT o.subject_id) FROM registry_edges o
                         WHERE o.holder_id = h.id AND o.is_current
                           AND o.subject_id <> s.id) AS other_companies_count
                  FROM registry_edges r
                  JOIN registry_entities h ON h.id = r.holder_id
                  JOIN registry_entities s ON s.id = r.subject_id
                  JOIN registry_acts a ON a.id = r.source_act_id
                  JOIN companies c ON c.nif = s.nif
                  LEFT JOIN companies lc ON lc.nif = h.nif
                 WHERE c.id = CAST(:cid AS UUID)
                   AND r.edge_type <> 'spouse_of'
                   {clause}
                 GROUP BY h.id, h.nif, h.name, h.kind, s.id, r.edge_type,
                          r.role_label, r.is_current, r.source_act_id
                 ORDER BY r.is_current DESC, r.edge_type,
                          sum(r.quota_amount) DESC NULLS LAST, h.name
                 LIMIT 200
                """
            ),
            {"cid": company_id},
        )
    ).mappings().all()
    still_in = _still_in(rows, "person_id")
    return {"people": [_with_status(dict(r), still_in, "person_id") for r in rows]}


@router.get("/people/{nif}/companies")
async def person_companies(
    nif: str,
    session: Annotated[AsyncSession, Depends(get_session)],
    _user: Annotated[CurrentUser, Depends(current_user)],
) -> dict[str, Any]:
    """Onde é que esta pessoa aparece — **todas** as entidades do índice, não só
    as monitorizadas. É a pergunta que o grafo existe para responder.

    Os três estados são os mesmos da ficha da empresa, e pela mesma razão: quem
    entra por aqui não pode ler "saiu" onde só houve um aumento de quota.
    """
    norm = "".join(ch for ch in nif if ch.isdigit())
    rows = (
        await session.execute(
            text(
                """
                SELECT s.id::text AS subject_id, s.nif, s.name AS legal_name, s.kind,
                       r.edge_type, r.role_label AS cargo, r.is_current,
                       sum(r.quota_amount) AS quota_amount,
                       sum(r.quota_share) AS quota_share,
                       min(r.currency) AS currency,
                       count(*) FILTER (WHERE r.edge_type = 'holds_quota') AS quotas,
                       max(a.act_date) AS act_date,
                       max(c.id::text) AS company_id,
                       min(c.monitoring_type) AS monitoring_type,
                       bool_or(i.insolvency_open) AS insolvency_open
                  FROM registry_edges r
                  JOIN registry_entities h ON h.id = r.holder_id
                  JOIN registry_entities s ON s.id = r.subject_id
                  JOIN registry_acts a ON a.id = r.source_act_id
                  LEFT JOIN companies c ON c.nif = s.nif
                  LEFT JOIN entity_incident_summary i ON i.nif = s.nif
                 WHERE h.nif = :nif AND r.edge_type <> 'spouse_of'
                 GROUP BY s.id, s.nif, s.name, s.kind, r.edge_type,
                          r.role_label, r.is_current, r.source_act_id
                 ORDER BY r.is_current DESC, sum(r.quota_amount) DESC NULLS LAST
                 LIMIT 100
                """
            ),
            {"nif": norm},
        )
    ).mappings().all()
    name = (
        await session.execute(
            text("SELECT name FROM registry_entities WHERE nif = :nif"), {"nif": norm}
        )
    ).scalar()
    still_in = _still_in(rows, "subject_id")
    return {
        "nif": norm,
        "name": name,
        "companies": [_with_status(dict(r), still_in, "subject_id") for r in rows],
    }
