"""Configuration management for Reviewate workflow"""

from __future__ import annotations

import os
from enum import Enum
from typing import Annotated

from pydantic import BaseModel, Field, PrivateAttr

from .agents import AgentConfig
from .config_file import get_config_model, get_config_url, load_config_file
from .errors import ReviewateError


class Platform(str, Enum):
    """Supported Git platforms"""

    GITHUB = "github"
    GITLAB = "gitlab"

    def default_url(self) -> str:
        """Get the default API URL for the platform"""
        return {
            Platform.GITHUB: "https://api.github.com",
            Platform.GITLAB: "https://gitlab.com/api/v4",
        }[self]


class PlatformConfig(BaseModel):
    """Platform configuration"""

    name: Annotated[Platform | None, Field(description="Platform name (github/gitlab)")] = None
    url: Annotated[str | None, Field(description="Platform API URL (optional)")] = None

    def get_token(self) -> str:
        """Get the token for the platform from environment variables"""
        if self.name == Platform.GITHUB:
            if token := os.getenv("GITHUB_API_TOKEN"):
                return token
            raise ReviewateError("GitHub token not found. Set GITHUB_API_TOKEN env var.")

        if self.name == Platform.GITLAB:
            if token := os.getenv("GITLAB_API_TOKEN"):
                return token
            raise ReviewateError("GitLab token not found. Set GITLAB_API_TOKEN env var.")

        raise ReviewateError(f"Unknown platform: {self.name}")


# =============================================================================
# Agent Tier Configuration
# =============================================================================
# Two-tier system for agent model configuration:
# - REVIEW tier: Review agents (review, fact_checker, triage)
# - UTILITY tier: Supporting agents (parsing, formatting, deduplication)


def parse_model_string(model_string: str) -> tuple[str, str]:
    """Parse a 'provider/model' string into (provider, model) tuple.

    Args:
        model_string: String in format 'provider/model' (e.g., 'anthropic/claude-sonnet-4')

    Returns:
        Tuple of (provider, model)

    Raises:
        ReviewateError: If the format is invalid
    """
    if "/" not in model_string:
        raise ReviewateError(
            f"Invalid model format: '{model_string}'. "
            "Expected format: 'provider/model' (e.g., 'anthropic/claude-sonnet-4')"
        )
    parts = model_string.split("/", 1)
    return parts[0].strip(), parts[1].strip()


class AgentTier(str, Enum):
    """Agent tier for model selection"""

    REVIEW = "review"  # Specialized review agents - strongest model
    UTILITY = "utility"  # Supporting agents - fast, cheap model


# Agent-to-tier mapping
AGENT_TIERS: dict[str, AgentTier] = {
    # Review tier - agents that analyze code and need strong reasoning
    "triage_agent": AgentTier.REVIEW,
    "review_agent": AgentTier.REVIEW,  # 5 parallel instances in ensemble
    "fact_checker": AgentTier.REVIEW,
    # Utility tier - supporting agents for parsing, formatting, synthesis
    "output_parser": AgentTier.UTILITY,
    "summary_parser": AgentTier.UTILITY,
    "style_agent": AgentTier.UTILITY,
    "duplicate_detector": AgentTier.UTILITY,
    "issue_explorer": AgentTier.UTILITY,
    "summarizer": AgentTier.UTILITY,
}


def get_agent_default(agent_name: str) -> dict[str, str]:
    """Get default provider/model for an agent based on its tier.

    Requires tier-level env vars to be set:
    - REVIEW_AGENT_PROVIDER, REVIEW_AGENT_MODEL
    - UTILITY_AGENT_PROVIDER, UTILITY_AGENT_MODEL

    Args:
        agent_name: Name of the agent

    Returns:
        Dict with 'provider' and 'model' keys

    Raises:
        ReviewateError: If required env vars are not set
    """
    tier = AGENT_TIERS.get(agent_name, AgentTier.UTILITY)
    tier_prefix = tier.value.upper()

    tier_provider = os.getenv(f"{tier_prefix}_AGENT_PROVIDER")
    tier_model = os.getenv(f"{tier_prefix}_AGENT_MODEL")

    if not tier_provider or not tier_model:
        missing = []
        if not tier_provider:
            missing.append(f"{tier_prefix}_AGENT_PROVIDER")
        if not tier_model:
            missing.append(f"{tier_prefix}_AGENT_MODEL")
        raise ReviewateError(
            f"Model configuration required. Set env vars: {', '.join(missing)}. "
            f"Example: {tier_prefix}_AGENT_PROVIDER=gemini {tier_prefix}_AGENT_MODEL=gemini-2.5-flash"
        )

    return {
        "provider": tier_provider.strip('"').strip("'"),
        "model": tier_model.strip('"').strip("'"),
    }


