import json
import math
from datetime import datetime, timedelta, timezone
from pathlib import Path
from typing import Optional

import pandas as pd
from fastapi import APIRouter, Depends, Form, Request
from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.templating import Jinja2Templates
from sqlalchemy.orm import Session, joinedload

from ..core.config import get_settings
from ..core.database import InternalSession, PlatformSession
from ..core.security import create_access_token, decode_access_token, verify_password
from ..db_models.internal import LogAdmin, SesionAdmin, UsuarioAdmin
from ..db_models.platform import (
    Actividad, Alumno, Curso, Entrega, Matricula, Profesor, Progreso, Rol, Usuario,
)

router = APIRouter(tags=["Web Panel"])
settings = get_settings()

templates = Jinja2Templates(directory=Path(__file__).parent.parent / "templates")

MODELS_DIR  = Path(settings.models_base_path)
DATASETS_DIR = MODELS_DIR / "datasets"
STATS_DIR    = MODELS_DIR / "statistics"
PREDS_DIR    = MODELS_DIR / "predictions"
FIGURES_DIR  = STATS_DIR / "iframe_figures"

STAT_DASHBOARDS = {
    "attendance":          "Asistencia",
    "demographics":        "Demografía",
    "school_group":        "Grupo Escolar",
    "student_performance": "Rendimiento Estudiantil",
    "subject_performance": "Rendimiento por Asignatura",
    "support":             "Soporte",
    "term_progression":    "Progresión Trimestral",
}
ML_DASHBOARDS = {
    "avg_final":      "Promedio Final",
    "grade_tendency": "Tendencia de Notas",
    "math_final":     "Notas Matemáticas",
    "math_pass":      "Aprobado Matemáticas",
    "models_overview": "Vista General Modelos",
    "support_level":  "Nivel de Soporte",
}
NLP_DATASETS = {
    "career_recommendation": "Recomendación de Carrera",
    "risk_alert":            "Alertas de Riesgo",
    "computer_science":      "Predicción Computación",
    "technical_drawing":     "Predicción Dibujo Técnico",
}
ML_PRED_DATASETS = {
    "math_risk":    "Riesgo Matemáticas",
    "support_level": "Nivel de Soporte",
}
ALL_PRED_DATASETS = {**NLP_DATASETS, **ML_PRED_DATASETS}

