from __future__ import annotations

import hmac
import uuid

from fastapi import Depends, HTTPException, Request, status
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload

from .database import get_db
from .models import User, UserRole


def session_is_authenticated(request: Request) -> bool:
    return bool(request.session.get("user_id"))


def session_role(request: Request) -> str:
    return str(request.session.get("role") or "")


def session_is_technical(request: Request) -> bool:
    return session_is_authenticated(request) and session_role(request) in {
        UserRole.technicien.value,
        UserRole.admin.value,
    }


def session_is_admin(request: Request) -> bool:
    return session_is_authenticated(request) and session_role(request) == UserRole.admin.value


async def require_user(
    request: Request,
    db: AsyncSession = Depends(get_db),
) -> User:
    raw_id = request.session.get("user_id")
    if not raw_id:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Authentification requise")
    try:
        user_id = uuid.UUID(str(raw_id))
    except ValueError as exc:
        request.session.clear()
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Session invalide") from exc
    statement = (
        select(User)
        .options(selectinload(User.commune))
        .where(User.id == user_id, User.actif.is_(True))
    )
    user = await db.scalar(statement)
    if not user:
        request.session.clear()
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Compte inactif ou introuvable")
    return user


async def require_technical_user(user: User = Depends(require_user)) -> User:
    if user.role not in {UserRole.technicien, UserRole.admin}:
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Accès technique interdit")
    return user


async def require_admin_user(user: User = Depends(require_user)) -> User:
    if user.role != UserRole.admin:
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Accès administrateur interdit")
    return user


def verify_csrf(request: Request) -> None:
    expected = str(request.session.get("csrf_token") or "")
    provided = request.headers.get("X-CSRF-Token", "")
    if not expected or not hmac.compare_digest(expected, provided):
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Jeton de sécurité invalide")


async def require_technical_csrf(
    request: Request,
    user: User = Depends(require_technical_user),
) -> User:
    verify_csrf(request)
    return user


async def require_admin_csrf(
    request: Request,
    user: User = Depends(require_admin_user),
) -> User:
    verify_csrf(request)
    return user
