"""FastAPI dependency injection setup."""

from collections.abc import AsyncGenerator
from typing import Annotated

import structlog
from fastapi import Depends, Header, HTTPException, Request
from redis.asyncio import Redis
from sqlalchemy.ext.asyncio import AsyncSession

from src.core.config import settings
from src.db.models import ApiKey
from src.db.session import get_async_session
from src.services.auth_service import (
    hash_api_key,
    update_last_used,
    validate_api_key,
)
from src.services.rate_limiter import check_rate_limit

logger = structlog.get_logger(__name__)

# Re-export database session dependency for convenience
AsyncSessionDep = Annotated[AsyncSession, Depends(get_async_session)]


# Redis client - using connection pool for reusability
_redis_client: Redis | None = None


async def get_redis_client() -> AsyncGenerator[Redis, None]:
    """Get async Redis client with connection pooling.

    Yields:
        Async Redis client instance.
    """
    global _redis_client

    if _redis_client is None:
        _redis_client = Redis.from_url(
            settings.redis_url,
            encoding="utf-8",
            decode_responses=True,
        )

    yield _redis_client


RedisDep = Annotated[Redis, Depends(get_redis_client)]


# Note: Milvus connections are created on-demand in service functions
# No persistent client dependency needed


async def get_current_api_key(
    request: Request,
    x_api_key: Annotated[str | None, Header()] = None,
    session: AsyncSession = Depends(get_async_session),
    redis_client: Redis = Depends(get_redis_client),
) -> ApiKey:
    """Authenticate request using API key from X-API-Key header.

    Validates the API key, checks rate limits, and updates last_used_at.

    Args:
        request: FastAPI request object (for logging context).
        x_api_key: The API key from the X-API-Key header.
        session: Async database session.
        redis_client: Async Redis client for rate limiting.

    Returns:
        The authenticated ApiKey model.

    Raises:
        HTTPException: 401 if missing/invalid API key, 429 if rate limited.
    """
    # Check for missing API key
    if not x_api_key:
        logger.warning("auth_missing_api_key", path=request.url.path)
        raise HTTPException(
            status_code=401,
            detail={
                "error": {
                    "code": "MISSING_API_KEY",
                    "message": "X-API-Key header required",
                }
            },
        )

    # Validate API key
    api_key = await validate_api_key(x_api_key, session)

    if api_key is None:
        logger.warning(
            "auth_invalid_api_key",
            path=request.url.path,
            key_hash_prefix=hash_api_key(x_api_key)[:8],
        )
        raise HTTPException(
            status_code=401,
            detail={
                "error": {
                    "code": "INVALID_API_KEY",
                    "message": "API key not found or inactive",
                }
            },
        )

    # Check rate limit
    key_hash = hash_api_key(x_api_key)
    rate_result = await check_rate_limit(
        redis_client=redis_client,
        key_hash=key_hash,
        rate_limit=api_key.rate_limit_per_minute,
    )

    if not rate_result.allowed:
        logger.warning(
            "auth_rate_limited",
            api_key_id=str(api_key.id),
            path=request.url.path,
            retry_after=rate_result.retry_after,
        )
        raise HTTPException(
            status_code=429,
            detail={
                "error": {
                    "code": "RATE_LIMIT_EXCEEDED",
                    "message": "Too many requests. Please try again later.",
                },
                "retry_after": rate_result.retry_after,
            },
            headers={"Retry-After": str(rate_result.retry_after)},
        )

    # Update last_used_at
    await update_last_used(api_key.id, session)

    logger.info(
        "auth_success",
        api_key_id=str(api_key.id),
        api_key_name=api_key.name,
        path=request.url.path,
        rate_limit_remaining=rate_result.remaining,
    )

    return api_key


# Type alias for API key dependency
ApiKeyDep = Annotated[ApiKey, Depends(get_current_api_key)]


# Legacy alias for backwards compatibility
CurrentApiKeyDep = ApiKeyDep
