"""Dependency injection for FastAPI routes."""

from collections.abc import AsyncGenerator

from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from datetime import datetime, timezone

from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession

from mixlift.auth.service import decode_token
from mixlift.config import settings
from mixlift.db import get_session
from mixlift.projects.models import ModelRun, Project, User

security = HTTPBearer()


async def get_db() -> AsyncGenerator[AsyncSession, None]:
    async for session in get_session():
        yield session


async def get_current_user(
    credentials: HTTPAuthorizationCredentials = Depends(security),
    session: AsyncSession = Depends(get_db),
) -> User:
    payload = decode_token(credentials.credentials)
    if not payload or payload.get("type") != "access":
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")

    user_id = payload.get("sub")
    result = await session.execute(select(User).where(User.id == user_id))
    user = result.scalar_one_or_none()
    if not user:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found")

    return user


def require_tier(min_tier: str):
    """Dependency that checks user's subscription tier."""
    tier_levels = {"free": 0, "pro": 1}

    async def check_tier(user: User = Depends(get_current_user)):
        if tier_levels.get(user.tier, 0) < tier_levels.get(min_tier, 0):
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail=f"This feature requires {min_tier} tier or above",
            )
        return user

    return check_tier


async def check_free_tier_run_limit(
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_db),
) -> User:
    """Reject training requests if a free-tier user has hit their monthly limit."""
    if user.tier != "free":
        return user

    now = datetime.now(timezone.utc)
    month_start = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)

    count_result = await session.execute(
        select(func.count(ModelRun.id))
        .join(Project, ModelRun.project_id == Project.id)
        .where(
            Project.user_id == user.id,
            ModelRun.status == "completed",
            ModelRun.created_at >= month_start,
        )
    )
    count = count_result.scalar_one()

    if count >= settings.free_max_runs_per_month:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail=f"Free tier limit reached: {settings.free_max_runs_per_month} model runs per month. Upgrade to continue.",
        )
    return user
