from typing import Annotated
from urllib.parse import urlencode

from fastapi import APIRouter, Depends, Form, HTTPException, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from starlette.responses import RedirectResponse
from starlette.status import HTTP_303_SEE_OTHER, HTTP_500_INTERNAL_SERVER_ERROR

from app.application.users.passwords import (
    PasswordPolicyError,
    PasswordServiceError,
    reset_user_password,
    validate_new_password,
)
from app.auth import hash_password, require_role
from app.database import db
from app.models import UserInDB
from app.routes.shell_navigation import build_shell_context


router = APIRouter()
templates = Jinja2Templates(directory="web")
CurrentAdmin = Annotated[UserInDB, Depends(require_role("admin"))]


async def _render_panel_admin(request: Request, user, **context):
    usuarios = await db.users.find().to_list(length=None)
    base_context = {
        "request": request,
        "user": user,
        "usuarios": usuarios,
        **build_shell_context(request=request, user=user),
    }
    base_context.update(context)
    return templates.TemplateResponse(request, "panel-admin.html", base_context)

@router.get("/panel-admin", response_class=HTMLResponse)
async def panel_admin(
    request: Request,
    user: CurrentAdmin,
    crear_exito: str | None = None,
    clave_exito: str | None = None,
    eliminar_exito: str | None = None
):
    return await _render_panel_admin(
        request,
        user,
        crear_exito=crear_exito,
        clave_exito=clave_exito,
        eliminar_exito=eliminar_exito,
    )


@router.post("/admin/usuarios/crear")
async def crear_usuario(
    request: Request,
    username: Annotated[str, Form(...)],
    password: Annotated[str, Form(...)],
    role: Annotated[str, Form(...)],
    user: CurrentAdmin,
):
    if role not in ["admin", "medico"]:
        return await _render_panel_admin(
            request,
            user,
            crear_error="Rol no válido. Solo se permite 'admin' o 'medico'.",
        )

    existing = await db.users.find_one({"username": username})
    if existing:
        return await _render_panel_admin(
            request,
            user,
            crear_error="El usuario ya existe.",
        )

    await db.users.insert_one({
        "username": username,
        "password": hash_password(password),
        "role": role
    })

    params = urlencode({"crear_exito": f"Usuario '{username}' creado exitosamente."})
    return RedirectResponse(f"/panel-admin?{params}", status_code=HTTP_303_SEE_OTHER)



@router.post("/admin/usuarios/cambiar-password")
async def cambiar_password(
    request: Request,
    username: Annotated[str, Form(...)],
    new_password: Annotated[str, Form(...)],
    user: CurrentAdmin,
):
    try:
        validate_new_password(new_password)
        await reset_user_password(user.username, username, new_password)
    except PasswordPolicyError as exc:
        return await _render_panel_admin(
            request,
            user,
            clave_error=exc.user_message,
        )
    except PasswordServiceError as exc:
        raise HTTPException(
            status_code=HTTP_500_INTERNAL_SERVER_ERROR,
            detail="No se pudo actualizar la contraseña en este momento.",
        ) from exc

    params = urlencode({"clave_exito": f"Contraseña de '{username}' actualizada."})
    return RedirectResponse(f"/panel-admin?{params}", status_code=HTTP_303_SEE_OTHER)


@router.post("/admin/usuarios/eliminar")
async def eliminar_usuario(
    request: Request,
    username: Annotated[str, Form(...)],
    user: CurrentAdmin,
):
    usuario = await db.users.find_one({"username": username})
    if not usuario:
        return await _render_panel_admin(
            request,
            user,
            eliminar_error="Usuario no encontrado.",
        )

    if usuario["username"] == "admin":
        return await _render_panel_admin(
            request,
            user,
            eliminar_error="No se puede eliminar el usuario admin principal.",
        )

    await db.users.delete_one({"username": username})
    params = urlencode({"eliminar_exito": f"Usuario '{username}' eliminado correctamente."})
    return RedirectResponse(f"/panel-admin?{params}", status_code=HTTP_303_SEE_OTHER)
