"""
Dependency FastAPI – get_db, get_current_user, require_admin, RLS middleware.
"""

import uuid
from typing import Annotated

from fastapi import Depends, Request
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from jose import JWTError
from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.exceptions import ForbiddenError, TokenInvalidError
from app.core.security import decode_token
from app.database import get_db
from app.models.user import User
from sqlalchemy import select

security = HTTPBearer()

# ─── Database con RLS ─────────────────────────────────────────────────────────

async def _set_rls_tenant_id(db: AsyncSession, tenant_id: uuid.UUID) -> None:
    """
    Imposta la variabile di sessione PostgreSQL per RLS.

    È un no-op su SQLite (test environment) poiché SQLite non supporta
    il comando SET LOCAL né il concetto di Row Level Security.
    """
    try:
        await db.execute(
            text(f"SET LOCAL app.current_tenant_id = '{tenant_id!s}'")
        )
    except Exception:
        # SQLite (usato nei test di integrazione) non supporta SET LOCAL.
        # In produzione (PostgreSQL) questo comando funziona sempre.
        pass


async def get_db_with_rls(
    tenant_id: uuid.UUID,
    db: AsyncSession = Depends(get_db),
) -> AsyncSession:
    """
    Imposta la variabile di sessione PostgreSQL per RLS.
    Da usare dopo aver estratto il tenant_id dall'utente autenticato.
    """
    await _set_rls_tenant_id(db, tenant_id)
    return db


# ─── Utente corrente ──────────────────────────────────────────────────────────

async def get_current_user(
    credentials: Annotated[HTTPAuthorizationCredentials, Depends(security)],
    db: AsyncSession = Depends(get_db),
) -> User:
    """
    Estrae e valida il JWT dall'header Authorization: Bearer <token>.
    Carica l'utente dal DB e imposta RLS.
    """
    token = credentials.credentials

    try:
        payload = decode_token(token)
    except JWTError:
        raise TokenInvalidError()

    if payload.get("type") != "access":
        raise TokenInvalidError()

    user_id_str = payload.get("sub")
    tenant_id_str = payload.get("tid")

    if not user_id_str or not tenant_id_str:
        raise TokenInvalidError()

    try:
        user_id = uuid.UUID(user_id_str)
        tenant_id = uuid.UUID(tenant_id_str)
    except ValueError:
        raise TokenInvalidError()

    # Imposta RLS per questo tenant (no-op su SQLite/test)
    await _set_rls_tenant_id(db, tenant_id)

    # Carica utente
    result = await db.execute(
        select(User).where(User.id == user_id, User.tenant_id == tenant_id)
    )
    user = result.scalar_one_or_none()

    if not user:
        raise TokenInvalidError()

    if not user.is_active:
        from app.core.exceptions import AccountDisabledError
        raise AccountDisabledError()

    return user


# ─── Role guards ──────────────────────────────────────────────────────────────

async def require_admin(
    current_user: Annotated[User, Depends(get_current_user)],
) -> User:
    """Richiede ruolo admin o super_admin."""
    if not current_user.is_admin:
        raise ForbiddenError("Richiesto ruolo amministratore")
    return current_user


async def require_super_admin(
    current_user: Annotated[User, Depends(get_current_user)],
) -> User:
    """Richiede ruolo super_admin."""
    if not current_user.is_super_admin:
        raise ForbiddenError("Richiesto ruolo super_admin")
    return current_user


# ─── Tipo annotato per ridurre boilerplate negli endpoint ─────────────────────
CurrentUser = Annotated[User, Depends(get_current_user)]
AdminUser = Annotated[User, Depends(require_admin)]
SuperAdminUser = Annotated[User, Depends(require_super_admin)]
DB = Annotated[AsyncSession, Depends(get_db)]
