"""Quota enforcement for free-tier limits."""

from datetime import UTC, datetime

from sqlalchemy import func, select

from sulci_core.config import SulciConfig
from sulci_core.exceptions import QuotaExceededError
from sulci_core.schema import KnowledgeAtomRow, ProjectRow, UsageCounterRow


class QuotaService:
    """Enforces usage quotas. All methods are no-ops when quotas_enabled is False."""

    def __init__(self, config: SulciConfig, session_factory) -> None:
        self._config = config
        self._session_factory = session_factory
        self._user_id: str = ""  # Default: empty string = single-tenant / unscoped
        self._tier: str | None = None  # Override tier from JWT in multi-tenant mode

    def for_user(self, user_id: str, tier: str | None = None) -> "QuotaService":
        """Return a new QuotaService scoped to a specific user."""
        svc = QuotaService(self._config, self._session_factory)
        svc._user_id = user_id or ""
        svc._tier = tier
        return svc

    @property
    def enabled(self) -> bool:
        return self._config.quotas_enabled

    @property
    def _effective_tier(self) -> str:
        return self._tier or self._config.tier

    @property
    def _unlimited(self) -> bool:
        """True for admin tier or when quotas are disabled."""
        from sulci_core.auth import TIER_LIMITS
        limits = TIER_LIMITS.get(self._effective_tier, {})
        return limits.get("unlimited", False)

    def _tier_limit(self, key: str, config_fallback: int) -> int:
        """Get limit from TIER_LIMITS for the current tier, falling back to config."""
        from sulci_core.auth import TIER_LIMITS
        limits = TIER_LIMITS.get(self._effective_tier, {})
        return limits.get(key, config_fallback)

    async def check_atoms(self, raise_if_exceeded: bool = True) -> tuple[int, int]:
        """Check atom count vs limit. Returns (current, limit)."""
        if not self.enabled or self._unlimited:
            return (0, 0)
        limit = self._tier_limit("max_atoms", self._config.quota_max_atoms)
        async with self._session_factory() as session:
            q = select(func.count()).select_from(KnowledgeAtomRow).where(KnowledgeAtomRow.is_active.is_(True))
            if self._user_id:
                q = q.where(KnowledgeAtomRow.user_id == self._user_id)
            current = await session.scalar(q) or 0
        if raise_if_exceeded and current >= limit:
            raise QuotaExceededError("atoms", limit, current)
        return (current, limit)

    async def check_projects(self, raise_if_exceeded: bool = True) -> tuple[int, int]:
        """Check project count vs limit. Returns (current, limit)."""
        if not self.enabled or self._unlimited:
            return (0, 0)
        limit = self._tier_limit("max_projects", self._config.quota_max_projects)
        async with self._session_factory() as session:
            q = select(func.count()).select_from(ProjectRow).where(ProjectRow.status == "active")
            if self._user_id:
                q = q.where(ProjectRow.user_id == self._user_id)
            current = await session.scalar(q) or 0
        if raise_if_exceeded and current >= limit:
            raise QuotaExceededError("projects", limit, current)
        return (current, limit)

    async def check_extractions(self, raise_if_exceeded: bool = True) -> tuple[int, int]:
        """Check monthly extraction count vs limit. Returns (current, limit)."""
        if not self.enabled or self._unlimited:
            return (0, 0)
        limit = self._tier_limit("max_extractions_month", self._config.quota_max_extractions_month)
        period = datetime.now(UTC).strftime("%Y-%m")
        current = await self._get_counter("extractions", period)
        if raise_if_exceeded and current >= limit:
            raise QuotaExceededError("extractions", limit, current, period)
        return (current, limit)

    async def check_context_queries(self, raise_if_exceeded: bool = True) -> tuple[int, int]:
        """Check daily context query count vs limit. Returns (current, limit)."""
        if not self.enabled or self._unlimited:
            return (0, 0)
        limit = self._tier_limit("max_context_queries_day", self._config.quota_max_context_queries_day)
        period = datetime.now(UTC).strftime("%Y-%m-%d")
        current = await self._get_counter("context_queries", period)
        if raise_if_exceeded and current >= limit:
            raise QuotaExceededError("context_queries", limit, current, period)
        return (current, limit)

    async def increment_extractions(self) -> None:
        """Bump the monthly extraction counter."""
        if not self.enabled:
            return
        period = datetime.now(UTC).strftime("%Y-%m")
        await self._increment_counter("extractions", period)

    async def increment_context_queries(self) -> None:
        """Bump the daily context query counter."""
        if not self.enabled:
            return
        period = datetime.now(UTC).strftime("%Y-%m-%d")
        await self._increment_counter("context_queries", period)

    async def get_usage_summary(self) -> dict:
        """Return all limits + current usage for dashboard display."""
        atoms_current, atoms_limit = await self.check_atoms(raise_if_exceeded=False)
        projects_current, projects_limit = await self.check_projects(raise_if_exceeded=False)
        extractions_current, extractions_limit = await self.check_extractions(raise_if_exceeded=False)
        queries_current, queries_limit = await self.check_context_queries(raise_if_exceeded=False)

        return {
            "quotas_enabled": self.enabled,
            "tier": self._effective_tier,
            "limits": {
                "atoms": atoms_limit,
                "projects": projects_limit,
                "extractions_month": extractions_limit,
                "context_queries_day": queries_limit,
            },
            "current": {
                "atoms": atoms_current,
                "projects": projects_current,
                "extractions_month": extractions_current,
                "context_queries_day": queries_current,
            },
        }

    # --- Internal helpers ---

    async def _get_counter(self, counter_name: str, period_key: str) -> int:
        async with self._session_factory() as session:
            row = await session.get(UsageCounterRow, (self._user_id, counter_name, period_key))
            return row.count if row else 0

    async def _increment_counter(self, counter_name: str, period_key: str) -> None:
        async with self._session_factory() as session:
            row = await session.get(UsageCounterRow, (self._user_id, counter_name, period_key))
            if row:
                row.count += 1
            else:
                session.add(
                    UsageCounterRow(user_id=self._user_id, counter_name=counter_name, period_key=period_key, count=1)
                )
            await session.commit()
