"""FastAPI dependency providers."""
from __future__ import annotations

from collections.abc import AsyncGenerator
from functools import lru_cache
from typing import Annotated

import redis.asyncio as aioredis
from fastapi import Depends, HTTPException, Request, Security, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from jose import JWTError, jwt
from sqlalchemy.ext.asyncio import AsyncSession
from telegram.ext import Application

from app.config import Settings, get_settings
from app.db.models.user import User
from app.db.repositories.user import UserRepository
from app.db.session import AsyncSessionLocal
from app.services.ai import AIService
from app.services.queue import QueueService
from app.services.storage import StorageService

_bearer = HTTPBearer(auto_error=False)


# ── Settings ──────────────────────────────────────────────────────────────────

def get_settings_dep() -> Settings:
    return get_settings()


# ── Database session ──────────────────────────────────────────────────────────

async def get_db_session() -> AsyncGenerator[AsyncSession, None]:
    async with AsyncSessionLocal() as session:
        try:
            yield session
            await session.commit()
        except Exception:
            await session.rollback()
            raise


# ── Redis ─────────────────────────────────────────────────────────────────────

_redis_pool: aioredis.Redis | None = None


def get_redis_pool(settings: Settings = Depends(get_settings_dep)) -> aioredis.Redis:
    global _redis_pool
    if _redis_pool is None:
        _redis_pool = aioredis.from_url(
            settings.redis_url.get_secret_value(),
            encoding="utf-8",
            decode_responses=True,
        )
    return _redis_pool


async def get_redis(settings: Settings = Depends(get_settings_dep)) -> aioredis.Redis:
    return get_redis_pool(settings)


# ── Services ──────────────────────────────────────────────────────────────────

_ai_service: AIService | None = None
_storage_service: StorageService | None = None
_queue_service: QueueService | None = None
_bot_app: Application | None = None


def get_ai_service(settings: Settings = Depends(get_settings_dep)) -> AIService:
    global _ai_service
    if _ai_service is None:
        _ai_service = AIService(settings)
    return _ai_service


def get_storage_service(settings: Settings = Depends(get_settings_dep)) -> StorageService:
    global _storage_service
    if _storage_service is None:
        _storage_service = StorageService(settings)
    return _storage_service


def get_queue(
    redis: aioredis.Redis = Depends(get_redis),
) -> QueueService:
    global _queue_service
    if _queue_service is None:
        _queue_service = QueueService(redis)
    return _queue_service


def get_bot_app(request: Request) -> Application:
    bot_app = request.app.state.bot_app
    if bot_app is None:
        raise HTTPException(status_code=503, detail="Bot not initialized")
    return bot_app


# ── Auth (JWT) ────────────────────────────────────────────────────────────────

async def get_current_user(
    credentials: HTTPAuthorizationCredentials | None = Security(_bearer),
    db: AsyncSession = Depends(get_db_session),
    settings: Settings = Depends(get_settings_dep),
) -> User:
    if not credentials:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Not authenticated")

    try:
        payload = jwt.decode(
            credentials.credentials,
            settings.app_secret_key.get_secret_value(),
            algorithms=["HS256"],
        )
        telegram_id: int = int(payload["sub"])
    except (JWTError, KeyError, ValueError):
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")

    repo = UserRepository(db)
    user = await repo.get_by_telegram_id(telegram_id)
    if not user:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found")
    return user
