import re
from dataclasses import dataclass
from re import Pattern

from auditor.core.base import Validator
from auditor.validators.llm import (
    AnthropicValidator,
    DeepSeekValidator,
    GeminiValidator,
    GroqValidator,
    HuggingFaceValidator,
    OpenAIValidator,
    OpenRouterValidator,
    PerplexityValidator,
    ReplicateValidator,
    RunwayMLValidator,
)


@dataclass(frozen=True)
class ProviderConfig:
    """Configuration for an API provider."""
    pattern: Pattern[str]
    validator: Validator


class ValidatorFactory:
    """
    Factory class that manages validator registration and lookup.

    This class follows the Factory pattern and provides a centralized
    registry for API key patterns and their validators.  New providers
    can be added by calling the ``register()`` method.

    **v2.0 changes:**
    - All patterns now use greedy quantifiers with negative lookaheads
      ``(?=[^...] | $)`` to extract the complete key without truncation.
    - Three new providers: OpenRouter, Replicate, RunwayML.
    - More specific prefixes (``sk-ant-``, ``sk-or-v1-``, ``gsk_``, etc.)
      are registered before the generic OpenAI ``sk-`` catch-all.

    Attributes:
        _registry: Internal storage for provider configurations

    Example:
        ValidatorFactory.register(
            "custom",
            r"custom-[a-z0-9]{32}",
            CustomValidator()
        )
    """

    _registry: dict[str, ProviderConfig] = {}

    @classmethod
    def _initialize_defaults(cls) -> None:
        """Initialize the factory with default provider configurations.

        Provider order: more specific prefixes first so that, when
        iterating the registry, a key like ``sk-ant-xxx`` is matched
        by Anthropic before the generic OpenAI pattern can attempt it.
        With provider-scoped caching (``tested_pairs``) this does not
        affect correctness, but it reduces unnecessary validation
        requests.
        """
        defaults = {
            # --- Specific prefixes first ---
            "anthropic": (
                r"sk-ant-[A-Za-z0-9_-]{32,128}(?=[^A-Za-z0-9_-]|$)",
                AnthropicValidator(),
            ),
            "openrouter": (
                r"sk-or-v1-[A-Za-z0-9]{64}(?=[^A-Za-z0-9]|$)",
                OpenRouterValidator(),
            ),
            "deepseek": (
                r"sk-[A-Za-z0-9]{32}(?=[^A-Za-z0-9]|$)",
                DeepSeekValidator(),
            ),
            "groq": (
                r"gsk_[A-Za-z0-9]{32,64}(?=[^A-Za-z0-9]|$)",
                GroqValidator(),
            ),
            "perplexity": (
                r"pplx-[A-Za-z0-9]{16,64}(?=[^A-Za-z0-9]|$)",
                PerplexityValidator(),
            ),
            "huggingface": (
                r"hf_[A-Za-z0-9]{32,64}(?=[^A-Za-z0-9]|$)",
                HuggingFaceValidator(),
            ),
            "replicate": (
                r"r8_[A-Za-z0-9]{37}(?=[^A-Za-z0-9]|$)",
                ReplicateValidator(),
            ),
            "runwayml": (
                r"key_[0-9A-Fa-f]{128}(?=[^0-9A-Fa-f]|$)",
                RunwayMLValidator(),
            ),
            "google_ai": (
                r"AIza[A-Za-z0-9_-]{35}(?=[^A-Za-z0-9_-]|$)",
                GeminiValidator(),
            ),
            # --- Generic sk- catch-all last ---
            "openai": (
                r"sk-(?:proj-|svcacct-|None-)?[A-Za-z0-9_-]{32,120}(?=[^A-Za-z0-9_-]|$)",
                OpenAIValidator(),
            ),
        }

        for name, (pattern, validator) in defaults.items():
            cls._registry[name] = ProviderConfig(
                pattern=re.compile(pattern),
                validator=validator,
            )

    @classmethod
    def register(cls, name: str, pattern: str, validator: Validator) -> None:
        """
        Register a new provider configuration.

        Args:
            name: Unique identifier for the provider
            pattern: Regex pattern to match API keys
            validator: Validator instance to verify keys

        Raises:
            ValueError: If name is already registered
        """
        if name in cls._registry:
            raise ValueError(f"Provider '{name}' is already registered")

        cls._registry[name] = ProviderConfig(
            pattern=re.compile(pattern),
            validator=validator,
        )

    @classmethod
    def unregister(cls, name: str) -> bool:
        """
        Remove a provider from the registry.

        Args:
            name: The provider name to remove

        Returns:
            True if removed, False if not found
        """
        if name in cls._registry:
            del cls._registry[name]
            return True
        return False

    @classmethod
    def get(cls, name: str) -> ProviderConfig | None:
        """Get a specific provider configuration."""
        cls._ensure_initialized()
        return cls._registry.get(name)

    @classmethod
    def get_all(cls) -> dict[str, ProviderConfig]:
        """Get all registered provider configurations."""
        cls._ensure_initialized()
        return cls._registry.copy()

    @classmethod
    def get_config(cls) -> dict[str, tuple[str, Validator]]:
        """
        Get configuration in legacy format for backward compatibility.

        Returns:
            Dict mapping provider names to (pattern_str, validator) tuples
        """
        cls._ensure_initialized()
        return {
            name: (config.pattern.pattern, config.validator)
            for name, config in cls._registry.items()
        }

    @classmethod
    def _ensure_initialized(cls) -> None:
        """Ensure default providers are registered."""
        if not cls._registry:
            cls._initialize_defaults()

    @classmethod
    def list_providers(cls) -> list[str]:
        """Get a list of all registered provider names."""
        cls._ensure_initialized()
        return list(cls._registry.keys())