MODEL_CONFIGS: dict = {
    "avg-final": {
        "name": "M1 — Promedio Final (Regresión Lineal)",
        "fields": [
            {"name": "age",                      "label": "Edad",              "type": "number", "min": 11,  "max": 25,  "default": 15,  "step": 1},
            {"name": "gender_encoded",           "label": "Género",            "type": "select", "options": {"0": "Femenino (0)", "1": "Masculino (1)"}},
            {"name": "course_encoded",           "label": "Curso",             "type": "select", "options": {"0":"Primary 6","1":"Secondary 1","2":"Secondary 2","3":"Secondary 3","4":"Secondary 4","5":"Secondary 5","6":"Secondary 6"}},
            {"name": "attendance_absences_total","label": "Total Ausencias",   "type": "number", "min": 0,   "max": 200, "default": 5,   "step": 1},
            {"name": "support_level_num",        "label": "Nivel Apoyo",       "type": "select", "options": {"0":"Low","1":"Medium","2":"High"}},
            {"name": "total_subjects",           "label": "Nº Asignaturas",    "type": "number", "min": 1,   "max": 20,  "default": 16,  "step": 1},
            {"name": "term1_avg",                "label": "Promedio Trim. 1",  "type": "number", "min": 0,   "max": 100, "default": 60,  "step": 0.1},
        ],
    },
    "math-final": {
        "name": "M4 — Nota Final Matemáticas (Regresión Lineal)",
        "fields": [
            {"name": "Mathematics_term1",        "label": "Matemáticas T1",    "type": "number", "min": 0, "max": 100, "default": 60, "step": 0.1},
            {"name": "Mathematics_term2",        "label": "Matemáticas T2",    "type": "number", "min": 0, "max": 100, "default": 60, "step": 0.1},
            {"name": "Mathematics_term3",        "label": "Matemáticas T3",    "type": "number", "min": 0, "max": 100, "default": 60, "step": 0.1},
            {"name": "attendance_absences_total","label": "Total Ausencias",   "type": "number", "min": 0, "max": 200, "default": 5,  "step": 1},
        ],
    },
    "grade-tendency": {
        "name": "M2 — Tendencia de Notas (Regresión Logística)",
        "fields": [
            {"name": "term1_avg",                "label": "Promedio T1",       "type": "number", "min": 0, "max": 100, "default": 60, "step": 0.1},
            {"name": "term2_avg",                "label": "Promedio T2",       "type": "number", "min": 0, "max": 100, "default": 62, "step": 0.1},
            {"name": "term3_avg",                "label": "Promedio T3",       "type": "number", "min": 0, "max": 100, "default": 65, "step": 0.1},
            {"name": "attendance_absences_total","label": "Total Ausencias",   "type": "number", "min": 0, "max": 200, "default": 5,  "step": 1},
            {"name": "support_level_num",        "label": "Nivel Apoyo",       "type": "select", "options": {"0":"Low","1":"Medium","2":"High"}},
            {"name": "age",                      "label": "Edad",              "type": "number", "min": 11, "max": 25, "default": 15, "step": 1},
            {"name": "gender_encoded",           "label": "Género",            "type": "select", "options": {"0":"Femenino (0)","1":"Masculino (1)"}},
        ],
    },
    "math-pass": {
        "name": "M3 — Aprobado/Suspenso Matemáticas (Logística)",
        "fields": [
            {"name": "Mathematics_term1",        "label": "Matemáticas T1",    "type": "number", "min": 0, "max": 100, "default": 60, "step": 0.1},
            {"name": "Mathematics_term2",        "label": "Matemáticas T2",    "type": "number", "min": 0, "max": 100, "default": 60, "step": 0.1},
            {"name": "Mathematics_term3",        "label": "Matemáticas T3",    "type": "number", "min": 0, "max": 100, "default": 60, "step": 0.1},
            {"name": "attendance_absences_total","label": "Total Ausencias",   "type": "number", "min": 0, "max": 200, "default": 5,  "step": 1},
            {"name": "support_level_num",        "label": "Nivel Apoyo",       "type": "select", "options": {"0":"Low","1":"Medium","2":"High"}},
        ],
    },
    "support-level": {
        "name": "M5 — Nivel de Soporte Necesario (Multinomial)",
        "fields": [
            {"name": "term1_avg",                "label": "Promedio T1",       "type": "number", "min": 0, "max": 100, "default": 60, "step": 0.1},
            {"name": "attendance_absences_total","label": "Total Ausencias",   "type": "number", "min": 0, "max": 200, "default": 5,  "step": 1},
            {"name": "failed_subjects",          "label": "Asig. Suspendidas", "type": "number", "min": 0, "max": 20,  "default": 0,  "step": 1},
            {"name": "grade_tendency_encoded",   "label": "Tendencia",         "type": "select", "options": {"0":"Downtrend/Stable","1":"Uptrend"}},
            {"name": "age",                      "label": "Edad",              "type": "number", "min": 11, "max": 25, "default": 15, "step": 1},
            {"name": "gender_encoded",           "label": "Género",            "type": "select", "options": {"0":"Femenino (0)","1":"Masculino (1)"}},
        ],
    },
}

PRED_CSV_MAP = {
    "career_recommendation": "nlp_pred_career_recommendation.csv",
    "risk_alert":            "nlp_pred_risk_alert.csv",
    "computer_science":      "nlp_pred_computer_science.csv",
    "technical_drawing":     "nlp_pred_technical_drawing.csv",
    "math_risk":             "math_risk_scores.csv",
    "support_level":         "support_level_predictions.csv",
}

STUDENT_PRED_FILES = {
    "Alerta de Riesgo":        "nlp_pred_risk_alert.csv",
    "Recomendación Carrera":   "nlp_pred_career_recommendation.csv",
    "Predicción Computación":  "nlp_pred_computer_science.csv",
    "Predicción Dibujo Técnico": "nlp_pred_technical_drawing.csv",
    "Riesgo Matemáticas":      "math_risk_scores.csv",
    "Nivel de Soporte":        "support_level_predictions.csv",
}

