from __future__ import annotations

from dataclasses import dataclass
from datetime import datetime
from typing import Optional

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

from ..models import APIKey
from ..model_defs.api_models import ApiUsage


@dataclass
class ApiContext:
    api_key_prefix: str
    account_id: str
    tier: str
    qps_limit: int
    monthly_quota: Optional[int]
    status: str


class ApiRepo:
    def __init__(self, session: AsyncSession):
        self.session = session

    async def get_key(self, full_key: str) -> Optional[ApiContext]:
        """Lookup API key by prefix + bcrypt verification.

        Returns ApiContext if valid and active, otherwise None.
        """
        # Validate
        if not full_key or len(full_key) < 16:
            return None

        try:
            import bcrypt
        except Exception:
            # bcrypt is required for secure key verification
            return None

        prefix = full_key[:8]
        stmt = select(APIKey).where(APIKey.key_prefix == prefix)
        result = await self.session.execute(stmt)
        row: Optional[APIKey] = result.scalar_one_or_none()
        if not row:
            return None

        # Status resolution
        status = (row.status or ("active" if row.is_active else "disabled")).lower()
        if status != "active":
            return ApiContext(
                api_key_prefix=row.key_prefix,
                account_id=str(row.account_id),
                tier=(row.tier or "free").lower(),
                qps_limit=int(row.qps_limit or row.rate_limit or 2),
                monthly_quota=int(row.monthly_quota)
                if row.monthly_quota is not None
                else None,
                status=status,
            )

        # Verify bcrypt hash
        if not bcrypt.checkpw(full_key.encode(), row.key_hash.encode()):
            return None

        # Update last_used_at
        try:
            row.last_used_at = datetime.utcnow()
            self.session.add(row)
            await self.session.commit()
        except Exception:
            await self.session.rollback()

        return ApiContext(
            api_key_prefix=row.key_prefix,
            account_id=str(row.account_id),
            tier=(row.tier or "free").lower(),
            qps_limit=int(row.qps_limit or row.rate_limit or 2),
            monthly_quota=int(row.monthly_quota)
            if row.monthly_quota is not None
            else None,
            status="active",
        )

    async def bump_usage(
        self,
        api_key_prefix: str,
        *,
        calls: int = 0,
        matches: int = 0,
        bytes_count: int = 0,
    ) -> None:
        """Increment usage counters for the current YYYYMM period.
        Uses an upsert-like pattern compatible with SQLite/Postgres.
        """
        now = datetime.utcnow()
        period = now.year * 100 + now.month

        # Try to locate existing row
        stmt = select(ApiUsage).where(
            ApiUsage.api_key == api_key_prefix, ApiUsage.period_yyyymm == period
        )
        r = await self.session.execute(stmt)
        usage: Optional[ApiUsage] = r.scalar_one_or_none()

        if usage is None:
            usage = ApiUsage(
                api_key=api_key_prefix,
                period_yyyymm=period,
                calls=int(calls or 0),
                matches=int(matches or 0),
                bytes=int(bytes_count or 0),
                last_update_at=now,
            )
            self.session.add(usage)
        else:
            usage.calls = (usage.calls or 0) + int(calls or 0)
            usage.matches = (usage.matches or 0) + int(matches or 0)
            usage.bytes = (usage.bytes or 0) + int(bytes_count or 0)
            usage.last_update_at = now
            self.session.add(usage)

        try:
            await self.session.commit()
        except Exception:
            await self.session.rollback()
