"""
Configuration models for metadata stage.

Provides simple configuration model for metadata allocation parameters.
"""

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

import yaml


@dataclass
class RegionConfig:
    """Configuration for a single region."""
    
    weight: float
    majority: str
    
    def __post_init__(self):
        """Validate region configuration."""
        if self.weight < 0 or self.weight > 1:
            raise ValueError(f"Region weight must be between 0 and 1, got {self.weight}")
        if not self.majority:
            raise ValueError("Region majority must be specified")


@dataclass
class MetadataConfig:
    """Configuration for metadata stage processing."""
    
    dataset_name: str
    majority_environment: str
    majority_regions: Dict[str, RegionConfig]
    base_allocation: str
    
    def __post_init__(self):
        """Validate configuration after initialization."""
        if not self.dataset_name:
            raise ValueError("dataset_name is required")
        if not self.majority_environment:
            raise ValueError("majority_environment is required")
        if not self.majority_regions:
            raise ValueError("majority_regions is required")
        if not self.base_allocation:
            raise ValueError("base_allocation is required")
        
        # Normalize region weights if they don't sum to 1.0
        self._normalize_region_weights()
    
    @classmethod
    def from_yaml(cls, config_path: str) -> 'MetadataConfig':
        """
        Load metadata configuration from YAML file.
        
        Args:
            config_path: Path to YAML configuration file
            
        Returns:
            MetadataConfig instance
            
        Raises:
            FileNotFoundError: If configuration file doesn't exist
            ValueError: If configuration is invalid
        """
        config_path = Path(config_path)
        if not config_path.exists():
            raise FileNotFoundError(f"Configuration file not found: {config_path}")
        
        with open(config_path, 'r') as f:
            config_data = yaml.safe_load(f)
        
        if not isinstance(config_data, dict):
            raise ValueError("Configuration file must contain a YAML dictionary")
        
        # Extract required fields
        required_fields = ['dataset_name', 'majority_environment', 'majority_regions', 'base_allocation']
        missing_fields = [field for field in required_fields if field not in config_data]
        
        if missing_fields:
            raise ValueError(f"Missing required configuration fields: {missing_fields}")
        
        # Parse majority_regions
        majority_regions = {}
        for region_name, region_data in config_data['majority_regions'].items():
            if not isinstance(region_data, dict):
                raise ValueError(f"Region '{region_name}' must be a dictionary with 'weight' and 'majority' keys")
            
            if 'weight' not in region_data or 'majority' not in region_data:
                raise ValueError(f"Region '{region_name}' must have 'weight' and 'majority' keys")
            
            majority_regions[region_name] = RegionConfig(
                weight=float(region_data['weight']),
                majority=region_data['majority']
            )
        
        return cls(
            dataset_name=config_data['dataset_name'],
            majority_environment=config_data['majority_environment'],
            majority_regions=majority_regions,
            base_allocation=config_data['base_allocation']
        )
    
    def validate(self) -> List[str]:
        """
        Validate configuration parameters.
        
        Returns:
            List of validation errors (empty if valid)
        """
        errors = []
        
        # Validate dataset_name format
        if not self.dataset_name or not isinstance(self.dataset_name, str):
            errors.append("dataset_name must be a non-empty string")
        
        # Validate majority_environment
        valid_environments = ['production', 'development', 'testing']
        if self.majority_environment not in valid_environments:
            errors.append(f"majority_environment must be one of: {valid_environments}")
        
        # Validate majority_regions
        valid_regions = ['na', 'latam', 'emea', 'apac']
        for region_name, region_config in self.majority_regions.items():
            if region_name not in valid_regions:
                errors.append(f"Region '{region_name}' must be one of: {valid_regions}")
        
        # Check that weights are reasonable (after normalization)
        total_weight = sum(region.weight for region in self.majority_regions.values())
        if abs(total_weight - 1.0) > 0.001:  # Allow small floating point differences
            errors.append(f"Region weights sum to {total_weight}, should be 1.0")
        
        # Validate base_allocation
        valid_allocations = ['analytics_data', 'project_management', 'customer_service', 'marketing', 'security']
        if self.base_allocation not in valid_allocations:
            errors.append(f"base_allocation must be one of: {valid_allocations}")
        
        return errors
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert configuration to dictionary."""
        majority_regions_dict = {}
        for region_name, region_config in self.majority_regions.items():
            majority_regions_dict[region_name] = {
                'weight': region_config.weight,
                'majority': region_config.majority
            }
        
        return {
            'dataset_name': self.dataset_name,
            'majority_environment': self.majority_environment,
            'majority_regions': majority_regions_dict,
            'base_allocation': self.base_allocation
        }
    
    def _normalize_region_weights(self) -> None:
        """Normalize region weights to sum to 1.0 if they don't already."""
        total_weight = sum(region.weight for region in self.majority_regions.values())
        
        if abs(total_weight - 1.0) > 0.001:  # Needs normalization
            for region_config in self.majority_regions.values():
                region_config.weight = region_config.weight / total_weight
    
    def get_normalized_region_weights(self) -> Dict[str, float]:
        """Get normalized region weights as a dictionary."""
        return {
            region_name: region_config.weight 
            for region_name, region_config in self.majority_regions.items()
        }