SUBJECTS = [
    "Mathematics","English","Science","ICT","Geography","History","Civics",
    "Agriculture","Business_Studies","Physical_Education","Swahili","French",
    "Biology","Chemistry","Physics","Entrepreneurship","Fine_Arts","Music",
    "Additional_Math","Religious_Education",
]

# ── Helpers ──────────────────────────────────────────────────────────────────────

def get_internal_db():
    db = InternalSession()
    try:
        yield db
    finally:
        db.close()

def get_platform_db():
    db = PlatformSession()
    try:
        yield db
    finally:
        db.close()

def _get_user(request: Request, db: Session) -> Optional[UsuarioAdmin]:
    token = request.cookies.get("access_token")
    if not token:
        print("DEBUG: No token in cookies")
        return None
    try:
        payload = decode_access_token(token)
        if not payload:
            print("DEBUG: decode_access_token returned None")
            return None
        user_id = payload.get("sub")
        if user_id is None:
            print("DEBUG: no sub in payload")
            return None
        print(f"DEBUG: fetching user {user_id}")
        user = db.query(UsuarioAdmin).filter(
            UsuarioAdmin.id_usuario == int(user_id),
            UsuarioAdmin.activo == True,
        ).first()
        if not user:
            print("DEBUG: user not found or not activo in db")
        return user
    except Exception as e:
        print(f"DEBUG: Exception in _get_user: {e}")
        return None

def _redirect_login() -> RedirectResponse:
    return RedirectResponse("/login", status_code=303)

def _load_csv(name: str) -> pd.DataFrame:
    return pd.read_csv(DATASETS_DIR / name)

# ── Auth ──────────────────────────────────────────────────────────────────────────

@router.get("/login", response_class=HTMLResponse)
async def login_page(request: Request):
    return templates.TemplateResponse("login.html", {"request": request, "error": None})

@router.post("/login")
async def login_submit(
    request: Request,
    username: str = Form(...),
    password: str = Form(...),
    db: Session = Depends(get_internal_db),
):
    user = db.query(UsuarioAdmin).filter(
        (UsuarioAdmin.username == username) | (UsuarioAdmin.email == username)
    ).first()

    if not user or not verify_password(password, user.password_hash) or not user.activo:
        return templates.TemplateResponse("login.html", {
            "request": request,
            "error": "Usuario o contraseña incorrectos",
        })

    expires = timedelta(minutes=settings.jwt_access_token_expire_minutes)
    token = create_access_token({"sub": str(user.id_usuario), "username": user.username}, expires)

    db.add(SesionAdmin(
        id_usuario=user.id_usuario, token=token,
        ip=request.client.host if request.client else None,
        user_agent=request.headers.get("user-agent"),
        fecha_expiracion=datetime.now(timezone.utc) + expires,
    ))
    user.ultimo_login = datetime.now(timezone.utc)
    db.add(LogAdmin(id_usuario=user.id_usuario, accion="WEB_LOGIN", descripcion="Login web panel"))
    db.commit()

    response = RedirectResponse("/panel", status_code=303)
    response.set_cookie(
        "access_token", token,
        httponly=True, samesite="lax",
        max_age=settings.jwt_access_token_expire_minutes * 60,
    )
    return response

@router.post("/logout")
async def logout(request: Request, db: Session = Depends(get_internal_db)):
    token = request.cookies.get("access_token")
    if token:
        db.query(SesionAdmin).filter(SesionAdmin.token == token).delete()
        db.commit()
    response = RedirectResponse("/login", status_code=303)
    response.delete_cookie("access_token")
    return response

# ── Root ──────────────────────────────────────────────────────────────────────────

@router.get("/", response_class=HTMLResponse)
async def root(request: Request, db: Session = Depends(get_internal_db)):
    user = _get_user(request, db)
    if not user:
        return _redirect_login()
    return RedirectResponse("/panel", status_code=303)

# ── Panel overview ────────────────────────────────────────────────────────────────

