from bluefox_core.database import get_session
from fastapi import Depends, HTTPException, Request
from fastapi.security import OAuth2PasswordBearer
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from bluefox_auth.csrf import validate_csrf
from bluefox_auth.models import BluefoxUser
from bluefox_auth.settings import AuthSettings
from bluefox_auth.tokens import InvalidTokenError, decode_token

# For Swagger UI support — auto_error=False so we can fall back to cookies
_oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/login", auto_error=False)


def _get_auth_settings(request: Request) -> AuthSettings:
    settings = getattr(request.app.state, "auth_settings", None)
    if settings is None:
        raise HTTPException(500, "Auth not configured")
    return settings


def _get_user_model(request: Request) -> type[BluefoxUser]:
    return getattr(request.app.state, "user_model", BluefoxUser)


def get_token_from_request(request: Request, settings: AuthSettings) -> tuple[str, bool]:
    """Extract token from Authorization header or cookie.

    Returns (token, is_cookie).
    """
    auth_header = request.headers.get("Authorization", "")
    if auth_header.lower().startswith("bearer "):
        return auth_header[7:], False

    token = request.cookies.get(settings.cookie_name_access)
    if token:
        return token, True

    raise HTTPException(401, "Not authenticated")


async def get_current_user(
    request: Request,
    session: AsyncSession = Depends(get_session),
    bearer_token: str | None = Depends(_oauth2_scheme),
) -> BluefoxUser:
    """Core dependency: extract and validate the current user from JWT."""
    settings = _get_auth_settings(request)
    user_model = _get_user_model(request)

    token, is_cookie = get_token_from_request(request, settings)

    try:
        payload = decode_token(token, settings, expected_type="access")
    except InvalidTokenError as err:
        raise HTTPException(401, str(err)) from err

    # CSRF validation for cookie-based mutating requests
    if is_cookie:
        validate_csrf(request, settings.cookie_name_csrf)

    result = await session.execute(select(user_model).where(user_model.id == payload.sub))
    user = result.scalar_one_or_none()

    if user is None or not user.is_active:
        raise HTTPException(401, "User not found or inactive")

    return user


async def current_active_user(
    user: BluefoxUser = Depends(get_current_user),
) -> BluefoxUser:
    """Public dependency for route protection."""
    return user


async def current_superuser(
    user: BluefoxUser = Depends(current_active_user),
) -> BluefoxUser:
    """Admin dependency — requires is_superuser."""
    if not user.is_superuser:
        raise HTTPException(403, "Superuser access required")
    return user
