"""Provider Registry — metadata store for all known providers.

Stores:
- provider name, status, free tier
- registration, card requirement
- API, available models, rate limits, token limits
- conditions, expiration, last checked, source, trust
- Free Score (optional): free, no-card, no-expiration, API availability,
  limit size, stability, official source, local availability
"""

from __future__ import annotations

import logging
import time
from dataclasses import dataclass, field
from enum import Enum
from typing import Any

logger = logging.getLogger(__name__)


class ProviderStatus(str, Enum):
    """Operational status of a provider."""
    ACTIVE = "active"
    DEGRADED = "degraded"
    INACTIVE = "inactive"
    BLOCKED = "blocked"
    UNKNOWN = "unknown"


class ProviderTrust(str, Enum):
    """Trust level of a provider."""
    OFFICIAL = "official"
    VERIFIED = "verified"
    COMMUNITY = "community"
    UNOFFICIAL = "unofficial"
    UNKNOWN = "unknown"


class ProviderFreeScore(str, Enum):
    """Free tier scoring classification."""
    EXCELLENT = "excellent"
    GOOD = "good"
    FAIR = "fair"
    POOR = "poor"
    NONE = "none"


@dataclass
class ProviderRecord:
    """Complete provider metadata record."""

    provider: str
    status: ProviderStatus = ProviderStatus.UNKNOWN

    # Registration
    free_tier: bool = False
    registration_required: bool = False
    card_required: bool = False

    # API
    api_base: str | None = None
    api_key_required: bool = True
    api_available: bool = False
    available_models: list[str] = field(default_factory=list)

    # Limits
    rate_limit: str | None = None
    rate_limit_value: int | None = None
    token_limit: int | None = None
    max_context_tokens: int | None = None

    # Conditions
    conditions: str | None = None
    expiration: float | None = None
    last_checked: float = field(default_factory=time.time)

    # Source and trust
    source: str | None = None
    trust: ProviderTrust = ProviderTrust.UNKNOWN

    # Free score components
    free: bool = False
    no_card: bool = False
    no_expiration: bool = False
    api_availability: bool = False
    limit_size: int | None = None
    stability: float = 0.0
    official_source: bool = False
    local_availability: bool = False

    # Computed
    free_score: ProviderFreeScore = ProviderFreeScore.NONE

    # Extra metadata
    metadata: dict[str, Any] = field(default_factory=dict)

    @property
    def is_free(self) -> bool:
        return self.free_tier or self.free

    @property
    def is_local(self) -> bool:
        return self.local_availability

    def update(self, **kwargs: Any) -> None:
        """Update fields in-place."""
        for key, value in kwargs.items():
            if hasattr(self, key):
                setattr(self, key, value)
        self.last_checked = time.time()

    def compute_free_score(self) -> ProviderFreeScore:
        """Compute free tier score based on conditions.

        Penalties:
        - card required
        - trial only
        - low limits
        - academic-only
        - unofficial source
        - unstable service
        """
        penalties = 0

        if self.card_required:
            penalties += 2
        if self.registration_required:
            penalties += 1
        if self.limit_size is not None and self.limit_size < 1000:
            penalties += 2
        if self.trust == ProviderTrust.UNOFFICIAL:
            penalties += 2
        if self.trust == ProviderTrust.COMMUNITY:
            penalties += 1
        if self.stability < 0.5:
            penalties += 2
        elif self.stability < 0.8:
            penalties += 1

        if penalties >= 5:
            return ProviderFreeScore.POOR
        elif penalties >= 3:
            return ProviderFreeScore.FAIR
        elif penalties >= 1:
            return ProviderFreeScore.GOOD
        elif self.free:
            return ProviderFreeScore.EXCELLENT
        return ProviderFreeScore.NONE


class ProviderRegistry:
    """Registry for provider metadata.

    Provides CRUD operations, health tracking, and free score computation.
    """

    def __init__(self) -> None:
        self._providers: dict[str, ProviderRecord] = {}
        self._by_status: dict[ProviderStatus, set[str]] = {
            s: set() for s in ProviderStatus
        }
        self._by_trust: dict[ProviderTrust, set[str]] = {
            t: set() for t in ProviderTrust
        }

    def register(self, provider: ProviderRecord) -> ProviderRecord:
        """Register a provider and return its record."""
        self._providers[provider.provider] = provider
        self._by_status[provider.status].add(provider.provider)
        self._by_trust[provider.trust].add(provider.provider)
        logger.info("Registered provider %s", provider.provider)
        return provider

    def unregister(self, provider_name: str) -> bool:
        """Remove a provider. Returns True if existed."""
        if provider_name not in self._providers:
            return False
        provider = self._providers.pop(provider_name)
        self._by_status[provider.status].discard(provider_name)
        self._by_trust[provider.trust].discard(provider_name)
        logger.info("Unregistered provider %s", provider_name)
        return True

    def get(self, provider_name: str) -> ProviderRecord | None:
        """Retrieve provider by name."""
        return self._providers.get(provider_name)

    def list_by_status(self, status: ProviderStatus) -> list[ProviderRecord]:
        """List providers with a given status."""
        return [self._providers[p] for p in self._by_status.get(status, set()) if p in self._providers]

    def list_by_trust(self, trust: ProviderTrust) -> list[ProviderRecord]:
        """List providers with a given trust level."""
        return [self._providers[p] for p in self._by_trust.get(trust, set()) if p in self._providers]

    def list_active(self) -> list[ProviderRecord]:
        """List all active providers."""
        return [self._providers[p] for p in self._by_status.get(ProviderStatus.ACTIVE, set()) if p in self._providers]

    def list_local(self) -> list[ProviderRecord]:
        """List all local providers."""
        return [p for p in self._providers.values() if p.local_availability]

    def list_free(self) -> list[ProviderRecord]:
        """List all free providers."""
        return [p for p in self._providers.values() if p.is_free]

    def list_all(self) -> list[ProviderRecord]:
        """List all registered providers."""
        return list(self._providers.values())

    def search(
        self,
        *,
        status: ProviderStatus | None = None,
        trust: ProviderTrust | None = None,
        local_only: bool | None = None,
        free_only: bool | None = None,
    ) -> list[ProviderRecord]:
        """Search providers with filters."""
        results = list(self._providers.values())

        if status:
            results = [p for p in results if p.status == status]
        if trust:
            results = [p for p in results if p.trust == trust]
        if local_only is not None:
            results = [p for p in results if p.local_availability == local_only]
        if free_only is not None:
            results = [p for p in results if p.is_free == free_only]

        return results

    def update(self, provider_name: str, **kwargs: Any) -> ProviderRecord | None:
        """Update provider metadata. Returns updated record or None."""
        provider = self._providers.get(provider_name)
        if provider is None:
            return None
        old_status = provider.status
        old_trust = provider.trust
        provider.update(**kwargs)

        if old_status != provider.status:
            self._by_status[old_status].discard(provider_name)
            self._by_status[provider.status].add(provider_name)
        if old_trust != provider.trust:
            self._by_trust[old_trust].discard(provider_name)
            self._by_trust[provider.trust].add(provider_name)

        return provider

    def compute_free_scores(self) -> dict[str, ProviderFreeScore]:
        """Compute free scores for all providers."""
        scores: dict[str, ProviderFreeScore] = {}
        for name, provider in self._providers.items():
            score = provider.compute_free_score()
            provider.free_score = score
            scores[name] = score
        return scores

    def count(self) -> int:
        return len(self._providers)

    def has(self, provider_name: str) -> bool:
        return provider_name in self._providers
