"""
Configuration loader for Code of Conquest.

Loads configuration from YAML files and environment variables,
providing typed access to all configuration values.
"""

import os
from dataclasses import dataclass, field
from typing import Dict, List, Optional
import yaml
from dotenv import load_dotenv


@dataclass
class AppConfig:
    """Application configuration."""
    name: str
    version: str
    environment: str
    debug: bool


@dataclass
class ServerConfig:
    """Server configuration."""
    host: str
    port: int
    workers: int


@dataclass
class RedisConfig:
    """Redis configuration."""
    host: str
    port: int
    db: int
    max_connections: int

    @property
    def url(self) -> str:
        """Generate Redis URL."""
        return f"redis://{self.host}:{self.port}/{self.db}"


@dataclass
class RQConfig:
    """RQ (Redis Queue) configuration."""
    queues: List[str]
    worker_timeout: int
    job_timeout: int


@dataclass
class AIModelConfig:
    """AI model configuration."""
    provider: str
    model: str
    max_tokens: int
    temperature: float


@dataclass
class AIConfig:
    """AI service configuration."""
    timeout: int
    max_retries: int
    cost_alert_threshold: float
    models: Dict[str, AIModelConfig] = field(default_factory=dict)


@dataclass
class RateLimitTier:
    """Rate limit configuration for a subscription tier."""
    requests_per_minute: int
    ai_calls_per_day: int
    custom_actions_per_day: int  # -1 for unlimited
    custom_action_char_limit: int


@dataclass
class RateLimitingConfig:
    """Rate limiting configuration."""
    enabled: bool
    storage_url: str
    tiers: Dict[str, RateLimitTier] = field(default_factory=dict)


@dataclass
class AuthConfig:
    """Authentication configuration."""
    cookie_name: str
    duration_normal: int
    duration_remember_me: int
    http_only: bool
    secure: bool
    same_site: str
    path: str
    password_min_length: int
    password_require_uppercase: bool
    password_require_lowercase: bool
    password_require_number: bool
    password_require_special: bool
    name_min_length: int
    name_max_length: int
    email_max_length: int


@dataclass
class SessionConfig:
    """Game session configuration."""
    timeout_minutes: int
    auto_save_interval: int
    min_players: int
    max_players_by_tier: Dict[str, int] = field(default_factory=dict)


@dataclass
class MarketplaceConfig:
    """Marketplace configuration."""
    auction_check_interval: int
    max_listings_by_tier: Dict[str, int] = field(default_factory=dict)


@dataclass
class CORSConfig:
    """CORS configuration."""
    origins: List[str]


@dataclass
class LoggingConfig:
    """Logging configuration."""
    level: str
    format: str
    handlers: List[str]
    file_path: str


