"""Catálogo de endpoints do Manual para IA, gerado a partir das rotas reais do FastAPI.

O manual (`manual/manual_ia.md`) guarda o bloco entre `INICIO`/`FIM` gerado por aqui; `scripts/gerar_manual_ia.py` o atualiza e
`tests/test_manual.py` falha quando o bloco fica desatualizado (rota nova, removida ou alterada).
"""

from typing import get_args

from fastapi.routing import APIRoute
from pydantic import BaseModel

from custos_precificacao.api.main import app

INICIO = "<!-- CATALOGO:INICIO (gerado por scripts/gerar_manual_ia.py; não edite à mão) -->"
FIM = "<!-- CATALOGO:FIM -->"

_METODOS = ("GET", "POST", "PUT", "PATCH", "DELETE")


def _primeira_linha(texto: str | None) -> str:
    if not texto:
        return ""
    linhas = [l.strip() for l in texto.strip().splitlines() if l.strip()]
    return linhas[0] if linhas else ""


def _modelo_do_corpo(tipo) -> type[BaseModel] | None:
    if isinstance(tipo, type) and issubclass(tipo, BaseModel):
        return tipo
    for arg in get_args(tipo):  # corpo opcional: `Modelo | None`
        if isinstance(arg, type) and issubclass(arg, BaseModel):
            return arg
    return None


def _campos_do_corpo(rota: APIRoute) -> str:
    campo = rota.body_field
    if campo is None:
        return ""
    modelo = _modelo_do_corpo(campo.field_info.annotation)
    if modelo is None:
        return f"corpo: {campo.name}"
    nomes = [nome if info.is_required() else f"{nome}?" for nome, info in modelo.model_fields.items()]
    return "corpo JSON: " + ", ".join(nomes)


def _parametros(rota: APIRoute) -> str:
    partes = []
    consulta = [p.name if p.required else f"{p.name}?" for p in rota.dependant.query_params]
    if consulta:
        partes.append("consulta: " + ", ".join(consulta))
    corpo = _campos_do_corpo(rota)
    if corpo:
        partes.append(corpo)
    return "; ".join(partes)


def _celula(texto: str) -> str:
    return texto.replace("|", "\\|").replace("\n", " ")


def _rotas() -> list[tuple[str, APIRoute]]:
    saida = []
    for rota in app.routes:
        if not isinstance(rota, APIRoute):
            continue
        tag = (rota.tags or ["outros"])[0]
        saida.append((tag, rota))
    return saida


def caminho_da_rota(rota: APIRoute) -> str:
    return rota.path


def gerar() -> str:
    """Bloco markdown (com marcadores) com uma tabela por grupo de rotas."""
    por_tag: dict[str, list[APIRoute]] = {}
    for tag, rota in _rotas():
        por_tag.setdefault(tag, []).append(rota)
    linhas = [INICIO, ""]
    for tag in sorted(por_tag):
        linhas += [f"### Grupo `{tag}`", "", "| Método | Caminho | Para que serve | Parâmetros |", "| --- | --- | --- | --- |"]
        for rota in sorted(por_tag[tag], key=lambda r: (r.path, min(_METODOS.index(m) for m in r.methods if m in _METODOS))):
            for metodo in sorted(rota.methods & set(_METODOS), key=_METODOS.index):
                explicacao = _primeira_linha(rota.description) or _primeira_linha(rota.endpoint.__doc__) or rota.name.replace("_", " ")
                linhas.append(f"| {metodo} | `{rota.path}` | {_celula(explicacao)} | {_celula(_parametros(rota))} |")
        linhas.append("")
    linhas.append(FIM)
    return "\n".join(linhas)


def aplicar(texto_manual: str) -> str:
    """Devolve o manual com o bloco do catálogo regenerado (os marcadores precisam existir)."""
    a = texto_manual.index(INICIO)
    b = texto_manual.index(FIM) + len(FIM)
    return texto_manual[:a] + gerar() + texto_manual[b:]
