"""Quota management middleware for API usage limits."""

import logging
from abc import ABC, abstractmethod
from typing import Dict, Any, Optional
from datetime import datetime, timezone, timedelta
from fastapi import Request, HTTPException
from starlette.middleware.base import BaseHTTPMiddleware

LOGGER = logging.getLogger(__name__)


class QuotaStore(ABC):
    """Abstract interface for quota storage."""
    
    @abstractmethod
    async def get_quota(self, user_id: str) -> Dict[str, Any]:
        """Get quota information for user.
        
        Args:
            user_id: User identifier
            
        Returns:
            Dictionary with 'used', 'limit', and 'reset_at' keys
        """
        pass
    
    @abstractmethod
    async def increment(self, user_id: str) -> None:
        """Increment quota usage for user.
        
        Args:
            user_id: User identifier
        """
        pass
    
    @abstractmethod
    async def reset_if_needed(self, user_id: str) -> None:
        """Reset quota if period has elapsed.
        
        Args:
            user_id: User identifier
        """
        pass


class InMemoryQuotaStore(QuotaStore):
    """In-memory quota storage (for development/testing)."""
    
    def __init__(self, default_limit: int = 1000, period_days: int = 30):
        """Initialize in-memory quota store.
        
        Args:
            default_limit: Default quota limit per period
            period_days: Quota reset period in days
        """
        self.default_limit = default_limit
        self.period_days = period_days
        self.quotas: Dict[str, Dict[str, Any]] = {}
    
    async def get_quota(self, user_id: str) -> Dict[str, Any]:
        """Get quota information for user."""
        await self.reset_if_needed(user_id)
        
        if user_id not in self.quotas:
            self.quotas[user_id] = {
                "used": 0,
                "limit": self.default_limit,
                "reset_at": self._get_next_reset(),
            }
        
        return self.quotas[user_id]
    
    async def increment(self, user_id: str) -> None:
        """Increment quota usage for user."""
        quota = await self.get_quota(user_id)
        quota["used"] += 1
    
    async def reset_if_needed(self, user_id: str) -> None:
        """Reset quota if period has elapsed."""
        if user_id in self.quotas:
            quota = self.quotas[user_id]
            reset_at = datetime.fromisoformat(quota["reset_at"])
            if datetime.now(timezone.utc) >= reset_at:
                quota["used"] = 0
                quota["reset_at"] = self._get_next_reset()
    
    def _get_next_reset(self) -> str:
        """Calculate next quota reset time.
        
        Returns:
            ISO format timestamp string
        """
        next_reset = datetime.now(timezone.utc) + timedelta(days=self.period_days)
        return next_reset.isoformat()


class QuotaMiddleware(BaseHTTPMiddleware):
    """Enforce per-user/tenant quotas."""
    
    def __init__(self, app, quota_store: QuotaStore):
        """Initialize quota middleware.
        
        Args:
            app: FastAPI application
            quota_store: Quota storage backend
        """
        super().__init__(app)
        self.quota_store = quota_store
    
    async def dispatch(self, request: Request, call_next):
        """Process request with quota checking.
        
        Args:
            request: Incoming HTTP request
            call_next: Next middleware in chain
            
        Returns:
            HTTP response with quota headers
            
        Raises:
            HTTPException: 429 if quota exceeded
        """
        # Only apply quotas to discovery endpoints
        if request.url.path.startswith("/discover") or request.url.path.startswith("/v1/discover"):
            user_id = self._get_user_id(request)
            quota_info = await self.quota_store.get_quota(user_id)
            
            # Check if quota exceeded
            if quota_info["used"] >= quota_info["limit"]:
                LOGGER.warning(
                    "Quota exceeded",
                    extra={
                        "context": {
                            "user_id": user_id,
                            "used": quota_info["used"],
                            "limit": quota_info["limit"],
                        }
                    },
                )
                raise HTTPException(
                    status_code=429,
                    detail=f"Monthly quota exceeded ({quota_info['limit']} discoveries)",
                    headers={"X-Quota-Reset": quota_info["reset_at"]},
                )
            
            # Process request
            response = await call_next(request)
            
            # Increment quota on successful response
            if response.status_code == 200:
                await self.quota_store.increment(user_id)
                quota_info = await self.quota_store.get_quota(user_id)
            
            # Add quota headers
            response.headers["X-Quota-Limit"] = str(quota_info["limit"])
            response.headers["X-Quota-Remaining"] = str(
                quota_info["limit"] - quota_info["used"]
            )
            response.headers["X-Quota-Reset"] = quota_info["reset_at"]
            
            return response
        
        return await call_next(request)
    
    def _get_user_id(self, request: Request) -> str:
        """Extract user identifier from request.
        
        Args:
            request: HTTP request
            
        Returns:
            User identifier or IP address
        """
        if hasattr(request.state, "user"):
            return request.state.user.id
        
        client_host = request.client.host if request.client else "unknown"
        return f"ip:{client_host}"
