from fastapi import Request, HTTPException
import secrets
import time
import hmac
import sqlite3
from passlib.context import CryptContext
from ..database import SECRET_KEY
from .crud import get_user_by_id

# We create another context here or reuse the one from crud.
# It is better to have it in one place, but for now we duplicate or keep it here as the main user.
# In main.py `pwd_context` was used for verify.
# In crud.py `pwd_context` was used for hash.
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

UNSAFE_METHODS = {"POST", "PUT", "PATCH", "DELETE"}

def rotate_session(request: Request) -> None:
    request.session.clear()
    request.session["sid"] = secrets.token_urlsafe(32)
    request.session["csrf"] = secrets.token_urlsafe(32)
    request.session["created_at"] = int(time.time())

    request.session["created_at"] = int(time.time())

def require_csrf(request: Request, token: str | None):
    sess = request.session.get("csrf")
    if not sess or not token or not hmac.compare_digest(str(sess), str(token or "")):
        raise HTTPException(status_code=403, detail="CSRF token inválido")

def _enforce_csrf_if_unsafe(request: Request):
    if request.method in UNSAFE_METHODS:
        token = request.headers.get("X-CSRF-Token")
        require_csrf(request, token)

def current_user(request: Request):
    user_id = request.session.get("user_id")
    if not user_id:
        return None
    return get_user_by_id(int(user_id))

def require_admin(request: Request):
    me = current_user(request)
    if not me:
        raise HTTPException(status_code=401, detail="No autenticado")
    _enforce_csrf_if_unsafe(request)
    role = (me["role"] if isinstance(me, sqlite3.Row) else str(me.get("role", ""))) or ""
    if str(role).lower() != "admin":
        raise HTTPException(status_code=403, detail="Solo administradores")
    return me

def require_docente_or_admin(request: Request):
    me = current_user(request)
    if not me:
        raise HTTPException(status_code=401, detail="No autenticado")
    _enforce_csrf_if_unsafe(request)
    role = (me["role"] if isinstance(me, sqlite3.Row) else str(me.get("role", ""))) or ""
    if str(role).lower() not in ("docente", "admin"):
        raise HTTPException(status_code=403, detail="Solo docentes o admin")
    return me