@router.get("/panel", response_class=HTMLResponse)
async def panel_overview(request: Request, db: Session = Depends(get_internal_db)):
    user = _get_user(request, db)
    if not user:
        return _redirect_login()

    df = _load_csv("clean_dataset.csv")
    risk_df = _load_csv("nlp_pred_risk_alert.csv")

    kpis = {
        "total_students": len(df),
        "avg_final":      round(float(df["avg_final"].mean()), 1),
        "pass_rate":      round(float(df["passed_rate_percent"].mean()), 1),
        "high_risk":      int((risk_df["risk_level"] == "High").sum()),
        "low_support":    int((df["support_level"] == "Low").sum()),
    }

    chart_json = (STATS_DIR / "dashboard_demographics.json").read_text(encoding="utf-8")

    return templates.TemplateResponse("panel.html", {
        "request": request, "user": user, "active": "panel",
        "kpis": kpis, "chart_json": chart_json,
        "stat_dashboards": STAT_DASHBOARDS, "ml_dashboards": ML_DASHBOARDS,
    })

# ── Estadísticas ──────────────────────────────────────────────────────────────────

@router.get("/panel/estadisticas", response_class=HTMLResponse)
async def estadisticas(
    request: Request,
    dashboard: str = "demographics",
    db: Session = Depends(get_internal_db),
):
    user = _get_user(request, db)
    if not user:
        return _redirect_login()

    if dashboard not in STAT_DASHBOARDS:
        dashboard = "demographics"

    chart_json = (STATS_DIR / f"dashboard_{dashboard}.json").read_text(encoding="utf-8")
    figures = sorted(f.stem for f in FIGURES_DIR.glob("*.html")) if FIGURES_DIR.exists() else []

    return templates.TemplateResponse("estadisticas.html", {
        "request": request, "user": user, "active": "estadisticas",
        "stat_dashboards": STAT_DASHBOARDS,
        "current_dashboard": dashboard,
        "current_name": STAT_DASHBOARDS[dashboard],
        "chart_json": chart_json, "figures": figures,
    })

@router.get("/panel/figuras/{name}", response_class=HTMLResponse)
async def figura_html(name: str, request: Request, db: Session = Depends(get_internal_db)):
    user = _get_user(request, db)
    if not user:
        return _redirect_login()
    path = FIGURES_DIR / f"{name}.html"
    if not path.exists():
        return HTMLResponse("<p>Figura no encontrada</p>", status_code=404)
    return HTMLResponse(path.read_text(encoding="utf-8"))

# ── Predicciones ──────────────────────────────────────────────────────────────────

@router.get("/panel/predicciones", response_class=HTMLResponse)
async def predicciones(
    request: Request,
    tipo: str = "risk_alert",
    page: int = 1,
    db: Session = Depends(get_internal_db),
):
    user = _get_user(request, db)
    if not user:
        return _redirect_login()

    if tipo not in ALL_PRED_DATASETS:
        tipo = "risk_alert"

    df = _load_csv(PRED_CSV_MAP[tipo])
    page_size = 30
    total = len(df)
    total_pages = max(1, math.ceil(total / page_size))
    page = max(1, min(page, total_pages))
    rows = df.iloc[(page-1)*page_size : page*page_size].fillna("").to_dict(orient="records")

    ml_key_map = {"math_risk": "math_pass", "support_level": "support_level"}
    ml_dashboard_json = None
    if tipo in ml_key_map:
        p = PREDS_DIR / f"dashboard_ml_{ml_key_map[tipo]}.json"
        if p.exists():
            ml_dashboard_json = p.read_text(encoding="utf-8")

    return templates.TemplateResponse("predicciones.html", {
        "request": request, "user": user, "active": "predicciones",
        "all_datasets": ALL_PRED_DATASETS,
        "current_tipo": tipo, "current_name": ALL_PRED_DATASETS[tipo],
        "columns": list(df.columns), "rows": rows,
        "total": total, "page": page, "total_pages": total_pages,
        "ml_dashboard_json": ml_dashboard_json, "ml_dashboards": ML_DASHBOARDS,
    })

