"""
Kafka configuration management module.

This module handles loading, validating, and managing Kafka configuration
from YAML files with proper validation and error handling.
"""

import os
import yaml
from typing import Dict, Any, Optional, List
from dataclasses import dataclass, field
from pathlib import Path


@dataclass
class ProducerConfig:
    """Producer configuration."""
    bootstrap_servers: str = "localhost:9092"
    acks: str = "all"
    retries: int = 3
    batch_size: int = 16384
    linger_ms: int = 5
    compression_type: str = "none"

    @classmethod
    def from_dict(cls, data: Dict[str, Any], bootstrap_servers: str = None) -> 'ProducerConfig':
        # Use provided bootstrap_servers or fall back to default in data
        if bootstrap_servers and 'bootstrap_servers' not in data:
            data['bootstrap_servers'] = bootstrap_servers
        return cls(**data)


@dataclass
class ConsumerConfig:
    """Consumer configuration."""
    bootstrap_servers: str = "localhost:9092"
    group_id: str = "mcp-consumer"
    auto_offset_reset: str = "earliest"
    enable_auto_commit: bool = True
    auto_commit_interval_ms: int = 1000
    session_timeout_ms: int = 30000
    heartbeat_interval_ms: int = 3000
    max_poll_records: int = 500
    max_poll_interval_ms: int = 300000

    @classmethod
    def from_dict(cls, data: Dict[str, Any], bootstrap_servers: str = None) -> 'ConsumerConfig':
        # Use provided bootstrap_servers or fall back to default in data
        if bootstrap_servers and 'bootstrap_servers' not in data:
            data['bootstrap_servers'] = bootstrap_servers
        return cls(**data)


@dataclass
class TopicConfig:
    """Topic configuration."""
    partitions: int = 3
    replication_factor: int = 1
    configs: Dict[str, str] = field(default_factory=dict)

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'TopicConfig':
        return cls(**data)


@dataclass
class SecurityConfig:
    """Security configuration."""
    protocol: str = "PLAINTEXT"
    sasl_mechanism: Optional[str] = None
    sasl_username: Optional[str] = None
    sasl_password: Optional[str] = None
    ssl_ca_location: Optional[str] = None
    ssl_certfile: Optional[str] = None
    ssl_keyfile: Optional[str] = None

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'SecurityConfig':
        return cls(**data)


@dataclass
class KafkaConfig:
    """Main Kafka configuration."""
    bootstrap_servers: str = "localhost:9092"
    security: SecurityConfig = field(default_factory=SecurityConfig)
    topics: Dict[str, TopicConfig] = field(default_factory=dict)
    consumers: Dict[str, ConsumerConfig] = field(default_factory=dict)
    producers: Dict[str, ProducerConfig] = field(default_factory=dict)

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'KafkaConfig':
        """Create KafkaConfig from dictionary."""
        config = cls()

        # Basic config
        if 'bootstrap_servers' in data:
            config.bootstrap_servers = data['bootstrap_servers']

        # Security config
        if 'security' in data:
            config.security = SecurityConfig.from_dict(data['security'])

        # Topics config
        if 'topics' in data:
            for name, topic_data in data['topics'].items():
                config.topics[name] = TopicConfig.from_dict(topic_data)

        # Consumers config - pass bootstrap_servers to inherit
        if 'consumers' in data:
            for name, consumer_data in data['consumers'].items():
                config.consumers[name] = ConsumerConfig.from_dict(
                    consumer_data,
                    bootstrap_servers=config.bootstrap_servers
                )

        # Producers config - pass bootstrap_servers to inherit
        if 'producers' in data:
            for name, producer_data in data['producers'].items():
                config.producers[name] = ProducerConfig.from_dict(
                    producer_data,
                    bootstrap_servers=config.bootstrap_servers
                )

        return config

    def get_default_producer_config(self) -> ProducerConfig:
        """Get default producer configuration."""
        if 'default' in self.producers:
            return self.producers['default']

        # Create default config if not exists
        default_producer = ProducerConfig(
            bootstrap_servers=self.bootstrap_servers
        )
        self.producers['default'] = default_producer
        return default_producer

    def get_default_consumer_config(self) -> ConsumerConfig:
        """Get default consumer configuration."""
        if 'default' in self.consumers:
            return self.consumers['default']

        # Create default config if not exists
        default_consumer = ConsumerConfig(
            bootstrap_servers=self.bootstrap_servers
        )
        self.consumers['default'] = default_consumer
        return default_consumer

    def get_topic_config(self, topic_name: str) -> TopicConfig:
        """Get topic configuration for a specific topic."""
        if topic_name in self.topics:
            return self.topics[topic_name]

        # Create default topic config if not exists
        default_topic = TopicConfig()
        self.topics[topic_name] = default_topic
        return default_topic


