from fastapi import APIRouter, Request, Depends, HTTPException, Form, Body, Query, Path
from fastapi.responses import HTMLResponse, RedirectResponse, JSONResponse
from sqlalchemy.orm import Session
from typing import Optional, List, Dict, Any
import json
from pydantic import BaseModel, Field

from app.database import templates
from app.models.base import get_db
from app.auth.deps import current_user, require_admin, require_csrf
from app.models import auth_crud, notification_crud as notif_crud, reservation_crud as res_crud, estadisticas_crud
from app.models.user import UserFinance

router = APIRouter()

# --- Schemas ---
class CategoryUpdate(BaseModel):
    category: Optional[str] = None

class RoleUpdate(BaseModel):
    role: str

class MatriculaPayload(BaseModel):
    matricula_cents: int = Field(ge=0)
    mode: Optional[str] = "add"

ALLOWED_ROLES = {"alumno", "docente", "admin"}
ALLOWED_CATEGORIES = {
    "prebenjamin", "benjamin", "alevines", "infantiles", "cadetes", "juvenil", "senior"
}

# --- Routes ---

@router.get("/gestion-usuarios", response_class=HTMLResponse, name="gestion_usuarios")
async def gestion_usuarios(
    request: Request, 
    db: Session = Depends(get_db),
    page: int = Query(1, ge=1),
    search: Optional[str] = Query(None)
):
    me = current_user(request)
    if not me:
        return RedirectResponse(url=request.url_for("login"), status_code=303)
    if isinstance(me, dict):
        me = dict(me)
    role = str((me.get("role") if isinstance(me, dict) else me["role"]) or "").lower()
    if role != "admin":
        return RedirectResponse(url=request.url_for("dashboard"), status_code=303)

    users_data = auth_crud.get_users_for_management(db, page=page, per_page=20, search=search)
    
    # Get statistics for charts
    stats = estadisticas_crud.obtener_estadisticas_agregadas(db)

    return templates.TemplateResponse(
        "gestion-usuarios.html",
        {
            "request": request,
            "user": me,
            "users": users_data["users"],
            "pagination": users_data["pagination"],
            "search": search,
            "stats": stats
        }
    )

@router.get("/api/estadisticas-jugadores", name="api_estadisticas_jugadores")
async def api_estadisticas_jugadores(request: Request, db: Session = Depends(get_db)):
    """API endpoint to get aggregated player statistics JSON"""
    me = current_user(request)
    if not me:
        raise HTTPException(status_code=403, detail="Acceso denegado")
        
    role = str((me.get("role") if isinstance(me, dict) else me["role"]) or "").lower()
    if role != "admin":
        raise HTTPException(status_code=403, detail="Acceso denegado")
        
    return estadisticas_crud.obtener_estadisticas_agregadas(db)

@router.post("/admin/users/delete", name="admin_delete_user")
async def admin_delete_user(
    request: Request,
    user_id: int = Form(...),
    next_url: str | None = Form(None),
    db: Session = Depends(get_db)
):
    me = current_user(request)
    if not me:
        return RedirectResponse(url="/login", status_code=303)
    if not auth_crud.is_admin(me):
        raise HTTPException(status_code=403, detail="Solo un admin puede eliminar usuarios.")
    target = auth_crud.get_user_by_id(db, user_id)
    if not target:
        raise HTTPException(status_code=404, detail="Usuario no encontrado.")
    if int(me["id"]) == int(target.id):
        raise HTTPException(status_code=400, detail="No puedes eliminar tu propia cuenta.")
    
    # Delete related data using SQLAlchemy
    try:
        notif_crud.delete_notifications_by_user(db, user_id)
    except Exception:
        pass
    
    # Use ORM to delete reservations and student entries
    res_crud.delete_reservations_by_user(db, user_id)
    res_crud.delete_student_entries_by_user(db, user_id)
    
    # Delete user
    auth_crud.delete_user(db, user_id)
    
    if next_url:
        # Simple validation could be added here
        return RedirectResponse(url=next_url, status_code=303)

    return RedirectResponse(url="/reservas", status_code=303)