@router.get("/panel/predicciones/dashboard/{name}", response_class=HTMLResponse)
async def pred_dashboard(name: str, request: Request, db: Session = Depends(get_internal_db)):
    user = _get_user(request, db)
    if not user:
        return _redirect_login()
    if name not in ML_DASHBOARDS:
        return _redirect_login()
    chart_json = (PREDS_DIR / f"dashboard_ml_{name}.json").read_text(encoding="utf-8")
    return templates.TemplateResponse("estadisticas.html", {
        "request": request, "user": user, "active": "predicciones",
        "stat_dashboards": ML_DASHBOARDS,
        "current_dashboard": name,
        "current_name": ML_DASHBOARDS[name],
        "chart_json": chart_json, "figures": [],
    })

# ── Estudiantes ───────────────────────────────────────────────────────────────────

@router.get("/panel/estudiantes", response_class=HTMLResponse)
async def estudiantes(
    request: Request,
    country: str = "", course: str = "", gender: str = "", support_level: str = "",
    page: int = 1,
    db: Session = Depends(get_internal_db),
):
    user = _get_user(request, db)
    if not user:
        return _redirect_login()

    df = _load_csv("clean_dataset.csv")
    if country:       df = df[df["country"].str.lower() == country.lower()]
    if course:        df = df[df["course"].str.lower() == course.lower()]
    if gender:        df = df[df["gender"].str.upper() == gender.upper()]
    if support_level: df = df[df["support_level"].str.lower() == support_level.lower()]

    page_size = 40
    total = len(df)
    total_pages = max(1, math.ceil(total / page_size))
    page = max(1, min(page, total_pages))

    cols = ["student_id","gender","age","country","course","support_level","avg_final","grade_tendency","passed_rate_percent"]
    rows = df[cols].iloc[(page-1)*page_size : page*page_size].fillna("").to_dict(orient="records")

    full_df = _load_csv("clean_dataset.csv")
    countries = sorted(full_df["country"].dropna().unique())
    courses   = sorted(full_df["course"].dropna().unique())

    return templates.TemplateResponse("estudiantes.html", {
        "request": request, "user": user, "active": "estudiantes",
        "rows": rows, "total": total, "page": page, "total_pages": total_pages,
        "filters": {"country": country, "course": course, "gender": gender, "support_level": support_level},
        "countries": countries, "courses": courses,
    })

@router.get("/panel/estudiantes/{student_id}", response_class=HTMLResponse)
async def estudiante_detail(
    request: Request, student_id: str,
    db: Session = Depends(get_internal_db),
):
    user = _get_user(request, db)
    if not user:
        return _redirect_login()

    df = _load_csv("clean_dataset.csv")
    row = df[df["student_id"] == student_id]
    if row.empty:
        return RedirectResponse("/panel/estudiantes", status_code=303)

    student = row.fillna("").to_dict(orient="records")[0]

    predictions: dict = {}
    for label, fname in STUDENT_PRED_FILES.items():
        try:
            pf = _load_csv(fname)
            prow = pf[pf["student_id"] == student_id]
            if not prow.empty:
                predictions[label] = prow.fillna("").to_dict(orient="records")[0]
        except Exception:
            pass

    grades = []
    for subj in SUBJECTS:
        final_key  = f"{subj}_final"
        status_key = f"{subj}_status"
        if final_key in student and student[final_key] != "":
            grades.append({
                "subject": subj.replace("_", " "),
                "final":   student[final_key],
                "status":  student.get(status_key, ""),
            })

    return templates.TemplateResponse("estudiante.html", {
        "request": request, "user": user, "active": "estudiantes",
        "student": student, "predictions": predictions, "grades": grades,
    })

# ── Modelos / Inferencia ──────────────────────────────────────────────────────────

@router.get("/panel/modelos", response_class=HTMLResponse)
async def modelos_page(
    request: Request,
    model: str = "avg-final",
    db: Session = Depends(get_internal_db),
):
    user = _get_user(request, db)
    if not user:
        return _redirect_login()
    if model not in MODEL_CONFIGS:
        model = "avg-final"
    return templates.TemplateResponse("modelos.html", {
        "request": request, "user": user, "active": "modelos",
        "model_configs": MODEL_CONFIGS, "current_model": model,
        "result": None, "error": None, "form_data": {},
    })