class WorkflowAgentConfig(BaseModel):
    """Individual agent configuration"""

    provider: str | None = None
    model: str | None = None


class AgentsConfig(BaseModel):
    """Agent-specific configurations using dict-based lookup."""

    agents: dict[str, WorkflowAgentConfig] = Field(default_factory=dict)

    def get(self, agent_name: str) -> WorkflowAgentConfig:
        """Get configuration for a specific agent.

        Returns the agent config if it exists, otherwise an empty config.
        This allows AGENT_DEFAULTS to provide fallback values.
        """
        return self.agents.get(agent_name, WorkflowAgentConfig())


class Config(BaseModel):
    """Main configuration structure"""

    platform_config: PlatformConfig = PlatformConfig()
    agent_config: AgentsConfig = AgentsConfig()
    # CLI model overrides (format: "provider/model")
    review_model_override: str | None = None
    utility_model_override: str | None = None
    # Config file data (loaded from ~/.reviewate/config.toml)
    _file_config: dict[str, object] | None = PrivateAttr(default=None)

    @classmethod
    def from_env(
        cls,
        review_model: str | None = None,
        utility_model: str | None = None,
        file_config: dict[str, object] | None = None,
    ) -> Config:
        """Load configuration from environment variables with optional CLI overrides.

        Platform config is loaded from PLATFORM_NAME and PLATFORM_URL.
        Agent config uses tier-based defaults with overrides in this order:
        1. CLI args (--review-model, --utility-model) - highest priority
        2. Environment variables (REVIEW_AGENT_MODEL, etc.)
        3. Config file (~/.reviewate/config.toml)

        Args:
            review_model: CLI override for review tier (format: 'provider/model')
            utility_model: CLI override for utility tier (format: 'provider/model')
            file_config: Pre-loaded config file data (or None to auto-load)
        """
        # Parse platform configuration
        platform_name_str = os.getenv("PLATFORM_NAME")
        platform_name = None
        if platform_name_str:
            try:
                platform_name = Platform(platform_name_str.lower())
            except ValueError:
                pass

        platform_url = os.getenv("PLATFORM_URL")
        platform_config = PlatformConfig(name=platform_name, url=platform_url)

        # Agent config is now tier-based - no per-agent env vars needed
        agent_config = AgentsConfig()

        # Auto-load config file if not provided
        if file_config is None:
            file_config = load_config_file()

        config = cls(
            platform_config=platform_config,
            agent_config=agent_config,
            review_model_override=review_model,
            utility_model_override=utility_model,
        )
        config._file_config = file_config
        return config

    def get_api_key(self, provider: str, tier: AgentTier | None = None) -> str:
        """Get API key for a specific provider.

        For OpenAI provider, checks tier-specific key first:
        - REVIEW_OPENAI_KEY for review tier
        - UTILITY_OPENAI_KEY for utility tier
        - Falls back to OPENAI_API_KEY
        """
        # Check tier-specific OpenAI key first
        if provider.lower() == "openai" and tier:
            tier_key = os.getenv(f"{tier.value.upper()}_OPENAI_KEY")
            if tier_key:
                return tier_key.strip('"').strip("'")

        env_var_map = {
            "gemini": "GEMINI_API_KEY",
            "openai": "OPENAI_API_KEY",
            "anthropic": "ANTHROPIC_API_KEY",
            "claude": "ANTHROPIC_API_KEY",
            "openrouter": "OPENROUTER_API_KEY",
            "deepseek": "DEEPSEEK_API_KEY",
            "numberly": "NUMBERLY_API_KEY",
        }

        if provider.lower() in env_var_map:
            if api_key := os.getenv(env_var_map[provider.lower()]):
                return api_key.strip('"').strip("'")

        raise ReviewateError(
            f"API key for provider '{provider}' not found. "
            f"Set {env_var_map.get(provider.lower(), provider.upper() + '_API_KEY')} env var."
        )

    def get_provider_url(self, provider: str, tier: AgentTier | None = None) -> str | None:
        """Get custom URL for a specific provider.

        For OpenAI provider, checks tier-specific URL first:
        - REVIEW_OPENAI_URL for review tier
        - UTILITY_OPENAI_URL for utility tier
        - Falls back to OPENAI_URL
        """
        if provider.lower() == "openai" and tier:
            tier_url = os.getenv(f"{tier.value.upper()}_OPENAI_URL")
            if tier_url:
                return tier_url.strip('"').strip("'")
        env_url = os.getenv(f"{provider.upper()}_URL")
        if env_url:
            return env_url

        # Fall back to config file
        if self._file_config:
            return get_config_url(provider, self._file_config)
        return None

    def get_agent_model(self, agent_name: str) -> str:
        """Get the model for a specific agent based on its tier.

        Priority: CLI override > env var > config file > error
        """
        tier = AGENT_TIERS.get(agent_name, AgentTier.UTILITY)

        # Check CLI override first
        if tier == AgentTier.REVIEW and self.review_model_override:
            _, model = parse_model_string(self.review_model_override)
            return model
        if tier == AgentTier.UTILITY and self.utility_model_override:
            _, model = parse_model_string(self.utility_model_override)
            return model

        # Try env vars
        try:
            return get_agent_default(agent_name)["model"]
        except ReviewateError:
            pass

        # Fall back to config file
        if self._file_config:
            model_str = get_config_model(tier.value, self._file_config)
            if model_str:
                _, model = parse_model_string(model_str)
                return model

        # Nothing found — raise original error
        return get_agent_default(agent_name)["model"]

    def get_agent_provider(self, agent_name: str) -> str:
        """Get the provider for a specific agent based on its tier.

        Priority: CLI override > env var > config file > error
        """
        tier = AGENT_TIERS.get(agent_name, AgentTier.UTILITY)

        # Check CLI override first
        if tier == AgentTier.REVIEW and self.review_model_override:
            provider, _ = parse_model_string(self.review_model_override)
            return provider
        if tier == AgentTier.UTILITY and self.utility_model_override:
            provider, _ = parse_model_string(self.utility_model_override)
            return provider

        # Try env vars
        try:
            return get_agent_default(agent_name)["provider"]
        except ReviewateError:
            pass

        # Fall back to config file
        if self._file_config:
            model_str = get_config_model(tier.value, self._file_config)
            if model_str:
                provider, _ = parse_model_string(model_str)
                return provider

        # Nothing found — raise original error
        return get_agent_default(agent_name)["provider"]

    @property
    def github_api_token(self) -> str | None:
        """Get GitHub API token from environment"""
        token = os.getenv("GITHUB_API_TOKEN")
        if token:
            return token.strip('"').strip("'")
        return None

    @property
    def gitlab_api_token(self) -> str | None:
        """Get GitLab API token from environment"""
        token = os.getenv("GITLAB_API_TOKEN")
        if token:
            return token.strip('"').strip("'")
        return None

    @property
    def github_api_url(self) -> str:
        """Get GitHub API URL (with fallback to default)"""
        return os.getenv("GITHUB_API_URL", "https://api.github.com")

    @property
    def gitlab_api_url(self) -> str:
        """Get GitLab API URL (with fallback to default)"""
        return os.getenv("GITLAB_API_URL", "https://gitlab.com/api/v4")

    def create_agent_config(self, agent_name: str) -> AgentConfig:
        """Create an AgentConfig instance for a specific agent.

        Uses tier-based defaults with env var overrides.
        Any provider/model supported by LiteLLM is accepted.

        For OpenAI provider, supports tier-specific URLs and keys:
        - REVIEW_OPENAI_URL, REVIEW_OPENAI_KEY for review tier
        - UTILITY_OPENAI_URL, UTILITY_OPENAI_KEY for utility tier
        """
        tier = AGENT_TIERS.get(agent_name, AgentTier.UTILITY)
        provider = self.get_agent_provider(agent_name)
        model = self.get_agent_model(agent_name)
        api_key = self.get_api_key(provider, tier)
        api_base = self.get_provider_url(provider, tier)
        return AgentConfig(
            provider=provider,
            model=model,
            api_key=api_key,
            api_base=api_base,
            tier=tier.value,
        )