@router.get("/api/users", response_class=JSONResponse, name="api_users")
async def api_users(
    request: Request,
    roles: Optional[str] = Query(None),
    role: Optional[str] = Query(None),
    db: Session = Depends(get_db)
):
    user = current_user(request)
    if not user:
        return RedirectResponse(url="/login", status_code=303)
    
    roles_list: List[str] = []
    if roles:
        roles_list = [r.strip().lower() for r in roles.split(",") if r.strip()]
    elif role:
        roles_list = [role.strip().lower()]
    
    users = auth_crud.get_all_users(db, roles=roles_list)
    return [auth_crud.user_to_dict(u) for u in users]


@router.post("/api/users/{user_id}/role", name="api_users_set_role")
async def api_users_set_role(user_id: int, payload: RoleUpdate, request: Request, db: Session = Depends(get_db)):
    require_admin(request)
    target = auth_crud.get_user_by_id(db, user_id)
    if not target:
        raise HTTPException(status_code=404, detail="Usuario no encontrado")
    new_role = (payload.role or "").strip().lower()
    if new_role not in ALLOWED_ROLES:
        raise HTTPException(status_code=400, detail="Rol inválido")
    current_role = str(target.role).lower()
    was_admin = current_role == "admin"
    is_demoting_admin = was_admin and new_role != "admin"
    if current_role == new_role:
        return {"ok": True, "user_id": user_id, "role": new_role}

    # If demoting admin, check if it's the last one
    if is_demoting_admin:
        admin_count = auth_crud.count_admins(db)
        if admin_count <= 1:
             raise HTTPException(status_code=400, detail="No puedes quitar al último admin.")
    
    auth_crud.set_user_role(db, user_id, new_role)
    
    return {"ok": True, "user_id": user_id, "role": new_role}


@router.patch("/api/users/{user_id}/role", response_model=dict)
async def api_users_update_role(request: Request, user_id: int = Path(...), payload: dict = Body(...), db: Session = Depends(get_db)):
    require_admin(request)
    target_role = (payload.get("role") or "").strip().lower()
    if target_role not in ALLOWED_ROLES:
        raise HTTPException(status_code=400, detail="Rol inválido")

    target = auth_crud.get_user_by_id(db, user_id)
    if not target:
        raise HTTPException(status_code=404, detail="Usuario no encontrado")
    auth_crud.set_user_role(db, user_id, target_role)
    updated = auth_crud.get_user_by_id(db, user_id)

    return {"ok": True, "user": auth_crud.user_to_dict(updated)}


@router.post("/api/users/{user_id}/category", name="api_users_set_category")
async def api_users_set_category(user_id: int, payload: CategoryUpdate, request: Request, db: Session = Depends(get_db)):
    require_admin(request)
    target = auth_crud.get_user_by_id(db, user_id)
    if not target:
        raise HTTPException(status_code=404, detail="Usuario no encontrado")
    cat = (payload.category or "").strip().lower()
    if cat and cat not in ALLOWED_CATEGORIES:
        raise HTTPException(status_code=400, detail="Categoría no válida")
    
    auth_crud.update_user_sports(db, user_id, category=cat if cat else None)
    
    return {"ok": True, "user_id": user_id, "category": cat}


