from fastapi import APIRouter, Depends, HTTPException, Request, Response
from sqlalchemy.orm import Session

from custos_precificacao import atividade, auth, config
from custos_precificacao.api.deps import get_session
from custos_precificacao.api.schemas import (
    LoginIn,
    TrocarSenhaIn,
    UsuarioNovoIn,
    UsuarioOut,
    UsuarioPatchIn,
)
from custos_precificacao.models import Usuario

router = APIRouter(prefix="/auth", tags=["auth"])
router_usuarios = APIRouter(prefix="/usuarios", tags=["usuarios"], dependencies=[Depends(auth.exigir_admin), Depends(atividade.auditar)])


def _saida(usuario: Usuario) -> UsuarioOut:
    return UsuarioOut(id=usuario.id, email=usuario.email, nome=usuario.nome, admin=usuario.admin, ativo=usuario.ativo)


def _definir_cookie(response: Response, usuario: Usuario) -> None:
    response.set_cookie(
        auth.COOKIE,
        auth.criar_token(usuario),
        max_age=auth.VALIDADE_SESSAO_S,
        httponly=True,
        secure=config.cookie_seguro(),
        samesite="lax",
        path="/",
    )


@router.post("/login", response_model=UsuarioOut, dependencies=[Depends(auth.exigir_cabecalho_csrf)])
def login(corpo: LoginIn, response: Response, session: Session = Depends(get_session)) -> UsuarioOut:
    usuario = auth.autenticar(session, corpo.email, corpo.senha)
    if usuario is None:
        raise HTTPException(status_code=401, detail="E-mail ou senha inválidos")
    _definir_cookie(response, usuario)
    atividade.registrar(session, usuario.email, "POST", "entrou", "Conta", "/auth/login")
    return _saida(usuario)


@router.post("/logout", status_code=204, dependencies=[Depends(auth.exigir_cabecalho_csrf)])
def logout(response: Response) -> None:
    response.delete_cookie(auth.COOKIE, path="/")


@router.get("/eu", response_model=UsuarioOut)
def eu(atual: auth.UsuarioAtual = Depends(auth.exigir_usuario)) -> UsuarioOut:
    return UsuarioOut(
        id=atual.id, email=atual.email, nome=atual.nome, admin=atual.admin, auth_desligada=config.auth_desligada()
    )


@router.post("/senha", response_model=UsuarioOut)
def trocar_senha(
    corpo: TrocarSenhaIn,
    response: Response,
    atual: auth.UsuarioAtual = Depends(auth.exigir_usuario),
    session: Session = Depends(get_session),
) -> UsuarioOut:
    usuario = session.get(Usuario, atual.id)
    if usuario is None:
        raise HTTPException(status_code=400, detail="Sem login ativo (modo de desenvolvimento)")
    if not auth.verificar_senha(corpo.senha_atual, usuario.senha_hash):
        raise HTTPException(status_code=422, detail="A senha atual não confere")
    try:
        auth.validar_senha(corpo.senha_nova)
    except ValueError as exc:
        raise HTTPException(status_code=422, detail=str(exc)) from exc
    usuario.senha_hash = auth.hash_senha(corpo.senha_nova)
    session.commit()
    _definir_cookie(response, usuario)  # a sessão antiga deixa de valer; esta continua
    atividade.registrar(session, usuario.email, "POST", "trocou a própria senha", "Conta", "/auth/senha")
    return _saida(usuario)


def _admins_ativos(session: Session) -> int:
    return session.query(Usuario).filter_by(admin=True, ativo=True).count()


@router_usuarios.get("", response_model=list[UsuarioOut])
def listar(session: Session = Depends(get_session)) -> list[UsuarioOut]:
    return [_saida(u) for u in session.query(Usuario).order_by(Usuario.nome).all()]


@router_usuarios.post("", response_model=UsuarioOut, status_code=201)
def criar(corpo: UsuarioNovoIn, session: Session = Depends(get_session)) -> UsuarioOut:
    try:
        usuario = auth.criar_usuario(session, corpo.email, corpo.nome, corpo.senha, admin=corpo.admin)
    except ValueError as exc:
        raise HTTPException(status_code=422, detail=str(exc)) from exc
    session.commit()
    return _saida(usuario)


@router_usuarios.patch("/{usuario_id}", response_model=UsuarioOut)
def alterar(usuario_id: int, corpo: UsuarioPatchIn, session: Session = Depends(get_session)) -> UsuarioOut:
    usuario = session.get(Usuario, usuario_id)
    if usuario is None:
        raise HTTPException(status_code=404, detail="Usuário não encontrado")
    enviados = corpo.model_fields_set
    if "nome" in enviados:
        nome = (corpo.nome or "").strip()
        if not nome:
            raise HTTPException(status_code=422, detail="O nome é obrigatório")
        usuario.nome = nome
    if "senha" in enviados and corpo.senha is not None:
        try:
            auth.validar_senha(corpo.senha)
        except ValueError as exc:
            raise HTTPException(status_code=422, detail=str(exc)) from exc
        usuario.senha_hash = auth.hash_senha(corpo.senha)
    if "admin" in enviados and corpo.admin is not None:
        usuario.admin = corpo.admin
    if "ativo" in enviados and corpo.ativo is not None:
        usuario.ativo = corpo.ativo
    session.flush()
    if _admins_ativos(session) == 0:
        session.rollback()
        raise HTTPException(status_code=409, detail="Precisa restar pelo menos um administrador ativo")
    session.commit()
    return _saida(usuario)
