from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session

from custos_precificacao import cadastro
from custos_precificacao.api.deps import get_session
from custos_precificacao.api import resultados
from custos_precificacao.api.schemas import (
    LinhaComercialOut,
    PremissaIn,
    PremissaOut,
    PremissaPatch,
    ResumoOut,
)
from custos_precificacao.models import CenarioPrecificacao, Planejamento, Premissa, Produto

router = APIRouter(tags=["premissas"])

TIPOS_VALIDOS = ("comercial", "operacional", "despesas", "financeiro")


def _validar_tipo(tipo: str) -> None:
    if tipo not in TIPOS_VALIDOS:
        raise HTTPException(status_code=422, detail=f"tipo inválido {tipo!r}; use um de {', '.join(TIPOS_VALIDOS)}")


def _obter_planejamento(session: Session, codigo: str) -> Planejamento:
    plano = session.query(Planejamento).filter_by(codigo=codigo).one_or_none()
    if plano is None:
        raise HTTPException(status_code=404, detail=f"Planejamento {codigo!r} não encontrado")
    return plano


def _obter_premissa(session: Session, codigo: str) -> Premissa:
    premissa = session.query(Premissa).filter_by(codigo=codigo).one_or_none()
    if premissa is None:
        raise HTTPException(status_code=404, detail=f"Premissa {codigo!r} não encontrada")
    return premissa


def _out(p: Premissa) -> PremissaOut:
    return PremissaOut(codigo=p.codigo, tipo=p.tipo, texto=p.texto)


@router.get("/planejamentos/{codigo}/resumo", response_model=ResumoOut)
def obter_resumo(codigo: str, session: Session = Depends(get_session)) -> ResumoOut:
    plano = _obter_planejamento(session, codigo)
    premissas = session.query(Premissa).filter_by(planejamento_id=plano.id).order_by(Premissa.codigo).all()

    resumo = ResumoOut(
        planejamento_codigo=plano.codigo,
        planejamento_nome=plano.nome,
        premissas=[_out(p) for p in premissas],
        comercial=[],
    )

    # Dados calculados são opcionais: se faltar Meta de Lucro/custeio, as Premissas continuam editáveis.
    try:
        calculado, dre = resultados.calcular(session)
        resumo.total = resultados.resultado_out(resultados.total_consolidado(calculado))
        resumo.centros = resultados.centros_out(calculado, dre)
        resumo.comercial = [
            LinhaComercialOut(
                cenario_codigo=codigo, cenario_nome=calculado.cenarios[codigo].nome, produto_codigo=l.produto.codigo, produto_nome=l.produto.nome,
                quantidade=l.quantidade, preco=l.preco_definido, receita=l.ao_definido.preco * l.quantidade,
            )
            for codigo, centro in calculado.centros.items()
            for l in centro.linhas
            if l.quantidade
        ]
    except ValueError as exc:
        resumo.aviso = str(exc)
    return resumo


@router.post("/planejamentos/{codigo}/premissas", response_model=PremissaOut, status_code=201)
def criar_premissa(codigo: str, corpo: PremissaIn, session: Session = Depends(get_session)) -> PremissaOut:
    plano = _obter_planejamento(session, codigo)
    _validar_tipo(corpo.tipo)
    if not corpo.codigo.strip() or not corpo.texto.strip():
        raise HTTPException(status_code=422, detail="codigo e texto são obrigatórios")
    try:
        criada = cadastro.criar(
            session,
            cadastro.resolver_entidade("premissa"),
            {"codigo": corpo.codigo.strip(), "planejamento_id": str(plano.id), "tipo": corpo.tipo, "texto": corpo.texto},
        )
    except IntegrityError as exc:
        session.rollback()
        raise HTTPException(status_code=409, detail=f"Já existe uma Premissa com código {corpo.codigo!r}") from exc
    return _out(criada)


@router.patch("/premissas/{codigo}", response_model=PremissaOut)
def atualizar_premissa(codigo: str, corpo: PremissaPatch, session: Session = Depends(get_session)) -> PremissaOut:
    _obter_premissa(session, codigo)
    valores: dict[str, str] = {}
    if corpo.tipo is not None:
        _validar_tipo(corpo.tipo)
        valores["tipo"] = corpo.tipo
    if corpo.texto is not None:
        if not corpo.texto.strip():
            raise HTTPException(status_code=422, detail="texto não pode ficar vazio")
        valores["texto"] = corpo.texto
    atualizada = cadastro.atualizar(session, cadastro.resolver_entidade("premissa"), codigo, valores)
    return _out(atualizada)


@router.delete("/premissas/{codigo}", status_code=204)
def excluir_premissa(codigo: str, session: Session = Depends(get_session)) -> None:
    _obter_premissa(session, codigo)
    cadastro.deletar(session, cadastro.resolver_entidade("premissa"), codigo)