class ConfigManager:
    """Configuration manager for Kafka settings."""

    def __init__(self, config_path: Optional[str] = None):
        """Initialize configuration manager."""
        self.config_path = config_path or self._get_default_config_path()
        self.config: Optional[KafkaConfig] = None

    def _get_default_config_path(self) -> str:
        """Get default configuration file path."""
        # Look for config in current directory first
        current_dir_config = os.path.join(os.getcwd(), "kafka_config.yaml")
        if os.path.exists(current_dir_config):
            return current_dir_config

        # Look for config in config directory
        config_dir_config = os.path.join(os.getcwd(), "config", "kafka_config.yaml")
        if os.path.exists(config_dir_config):
            return config_dir_config

        # Default path
        return "kafka_config.yaml"

    def load_config(self, config_path: Optional[str] = None) -> KafkaConfig:
        """
        Load configuration from YAML file.

        Args:
            config_path: Path to configuration file. If None, uses default path.

        Returns:
            Loaded KafkaConfig object

        Raises:
            FileNotFoundError: If configuration file doesn't exist
            ValueError: If configuration is invalid
        """
        if config_path:
            self.config_path = config_path

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

        try:
            with open(self.config_path, 'r', encoding='utf-8') as f:
                data = yaml.safe_load(f)

            if not data:
                raise ValueError("Empty configuration file")

            # Handle both with and without 'kafka' key
            kafka_data = data.get('kafka', data) if data else data
            self.config = KafkaConfig.from_dict(kafka_data)
            return self.config

        except yaml.YAMLError as e:
            raise ValueError(f"Invalid YAML format: {e}")
        except Exception as e:
            raise ValueError(f"Failed to load configuration: {e}")

    def get_producer_config(self, name: str = 'default') -> ProducerConfig:
        """Get producer configuration by name."""
        if self.config is None:
            raise ValueError("Configuration not loaded")

        if name in self.config.producers:
            return self.config.producers[name]

        # If specific name not found, use default
        return self.config.get_default_producer_config()

    def get_consumer_config(self, name: str = 'default') -> ConsumerConfig:
        """Get consumer configuration by name."""
        if self.config is None:
            raise ValueError("Configuration not loaded")

        if name in self.config.consumers:
            return self.config.consumers[name]

        # If specific name not found, use default
        return self.config.get_default_consumer_config()

    def get_bootstrap_servers(self) -> str:
        """Get bootstrap servers URL."""
        if self.config is None:
            raise ValueError("Configuration not loaded")

        return self.config.bootstrap_servers

    def reload_config(self) -> KafkaConfig:
        """Reload configuration from file."""
        return self.load_config()


# Global configuration manager instance
config_manager = ConfigManager()


def get_config() -> KafkaConfig:
    """Get global configuration instance."""
    if config_manager.config is None:
        config_manager.load_config()
    return config_manager.config


def reload_configuration() -> KafkaConfig:
    """Reload global configuration."""
    return config_manager.reload_config()