from typing import Annotated, Any

from fastapi import Depends, Request
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from motor.motor_asyncio import AsyncIOMotorCollection

from app.auth.jwt import TokenError, decode_token
from app.config import Settings, get_settings
from app.errors import ApiError
from app.services.auth_service import AuthService
from app.services.camera_service import CameraService
from app.services.clip_service import ClipService
from app.services.event_service import EventService
from app.services.storage_service import StorageService

bearer_scheme = HTTPBearer(auto_error=False)


def get_database(request: Request) -> Any:
    return request.app.state.database


def get_clip_collection(request: Request) -> AsyncIOMotorCollection[dict[str, Any]]:
    return get_database(request).get_clip_collection()


def get_user_collection(request: Request) -> AsyncIOMotorCollection[dict[str, Any]]:
    return get_database(request).get_user_collection()


def get_auth_service(
    users: Annotated[AsyncIOMotorCollection[dict[str, Any]], Depends(get_user_collection)],
    settings: Annotated[Settings, Depends(get_settings)],
) -> AuthService:
    return AuthService(users, settings)


def get_clip_service(
    collection: Annotated[AsyncIOMotorCollection[dict[str, Any]], Depends(get_clip_collection)],
) -> ClipService:
    return ClipService(collection)


def get_event_service(
    collection: Annotated[AsyncIOMotorCollection[dict[str, Any]], Depends(get_clip_collection)],
) -> EventService:
    return EventService(collection)


def get_camera_service(settings: Annotated[Settings, Depends(get_settings)]) -> CameraService:
    return CameraService(settings)


def get_storage_service(request: Request, settings: Annotated[Settings, Depends(get_settings)]) -> StorageService:
    return StorageService(get_database(request).get_minio_client(), settings)


async def get_current_username(
    request: Request,
    credentials: Annotated[HTTPAuthorizationCredentials | None, Depends(bearer_scheme)],
    settings: Annotated[Settings, Depends(get_settings)],
) -> str:
    if credentials is None:
        raise ApiError("not_authenticated", "Authentication credentials were not provided", 401)
    try:
        payload = decode_token(credentials.credentials, settings, "access")
    except TokenError as exc:
        raise ApiError("invalid_token", "Access token is invalid", 401) from exc
    username = payload["sub"]
    request.state.user_id = username
    return username


CurrentUsername = Annotated[str, Depends(get_current_username)]


async def require_auth(username: CurrentUsername) -> None:
    return None