@router.patch("/api/users/{user_id}/matricula", response_model=dict)
async def api_users_update_matricula(request: Request, user_id: int, payload: dict = Body(...), db: Session = Depends(get_db)):
    require_admin(request)

    mode = str(payload.get("mode", "set")).lower()
    if mode not in {"set", "add"}:
        raise HTTPException(status_code=400, detail="mode debe ser 'set' o 'add'.")

    has_eur = "matricula_eur" in payload
    has_cents = "matricula_cents" in payload

    if not has_eur and not has_cents:
        raise HTTPException(status_code=400, detail="Falta matricula_eur.")

    if has_eur:
        try:
            euros = round(float(str(payload["matricula_eur"]).replace(",", ".")), 2)
        except Exception:
            raise HTTPException(status_code=400, detail="matricula_eur invalido.")
    else:
        try:
            cents = int(payload["matricula_cents"])
        except Exception:
            raise HTTPException(status_code=400, detail="matricula_cents inválido.")
        euros = round(cents / 100.0, 2)

    finance = db.query(UserFinance).filter(UserFinance.user_id == user_id).first()
    if not finance:
        finance = UserFinance(user_id=user_id, matricula_eur=0.0)
        db.add(finance)
        db.commit()
        db.refresh(finance)
    current = float(finance.matricula_eur or 0.0)

    if mode == "set":
        new_val = euros
    else:
        new_val = round(current + euros, 2)

    finance.matricula_eur = new_val
    db.commit()

    return {
        "ok": True,
        "matricula_eur": new_val,
        "matricula_cents": int(round(new_val * 100))
    }

@router.delete("/api/users/{user_id}", response_model=dict)
async def api_users_delete(request: Request, user_id: int, db: Session = Depends(get_db)):
    require_admin(request)
    
    # This seems redundant with admin_delete_user but following the pattern
    # Assuming this is the JSON version of the delete endpoint
    target = auth_crud.get_user_by_id(db, user_id)
    if not target:
        raise HTTPException(status_code=404, detail="Usuario no encontrado.")
    
    me = current_user(request)
    if int(me["id"]) == int(target.id):
         raise HTTPException(status_code=400, detail="No puedes eliminar tu propia cuenta.")

    # Shared logic with admin_delete_user, could be extracted to service
    try:
        notif_crud.delete_notifications_by_user(db, user_id)
    except Exception:
        pass
    
    res_crud.delete_reservations_by_user(db, user_id)
    res_crud.delete_student_entries_by_user(db, user_id)
    auth_crud.delete_user(db, user_id)
    
    return {"ok": True, "message": "Usuario eliminado correctamente"}


@router.post("/api/users/{user_id}/mvp90", response_model=dict, name="api_users_toggle_mvp90")
async def api_users_toggle_mvp90(request: Request, user_id: int, payload: dict = Body(...), db: Session = Depends(get_db)):
    """
    Activa o desactiva el flag MVP-90 para un usuario.
    Solo accesible para admin (mismo patrón que el resto de /api/users/*).
    """
    require_admin(request)
    target = auth_crud.get_user_by_id(db, user_id)
    if not target:
        raise HTTPException(status_code=404, detail="Usuario no encontrado")

    value = bool(payload.get("is_mvp90", False))

    # Persistir el cambio en base de datos
    updated = auth_crud.set_user_mvp90(db, user_id, value)
    if not updated:
        raise HTTPException(status_code=500, detail="No se pudo actualizar MVP-90")

    # Volver a leer el usuario para devolver el estado real
    refreshed = auth_crud.get_user_by_id(db, user_id)
    is_mvp90 = bool(refreshed.is_mvp90) if refreshed is not None else value

    return {
        "ok": True,
        "user_id": user_id,
        "is_mvp90": is_mvp90,
    }
@router.post("/api/users/{user_id}/password", response_model=dict, name="api_users_reset_password")
async def api_users_reset_password(request: Request, user_id: int, payload: dict = Body(...), db: Session = Depends(get_db)):
    require_admin(request)
    target = auth_crud.get_user_by_id(db, user_id)
    if not target:
        raise HTTPException(status_code=404, detail="Usuario no encontrado")
        
    new_password = str(payload.get("password") or "").strip()
    if len(new_password) < 6:
        raise HTTPException(status_code=400, detail="La contraseña debe tener al menos 6 caracteres")
        
    auth_crud.set_user_password(db, user_id, new_password)
    return {"ok": True, "message": "Contraseña actualizada correctamente"}