@router.post("/panel/modelos", response_class=HTMLResponse)
async def modelos_infer(request: Request, db: Session = Depends(get_internal_db)):
    user = _get_user(request, db)
    if not user:
        return _redirect_login()

    form = await request.form()
    model = str(form.get("model_key", "avg-final"))
    if model not in MODEL_CONFIGS:
        model = "avg-final"

    result: Optional[dict] = None
    error: Optional[str]   = None

    try:
        import joblib
        import numpy as np

        PKL = MODELS_DIR / "models-pkl"
        fv = lambda k: float(form[k])

        if model == "avg-final":
            m = joblib.load(PKL / "linear_avg_final.pkl")
            X = np.array([[fv("age"), fv("gender_encoded"), fv("course_encoded"),
                           fv("attendance_absences_total"), fv("support_level_num"),
                           fv("total_subjects"), fv("term1_avg")]])
            result = {"Promedio Final Predicho": f"{m.predict(X)[0]:.2f}"}

        elif model == "math-final":
            m = joblib.load(PKL / "linear_mathematics_final.pkl")
            X = np.array([[fv("Mathematics_term1"), fv("Mathematics_term2"),
                           fv("Mathematics_term3"), fv("attendance_absences_total")]])
            result = {"Nota Final Matemáticas": f"{m.predict(X)[0]:.2f}"}

        elif model == "grade-tendency":
            scaler, m = joblib.load(PKL / "logistic_grade_tendency.pkl")
            X = np.array([[fv("term1_avg"), fv("term2_avg"), fv("term3_avg"),
                           fv("attendance_absences_total"), fv("support_level_num"),
                           fv("age"), fv("gender_encoded")]])
            pred = int(m.predict(scaler.transform(X))[0])
            proba = m.predict_proba(scaler.transform(X))[0]
            result = {
                "Predicción":    "⬆ Uptrend" if pred == 1 else "⬇ Downtrend",
                "P(Downtrend)": f"{proba[0]:.2%}",
                "P(Uptrend)":   f"{proba[1]:.2%}",
            }

        elif model == "math-pass":
            scaler, m = joblib.load(PKL / "logistic_mathematics_pass.pkl")
            X = np.array([[fv("Mathematics_term1"), fv("Mathematics_term2"),
                           fv("Mathematics_term3"), fv("attendance_absences_total"),
                           fv("support_level_num")]])
            pred = int(m.predict(scaler.transform(X))[0])
            proba = m.predict_proba(scaler.transform(X))[0]
            result = {
                "Predicción": "✅ Pass" if pred == 1 else "❌ Fail",
                "P(Fail)":   f"{proba[0]:.2%}",
                "P(Pass)":   f"{proba[1]:.2%}",
            }

        elif model == "support-level":
            scaler, m = joblib.load(PKL / "logistic_support_level.pkl")
            X = np.array([[fv("term1_avg"), fv("attendance_absences_total"),
                           fv("failed_subjects"), fv("grade_tendency_encoded"),
                           fv("age"), fv("gender_encoded")]])
            pred = int(m.predict(scaler.transform(X))[0])
            proba = m.predict_proba(scaler.transform(X))[0]
            labels = {0: "🟢 Low", 1: "🟡 Medium", 2: "🔴 High"}
            result = {
                "Nivel de Soporte": labels[pred],
                "P(Low)":   f"{proba[0]:.2%}",
                "P(Medium)":f"{proba[1]:.2%}",
                "P(High)":  f"{proba[2]:.2%}",
            }

    except Exception as e:
        error = str(e)

    return templates.TemplateResponse("modelos.html", {
        "request": request, "user": user, "active": "modelos",
        "model_configs": MODEL_CONFIGS, "current_model": model,
        "result": result, "error": error, "form_data": dict(form),
    })

# ── Plataforma ────────────────────────────────────────────────────────────────────

