from dataclasses import dataclass
from enum import Enum


class Algorithm(str, Enum):
    FIXED_WINDOW = "fixed_window"
    SLIDING_WINDOW = "sliding_window"
    TOKEN_BUCKET = "token_bucket"
    LEAKY_BUCKET = "leaky_bucket"


class Scope(str, Enum):
    ENDPOINT = "endpoint"
    ROUTER = "router"
    GLOBAL = "global"


@dataclass(frozen=True)
class RateLimitRule:
    """
    Defines a rate limit rule.

    Attributes:
        key: Identity key type ("ip", "user_id", "api_key", or custom)
        limit: Maximum number of requests allowed
        window: Time window in seconds
        algorithm: Algorithm to use
        scope: Scope of the rule
        priority: Rule priority (higher = checked first)
    """

    key: str
    limit: int
    window: int
    algorithm: Algorithm = Algorithm.SLIDING_WINDOW
    scope: Scope = Scope.ENDPOINT
    priority: int = 0

    def __post_init__(self) -> None:
        """Validate rule parameters."""
        if self.limit <= 0:
            raise ValueError("limit must be positive")
        if self.window <= 0:
            raise ValueError("window must be positive")

    @classmethod
    def from_string(cls, rule_str: str, key: str = "ip") -> "RateLimitRule":
        """
        Parse rate limit rule from string format.

        Examples:
            "100/minute" -> RateLimitRule(limit=100, window=60)
            "10/second" -> RateLimitRule(limit=10, window=1)
            "5/hour" -> RateLimitRule(limit=5, window=3600)

        Args:
            rule_str: Rule string in format "limit/unit"
            key: Identity key type

        Returns:
            RateLimitRule instance
        """
        parts = rule_str.split("/")
        if len(parts) != 2:
            raise ValueError(f"Invalid rule format: {rule_str}. Expected 'limit/unit'")

        try:
            limit = int(parts[0])
        except ValueError:
            raise ValueError(f"Invalid limit: {parts[0]}")

        unit = parts[1].lower()
        unit_map = {
            "second": 1,
            "minute": 60,
            "hour": 3600,
            "day": 86400,
        }

        if unit not in unit_map:
            raise ValueError(f"Invalid unit: {unit}. Must be one of: {list(unit_map.keys())}")

        window = unit_map[unit]

        return cls(key=key, limit=limit, window=window)
