"""
BGF Agents Configuration System

YAML/ENV configuration loading with environment-based overrides.
"""

from __future__ import annotations

import os
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any

import yaml


@dataclass
class DatabaseConfig:
    """Database configuration."""
    url: str = "postgresql://localhost:5432/postgres"
    pool_size: int = 5
    pool_timeout: int = 30


@dataclass
class RedisConfig:
    """Redis configuration."""
    url: str = "redis://localhost:6379"
    max_connections: int = 10
    socket_timeout: float = 5.0


@dataclass
class LLMConfig:
    """LLM provider configuration."""
    provider: str = "anthropic"  # anthropic, openai, local
    model: str = "claude-sonnet-4-20250514"
    api_key: str | None = None
    base_url: str | None = None
    max_tokens: int = 4096
    temperature: float = 0.7


@dataclass
class AgentConfig:
    """Agent-specific configuration."""
    max_iterations: int = 10
    timeout: float = 300.0
    retry_attempts: int = 3
    retry_delay: float = 1.0


@dataclass
class NotificationConfig:
    """Notification configuration."""
    slack_webhook_url: str | None = None
    pagerduty_key: str | None = None
    email_smtp_host: str | None = None
    email_smtp_port: int = 587


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

    # Sub-configurations
    database: DatabaseConfig = field(default_factory=DatabaseConfig)
    redis: RedisConfig = field(default_factory=RedisConfig)
    llm: LLMConfig = field(default_factory=LLMConfig)
    agent: AgentConfig = field(default_factory=AgentConfig)
    notification: NotificationConfig = field(default_factory=NotificationConfig)

    # General settings
    environment: str = "development"
    debug: bool = False
    log_level: str = "INFO"

    @classmethod
    def from_yaml(cls, path: str | Path) -> "Config":
        """Load configuration from YAML file."""
        path = Path(path)
        if not path.exists():
            return cls()

        with open(path) as f:
            data = yaml.safe_load(f) or {}

        return cls._from_dict(data)

    @classmethod
    def from_env(cls) -> "Config":
        """Load configuration from environment variables."""
        config = cls()

        # Database
        if url := os.environ.get("DATABASE_URL") or os.environ.get("TIMESCALEDB_URL"):
            config.database.url = url
        if pool_size := os.environ.get("DATABASE_POOL_SIZE"):
            config.database.pool_size = int(pool_size)

        # Redis
        if url := os.environ.get("REDIS_URL"):
            config.redis.url = url

        # LLM
        if provider := os.environ.get("LLM_PROVIDER"):
            config.llm.provider = provider
        if model := os.environ.get("LLM_MODEL"):
            config.llm.model = model
        if api_key := os.environ.get("ANTHROPIC_API_KEY"):
            config.llm.api_key = api_key
            config.llm.provider = "anthropic"
        elif api_key := os.environ.get("OPENAI_API_KEY"):
            config.llm.api_key = api_key
            config.llm.provider = "openai"
        if base_url := os.environ.get("LLM_BASE_URL"):
            config.llm.base_url = base_url

        # Notifications
        if webhook := os.environ.get("SLACK_WEBHOOK_URL"):
            config.notification.slack_webhook_url = webhook
        if key := os.environ.get("PAGERDUTY_ROUTING_KEY"):
            config.notification.pagerduty_key = key

        # General
        if env := os.environ.get("ENVIRONMENT") or os.environ.get("ENV"):
            config.environment = env
        if debug := os.environ.get("DEBUG"):
            config.debug = debug.lower() in ("true", "1", "yes")
        if level := os.environ.get("LOG_LEVEL"):
            config.log_level = level.upper()

        return config

    @classmethod
    def load(cls, config_path: str | Path | None = None) -> "Config":
        """
        Load configuration with priority:
        1. Environment variables (highest priority)
        2. YAML file
        3. Defaults (lowest priority)
        """
        # Start with defaults
        if config_path:
            config = cls.from_yaml(config_path)
        else:
            # Try default locations
            for path in ["bgf-agents.yaml", "config.yaml", "config/bgf-agents.yaml"]:
                if Path(path).exists():
                    config = cls.from_yaml(path)
                    break
            else:
                config = cls()

        # Override with environment variables
        env_config = cls.from_env()
        config = cls._merge(config, env_config)

        return config

    @classmethod
    def _from_dict(cls, data: dict[str, Any]) -> "Config":
        """Create config from dictionary."""
        config = cls()

        if db := data.get("database"):
            config.database = DatabaseConfig(
                url=db.get("url", config.database.url),
                pool_size=db.get("pool_size", config.database.pool_size),
                pool_timeout=db.get("pool_timeout", config.database.pool_timeout),
            )

        if redis := data.get("redis"):
            config.redis = RedisConfig(
                url=redis.get("url", config.redis.url),
                max_connections=redis.get("max_connections", config.redis.max_connections),
                socket_timeout=redis.get("socket_timeout", config.redis.socket_timeout),
            )

        if llm := data.get("llm"):
            config.llm = LLMConfig(
                provider=llm.get("provider", config.llm.provider),
                model=llm.get("model", config.llm.model),
                api_key=llm.get("api_key", config.llm.api_key),
                base_url=llm.get("base_url", config.llm.base_url),
                max_tokens=llm.get("max_tokens", config.llm.max_tokens),
                temperature=llm.get("temperature", config.llm.temperature),
            )

        if agent := data.get("agent"):
            config.agent = AgentConfig(
                max_iterations=agent.get("max_iterations", config.agent.max_iterations),
                timeout=agent.get("timeout", config.agent.timeout),
                retry_attempts=agent.get("retry_attempts", config.agent.retry_attempts),
                retry_delay=agent.get("retry_delay", config.agent.retry_delay),
            )

        if notif := data.get("notification"):
            config.notification = NotificationConfig(
                slack_webhook_url=notif.get("slack_webhook_url"),
                pagerduty_key=notif.get("pagerduty_key"),
                email_smtp_host=notif.get("email_smtp_host"),
                email_smtp_port=notif.get("email_smtp_port", 587),
            )

        config.environment = data.get("environment", config.environment)
        config.debug = data.get("debug", config.debug)
        config.log_level = data.get("log_level", config.log_level)

        return config

    @classmethod
    def _merge(cls, base: "Config", override: "Config") -> "Config":
        """Merge two configs, override takes priority for non-default values."""
        # Only override if the value was explicitly set (different from default)
        default = cls()

        if override.database.url != default.database.url:
            base.database.url = override.database.url
        if override.redis.url != default.redis.url:
            base.redis.url = override.redis.url
        if override.llm.provider != default.llm.provider:
            base.llm.provider = override.llm.provider
        if override.llm.model != default.llm.model:
            base.llm.model = override.llm.model
        if override.llm.api_key:
            base.llm.api_key = override.llm.api_key
        if override.llm.base_url:
            base.llm.base_url = override.llm.base_url
        if override.notification.slack_webhook_url:
            base.notification.slack_webhook_url = override.notification.slack_webhook_url
        if override.notification.pagerduty_key:
            base.notification.pagerduty_key = override.notification.pagerduty_key
        if override.environment != default.environment:
            base.environment = override.environment
        if override.debug != default.debug:
            base.debug = override.debug
        if override.log_level != default.log_level:
            base.log_level = override.log_level

        return base

    def to_dict(self) -> dict[str, Any]:
        """Convert config to dictionary."""
        return {
            "environment": self.environment,
            "debug": self.debug,
            "log_level": self.log_level,
            "database": {
                "url": self.database.url,
                "pool_size": self.database.pool_size,
                "pool_timeout": self.database.pool_timeout,
            },
            "redis": {
                "url": self.redis.url,
                "max_connections": self.redis.max_connections,
                "socket_timeout": self.redis.socket_timeout,
            },
            "llm": {
                "provider": self.llm.provider,
                "model": self.llm.model,
                "base_url": self.llm.base_url,
                "max_tokens": self.llm.max_tokens,
                "temperature": self.llm.temperature,
            },
            "agent": {
                "max_iterations": self.agent.max_iterations,
                "timeout": self.agent.timeout,
                "retry_attempts": self.agent.retry_attempts,
                "retry_delay": self.agent.retry_delay,
            },
            "notification": {
                "slack_webhook_url": self.notification.slack_webhook_url,
                "pagerduty_key": self.notification.pagerduty_key,
            },
        }


# Global config instance
_config: Config | None = None


def get_config() -> Config:
    """Get global configuration instance."""
    global _config
    if _config is None:
        _config = Config.load()
    return _config


def set_config(config: Config) -> None:
    """Set global configuration instance."""
    global _config
    _config = config


def reload_config(config_path: str | Path | None = None) -> Config:
    """Reload configuration."""
    global _config
    _config = Config.load(config_path)
    return _config


__all__ = [
    "Config",
    "DatabaseConfig",
    "RedisConfig",
    "LLMConfig",
    "AgentConfig",
    "NotificationConfig",
    "get_config",
    "set_config",
    "reload_config",
]