@router.get("/panel/plataforma", response_class=HTMLResponse)
async def plataforma(
    request: Request,
    tab: str = "resumen",
    page: int = 1,
    db: Session = Depends(get_internal_db),
    platform_db: Session = Depends(get_platform_db),
):
    user = _get_user(request, db)
    if not user:
        return _redirect_login()

    platform_error = None
    summary: dict = {}
    items: list = []
    columns: list = []
    total = 0
    total_pages = 1
    PAGE_SIZE = 30

    try:
        summary = {
            "total_users":       platform_db.query(Usuario).count(),
            "active_users":      platform_db.query(Usuario).filter(Usuario.activo == True).count(),
            "total_students":    platform_db.query(Alumno).count(),
            "total_teachers":    platform_db.query(Profesor).count(),
            "total_courses":     platform_db.query(Curso).count(),
            "total_enrollments": platform_db.query(Matricula).count(),
            "total_activities":  platform_db.query(Actividad).count(),
            "total_submissions": platform_db.query(Entrega).count(),
        }

        if tab == "usuarios":
            q = platform_db.query(Usuario).options(joinedload(Usuario.rol))
            total = q.count()
            total_pages = max(1, math.ceil(total / PAGE_SIZE))
            page = max(1, min(page, total_pages))
            rows = q.offset((page-1)*PAGE_SIZE).limit(PAGE_SIZE).all()
            columns = ["ID","Nombre","Email","Rol","Activo","Creado"]
            items = [{
                "ID": u.id_usuario,
                "Nombre": f"{u.nombre} {u.apellidos or ''}".strip(),
                "Email": u.email,
                "Rol": u.rol.nombre if u.rol else "",
                "Activo": "✅" if u.activo else "❌",
                "Creado": u.fecha_creacion.strftime("%d/%m/%Y") if u.fecha_creacion else "",
            } for u in rows]

        elif tab == "alumnos":
            q = platform_db.query(Alumno).options(joinedload(Alumno.usuario))
            total = q.count()
            total_pages = max(1, math.ceil(total / PAGE_SIZE))
            page = max(1, min(page, total_pages))
            rows = q.offset((page-1)*PAGE_SIZE).limit(PAGE_SIZE).all()
            columns = ["ID","Nombre","Email","Nivel"]
            items = [{
                "ID": a.id_alumno,
                "Nombre": f"{a.usuario.nombre} {a.usuario.apellidos or ''}".strip() if a.usuario else "",
                "Email": a.usuario.email if a.usuario else "",
                "Nivel": a.nivel or "",
            } for a in rows]

        elif tab == "profesores":
            q = platform_db.query(Profesor).options(joinedload(Profesor.usuario))
            total = q.count()
            total_pages = max(1, math.ceil(total / PAGE_SIZE))
            page = max(1, min(page, total_pages))
            rows = q.offset((page-1)*PAGE_SIZE).limit(PAGE_SIZE).all()
            columns = ["ID","Nombre","Email","Especialidad"]
            items = [{
                "ID": p.id_profesor,
                "Nombre": f"{p.usuario.nombre} {p.usuario.apellidos or ''}".strip() if p.usuario else "",
                "Email": p.usuario.email if p.usuario else "",
                "Especialidad": p.especialidad or "",
            } for p in rows]

        elif tab == "cursos":
            q = platform_db.query(Curso).options(joinedload(Curso.profesor).joinedload(Profesor.usuario))
            total = q.count()
            total_pages = max(1, math.ceil(total / PAGE_SIZE))
            page = max(1, min(page, total_pages))
            rows = q.offset((page-1)*PAGE_SIZE).limit(PAGE_SIZE).all()
            columns = ["ID","Nombre","Profesor","Creado"]
            items = [{
                "ID": c.id_curso, "Nombre": c.nombre,
                "Profesor": f"{c.profesor.usuario.nombre} {c.profesor.usuario.apellidos or ''}".strip() if c.profesor and c.profesor.usuario else "",
                "Creado": c.fecha_creacion.strftime("%d/%m/%Y") if c.fecha_creacion else "",
            } for c in rows]

    except Exception as e:
        platform_error = f"Base de datos de plataforma no disponible: {e}"

    return templates.TemplateResponse("plataforma.html", {
        "request": request, "user": user, "active": "plataforma",
        "tab": tab, "summary": summary,
        "columns": columns, "items": items,
        "total": total, "page": page, "total_pages": total_pages,
        "platform_error": platform_error,
    })