@dataclass
class Config:
    """
    Main configuration container.

    Loads configuration from YAML file based on environment,
    with overrides from environment variables.
    """
    app: AppConfig
    server: ServerConfig
    redis: RedisConfig
    rq: RQConfig
    ai: AIConfig
    rate_limiting: RateLimitingConfig
    auth: AuthConfig
    session: SessionConfig
    marketplace: MarketplaceConfig
    cors: CORSConfig
    logging: LoggingConfig

    # Environment variables (loaded from .env)
    secret_key: str = ""
    appwrite_endpoint: str = ""
    appwrite_project_id: str = ""
    appwrite_api_key: str = ""
    appwrite_database_id: str = ""
    anthropic_api_key: str = ""
    replicate_api_token: str = ""

    @classmethod
    def load(cls, environment: Optional[str] = None) -> 'Config':
        """
        Load configuration from YAML file and environment variables.

        Args:
            environment: Environment name (development, production, etc.).
                        If not provided, uses FLASK_ENV from environment.

        Returns:
            Config: Loaded configuration object.

        Raises:
            FileNotFoundError: If config file not found.
            ValueError: If required environment variables missing.
        """
        # Load environment variables from .env file
        load_dotenv()

        # Determine environment
        if environment is None:
            environment = os.getenv('FLASK_ENV', 'development')

        # Load YAML configuration
        config_path = os.path.join('config', f'{environment}.yaml')

        if not os.path.exists(config_path):
            raise FileNotFoundError(
                f"Configuration file not found: {config_path}"
            )

        with open(config_path, 'r') as f:
            config_data = yaml.safe_load(f)

        # Parse configuration sections
        app_config = AppConfig(**config_data['app'])
        server_config = ServerConfig(**config_data['server'])
        redis_config = RedisConfig(**config_data['redis'])
        rq_config = RQConfig(**config_data['rq'])

        # Parse AI models
        ai_models = {}
        for tier, model_data in config_data['ai']['models'].items():
            ai_models[tier] = AIModelConfig(**model_data)

        ai_config = AIConfig(
            timeout=config_data['ai']['timeout'],
            max_retries=config_data['ai']['max_retries'],
            cost_alert_threshold=config_data['ai']['cost_alert_threshold'],
            models=ai_models
        )

        # Parse rate limiting tiers
        rate_limit_tiers = {}
        for tier, tier_data in config_data['rate_limiting']['tiers'].items():
            rate_limit_tiers[tier] = RateLimitTier(**tier_data)

        rate_limiting_config = RateLimitingConfig(
            enabled=config_data['rate_limiting']['enabled'],
            storage_url=config_data['rate_limiting']['storage_url'],
            tiers=rate_limit_tiers
        )

        auth_config = AuthConfig(**config_data['auth'])
        session_config = SessionConfig(**config_data['session'])
        marketplace_config = MarketplaceConfig(**config_data['marketplace'])
        cors_config = CORSConfig(**config_data['cors'])
        logging_config = LoggingConfig(**config_data['logging'])

        # Load environment variables (secrets)
        secret_key = os.getenv('SECRET_KEY')
        if not secret_key:
            raise ValueError("SECRET_KEY environment variable is required")

        appwrite_endpoint = os.getenv('APPWRITE_ENDPOINT', '')
        appwrite_project_id = os.getenv('APPWRITE_PROJECT_ID', '')
        appwrite_api_key = os.getenv('APPWRITE_API_KEY', '')
        appwrite_database_id = os.getenv('APPWRITE_DATABASE_ID', 'main')
        anthropic_api_key = os.getenv('ANTHROPIC_API_KEY', '')
        replicate_api_token = os.getenv('REPLICATE_API_TOKEN', '')

        # Create and return config object
        return cls(
            app=app_config,
            server=server_config,
            redis=redis_config,
            rq=rq_config,
            ai=ai_config,
            rate_limiting=rate_limiting_config,
            auth=auth_config,
            session=session_config,
            marketplace=marketplace_config,
            cors=cors_config,
            logging=logging_config,
            secret_key=secret_key,
            appwrite_endpoint=appwrite_endpoint,
            appwrite_project_id=appwrite_project_id,
            appwrite_api_key=appwrite_api_key,
            appwrite_database_id=appwrite_database_id,
            anthropic_api_key=anthropic_api_key,
            replicate_api_token=replicate_api_token
        )

    def validate(self) -> None:
        """
        Validate configuration values.

        Raises:
            ValueError: If configuration is invalid.
        """
        # Validate AI API keys if needed
        if self.app.environment == 'production':
            if not self.anthropic_api_key:
                raise ValueError(
                    "ANTHROPIC_API_KEY required in production environment"
                )
            if not self.appwrite_endpoint or not self.appwrite_project_id:
                raise ValueError(
                    "Appwrite configuration required in production environment"
                )

        # Validate logging level
        valid_log_levels = ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']
        if self.logging.level not in valid_log_levels:
            raise ValueError(
                f"Invalid log level: {self.logging.level}. "
                f"Must be one of {valid_log_levels}"
            )


# Global config instance (loaded lazily)
_config: Optional[Config] = None


def get_config(environment: Optional[str] = None) -> Config:
    """
    Get the global configuration instance.

    Args:
        environment: Optional environment override.

    Returns:
        Config: Configuration object.
    """
    global _config

    if _config is None or environment is not None:
        _config = Config.load(environment)
        _config.validate()

    return _config
