"""Log geral de atividade: quem fez qual escrita pela API e quando (não guarda valores — o antes/depois dos
custos fica no histórico de versões, §4.3).

`auditar` é uma dependência com `yield` aplicada junto de `exigir_usuario`: depois do endpoint, **só se ele
terminou sem erro** e o método grava algo, registra a atividade. Usa uma sessão própria sobre a mesma conexão
para não mexer na transação do endpoint.
"""

from collections.abc import Generator

from fastapi import Depends, Request
from sqlalchemy.orm import Session

from custos_precificacao.api.deps import get_session
from custos_precificacao.models import RegistroAtividade

METODOS_SEGUROS = {"GET", "HEAD", "OPTIONS"}

RECURSOS = {
    "planejamentos": "Planejamento",
    "premissas": "Premissa",
    "cenarios": "Cenário",
    "elementos": "Elemento de custo",
    "plano-de-contas": "Plano de Contas",
    "centros-de-custo": "Centro de Custo",
    "direcionadores": "Direcionador",
    "despesas": "Despesa",
    "precificacao": "Precificação",
    "centros": "Centro de Resultados",
    "insumos": "Insumo",
    "produtos": "Produto",
    "usuarios": "Usuário",
    "importacao": "Importação",
    "auth": "Conta",
}

_ACOES_POR_METODO = {"POST": "criou", "PATCH": "alterou", "PUT": "definiu", "DELETE": "removeu"}
_ACOES_ESPECIAIS = {  # sufixo da rota (POST) → ação
    "/custeio": "rodou o custeio",
    "/versoes": "congelou uma versão",
    "/previa": "validou uma planilha",
    "/confirmar": "importou uma planilha",
    "/duplicar": "duplicou",
    "/copiar": "copiou",
}


def descrever(metodo: str, rota: str) -> tuple[str, str]:
    """(ação, recurso) em português a partir do método e do padrão da rota (ex.: '/produtos/{codigo}/duplicar')."""
    acao = _ACOES_POR_METODO.get(metodo, metodo.lower())
    if metodo == "POST":
        acao = next((texto for sufixo, texto in _ACOES_ESPECIAIS.items() if rota.rstrip("/").endswith(sufixo)), acao)
    primeiro = rota.strip("/").split("/", 1)[0]
    return acao, RECURSOS.get(primeiro, primeiro.capitalize() or "—")


def registrar(session: Session, usuario: str | None, metodo: str, acao: str, recurso: str, caminho: str) -> None:
    with Session(bind=session.get_bind()) as sessao_log:
        sessao_log.add(RegistroAtividade(
            usuario=usuario or "—", metodo=metodo, acao=acao, recurso=recurso, caminho=caminho[:300]
        ))
        sessao_log.commit()


def auditar(request: Request, session: Session = Depends(get_session)) -> Generator[None, None, None]:
    yield  # se o endpoint levantou erro, a exceção é lançada aqui e nada é registrado
    if request.method in METODOS_SEGUROS:
        return
    rota = getattr(request.scope.get("route"), "path", None) or request.url.path
    caminho = request.url.path
    prefixo = request.scope.get("root_path", "")
    if prefixo and caminho.startswith(prefixo):
        caminho = caminho[len(prefixo):]
    acao, recurso = descrever(request.method, rota)
    registrar(session, session.info.get("usuario"), request.method, acao, recurso, caminho)
