from dataclasses import dataclass
from uuid import UUID

import logging
from fastapi import Depends, HTTPException, status
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.ext.asyncio import AsyncSession

from app.auth.keycloak_dependencies import get_current_keycloak_payload
from app.infra.db.metadb.database import get_metadata_session
from app.infra.db.metadb.repositories.metadata_repository import MetadataRepository

logger = logging.getLogger(__name__)


async def get_metadata_repository(
    session: AsyncSession = Depends(get_metadata_session),
) -> MetadataRepository:
    return MetadataRepository(session)


def _keycloak_sub_from_payload(payload: dict) -> UUID:
    sub = payload.get("sub")
    if not sub:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Missing subject claim",
            headers={"WWW-Authenticate": "Bearer"},
        )

    try:
        return UUID(str(sub))
    except ValueError as exc:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid subject claim",
            headers={"WWW-Authenticate": "Bearer"},
        ) from exc


def _username_from_payload(payload: dict) -> str | None:
    username = payload.get("preferred_username") or payload.get("username")
    return str(username) if username else None


def _email_from_payload(payload: dict) -> str | None:
    email = payload.get("email")
    return str(email) if email else None


async def get_current_metadata_user(
    keycloak_payload: dict = Depends(get_current_keycloak_payload),
    metadata_repo: MetadataRepository = Depends(get_metadata_repository),
):
    keycloak_sub = _keycloak_sub_from_payload(keycloak_payload)
    try:
        user = await metadata_repo.get_user_by_keycloak_id(keycloak_sub)
    except SQLAlchemyError as exc:
        logger.error(
            "Metadata DB error while resolving current user",
            exc_info=True,
        )
        raise HTTPException(
            status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
            detail=f"Metadata database error: {exc}",
        ) from exc
    if not user or not user.is_active:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN, detail="Inactive user"
        )
    try:
        user = await metadata_repo.refresh_user_keycloak_snapshot(
            user,
            username=_username_from_payload(keycloak_payload),
            email=_email_from_payload(keycloak_payload),
        )
    except SQLAlchemyError as exc:
        logger.error(
            "Metadata DB error while refreshing current user snapshot",
            exc_info=True,
        )
        raise HTTPException(
            status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
            detail=f"Metadata database error: {exc}",
        ) from exc
    return user


async def get_current_metadata_admin(
    user=Depends(get_current_metadata_user),
):
    if user.is_superuser or user.role == "admin":
        return user
    raise HTTPException(
        status_code=status.HTTP_403_FORBIDDEN, detail="Admin access required"
    )


@dataclass(frozen=True)
class _AuthBypassUser:
    id: UUID = UUID(int=0)
    role: str = "admin"
    is_superuser: bool = True
    is_active: bool = True
