import uuid

from fastapi import Depends, HTTPException, Request, status
from fastapi.security import APIKeyHeader, HTTPAuthorizationCredentials, HTTPBearer

from src.core.exceptions import ForbiddenException, UnauthorizedException
from src.domain.entities import UserRole
from src.infrastructure.database.session import AsyncSession, get_db_session
from src.services.auth.auth_service import AuthService
from src.services.auth.security import RBACGuard, TokenData, decode_token, validate_token_type
from src.infrastructure.database.repositories import APIClientRepository, APIKeyRepository
from src.services.auth.security import verify_api_key

_bearer_scheme = HTTPBearer(auto_error=False)
_api_key_scheme = APIKeyHeader(name="X-API-Key", auto_error=False)


async def get_auth_service(session: AsyncSession = Depends(get_db_session)) -> AuthService:
    return AuthService(session)


async def get_api_key_user(
    api_key: str | None = Depends(_api_key_scheme),
    session: AsyncSession = Depends(get_db_session),
) -> TokenData | None:
    if not api_key:
        return None

    prefix = api_key[:8]
    key_repo = APIKeyRepository(session)
    key_entity = await key_repo.get_by_prefix(prefix)
    if not key_entity or not key_entity.is_active:
        return None

    from datetime import datetime, timezone
    if key_entity.expires_at and key_entity.expires_at < datetime.now(timezone.utc):
        return None

    if not verify_api_key(api_key, key_entity.key_hash):
        return None

    client_repo = APIClientRepository(session)
    client = await client_repo.get_by_id(key_entity.api_client_id)
    if not client or not client.is_active:
        return None

    await key_repo.update_last_used(key_entity.id)

    return TokenData({
        "sub": str(client.id),
        "role": "api_client",
        "scopes": client.scopes or [],
        "iss": "pip-platform",
        "type": "access",
        "jti": str(key_entity.id),
    })


async def get_current_user(
    request: Request,
    credentials: HTTPAuthorizationCredentials | None = Depends(_bearer_scheme),
    api_key_user: TokenData | None = Depends(get_api_key_user),
) -> TokenData:
    if credentials:
        try:
            payload = decode_token(credentials.credentials)
            validate_token_type(payload, expected_type="access")
            user = TokenData(payload)
            request.state.user = user
            return user
        except UnauthorizedException:
            raise
        except Exception as e:
            raise UnauthorizedException(f"Invalid token: {e}") from e

    if api_key_user:
        request.state.user = api_key_user
        return api_key_user

    raise UnauthorizedException("Authentication required. Provide Bearer token or X-API-Key header.")


def get_current_user_optional(
    credentials: HTTPAuthorizationCredentials | None = Depends(_bearer_scheme),
) -> TokenData | None:
    if not credentials:
        return None
    try:
        payload = decode_token(credentials.credentials)
        validate_token_type(payload, expected_type="access")
        return TokenData(payload)
    except Exception:
        return None


def require_role(role: UserRole):
    def _checker(current_user: TokenData = Depends(get_current_user)) -> TokenData:
        RBACGuard.require_role(current_user, role)
        return current_user
    return _checker


def require_scope(scope: str):
    def _checker(current_user: TokenData = Depends(get_current_user)) -> TokenData:
        RBACGuard.require_scope(current_user, scope)
        return current_user
    return _checker
