"""
Profile configuration for metadata stage.

Provides extensible YAML profile system for metadata allocation.
"""

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

import yaml
from rich.console import Console

from src.services.s3_data_service import S3DataService
from src.services.environment_service import env_service

console = Console()


@dataclass
class EnvironmentProfile:
    """Profile for environment metadata allocation."""
    
    key_variants: List[str]
    values: Dict[str, Dict[str, Any]]
    
    def get_variants(self, environment: str) -> List[str]:
        """Get value variants for a specific environment."""
        if environment not in self.values:
            return []
        return self.values[environment].get('variants', [])
    
    def get_weight(self, environment: str) -> float:
        """Get weight for a specific environment."""
        if environment not in self.values:
            return 0.0
        return self.values[environment].get('weight', 1.0)


@dataclass
class RegionProfile:
    """Profile for region metadata allocation."""
    
    values: Dict[str, Dict[str, Any]]
    
    def get_provider_regions(self, region: str, provider: str) -> List[str]:
        """Get provider-specific regions for a region."""
        if region not in self.values:
            return []
        provider_key = f"{provider.lower()}_regions"
        return self.values[region].get(provider_key, [])


@dataclass
class FeatureProfile:
    """Profile for feature metadata allocation."""
    
    key_variants: List[str]
    values: Dict[str, List[Dict[str, Any]]]
    
    def get_features(self, allocation: str) -> List[Dict[str, Any]]:
        """Get features for a specific allocation."""
        return self.values.get(allocation, [])


@dataclass
class GenericProfile:
    """Generic profile for cost centers, teams, etc."""
    
    key_variants: List[str]
    values: Dict[str, Dict[str, Any]]
    
    def get_variants(self, key: str) -> List[str]:
        """Get value variants for a specific key."""
        if key not in self.values:
            return []
        return self.values[key].get('variants', [])
    
    def get_weight(self, key: str) -> float:
        """Get weight for a specific key."""
        if key not in self.values:
            return 0.0
        return self.values[key].get('weight', 1.0)


@dataclass
class ProfileConfig:
    """Configuration for metadata profiles."""
    
    environments: EnvironmentProfile
    regions: RegionProfile
    features: FeatureProfile
    cost_centers: Optional[GenericProfile] = None
    teams: Optional[GenericProfile] = None
    
    def validate_key_collision(self) -> List[str]:
        """
        Check for overlapping key_variants across profiles.
        
        Returns:
            List of collision errors (empty if no collisions)
        """
        errors = []
        
        # Collect all key variants
        all_keys = set()
        profile_keys = {
            'environments': set(self.environments.key_variants),
            'features': set(self.features.key_variants)
        }
        
        # Add optional profiles if they exist
        if self.cost_centers:
            profile_keys['cost_centers'] = set(self.cost_centers.key_variants)
        if self.teams:
            profile_keys['teams'] = set(self.teams.key_variants)
        
        # Check for overlaps
        for profile_name, keys in profile_keys.items():
            overlaps = all_keys.intersection(keys)
            if overlaps:
                conflicting_profiles = []
                for other_profile, other_keys in profile_keys.items():
                    if other_profile != profile_name and other_keys.intersection(overlaps):
                        conflicting_profiles.append(other_profile)
                
                if conflicting_profiles:
                    errors.append(
                        f"Key collision in {profile_name}: {list(overlaps)} "
                        f"conflicts with {conflicting_profiles}"
                    )
            
            all_keys.update(keys)
        
        return errors
    
    @classmethod
    def from_s3(cls) -> 'ProfileConfig':
        """
        Load metadata profiles from S3 with auto-download.
        
        S3 location: czbillgen/metadata_profiles/metadata_profiles.yaml
        Cache location: ~/.czbg/cache/reference_data/metadata_profiles.yaml
        
        Returns:
            ProfileConfig instance
            
        Raises:
            ValueError: If S3 not configured or profiles are invalid
        """
        # Check S3 configuration
        s3_configured = env_service.is_configured("CZBILLGEN_S3_BUCKET")
        if not s3_configured:
            raise ValueError("S3 not configured - metadata profiles require S3 auto-download")
        
        try:
            console.print("🔄 Loading metadata profiles from S3 (auto-download if needed)...", style="dim")
            
            s3_service = S3DataService()
            profiles_content = s3_service.download_metadata_profiles()
            
            console.print("✅ Loaded metadata profiles from S3", style="dim green")
            
            # Parse YAML content
            profiles_data = yaml.safe_load(profiles_content)
            
            return cls._from_yaml_data(profiles_data)
            
        except Exception as e:
            console.print(f"❌ S3 metadata profiles loading failed: {e}", style="red")
            raise ValueError(f"Failed to load metadata profiles from S3: {e}")
    
    @classmethod
    def from_yaml(cls, profiles_path: str) -> 'ProfileConfig':
        """
        Load profile configuration from YAML file.
        
        Args:
            profiles_path: Path to YAML profiles file
            
        Returns:
            ProfileConfig instance
            
        Raises:
            FileNotFoundError: If profiles file doesn't exist
            ValueError: If profiles are invalid
        """
        profiles_path = Path(profiles_path)
        if not profiles_path.exists():
            raise FileNotFoundError(f"Profiles file not found: {profiles_path}")
        
        with open(profiles_path, 'r') as f:
            profiles_data = yaml.safe_load(f)
        
        if not isinstance(profiles_data, dict):
            raise ValueError("Profiles file must contain a YAML dictionary")
        
        return cls._from_yaml_data(profiles_data)
    
    @classmethod
    def _from_yaml_data(cls, profiles_data: Dict[str, Any]) -> 'ProfileConfig':
        """
        Create ProfileConfig from parsed YAML data.
        
        Args:
            profiles_data: Parsed YAML data dictionary
            
        Returns:
            ProfileConfig instance
            
        Raises:
            ValueError: If profiles are invalid
        """
        # Validate required sections
        required_sections = ['environments', 'regions', 'features']
        missing_sections = [section for section in required_sections if section not in profiles_data]
        
        if missing_sections:
            raise ValueError(f"Missing required profile sections: {missing_sections}")
        
        # Create profile objects
        try:
            environments = EnvironmentProfile(
                key_variants=profiles_data['environments']['key_variants'],
                values=profiles_data['environments']['values']
            )
            
            # Regions don't have key_variants anymore
            regions = RegionProfile(
                values=profiles_data['regions']['values']
            )
            
            features = FeatureProfile(
                key_variants=profiles_data['features']['key_variants'],
                values=profiles_data['features']['values']
            )
            
            # Optional profiles
            cost_centers = None
            if 'cost_centers' in profiles_data:
                cost_centers = GenericProfile(
                    key_variants=profiles_data['cost_centers']['key_variants'],
                    values=profiles_data['cost_centers']['values']
                )
            
            teams = None
            if 'teams' in profiles_data:
                teams = GenericProfile(
                    key_variants=profiles_data['teams']['key_variants'],
                    values=profiles_data['teams']['values']
                )
            
            profile_config = cls(
                environments=environments,
                regions=regions,
                features=features,
                cost_centers=cost_centers,
                teams=teams
            )
            
            # Validate key collisions
            collision_errors = profile_config.validate_key_collision()
            if collision_errors:
                raise ValueError(f"Profile key collisions: {collision_errors}")
            
            return profile_config
            
        except KeyError as e:
            raise ValueError(f"Invalid profile structure: missing key {e}")
        except Exception as e:
            raise ValueError(f"Failed to load profiles: {e}")
    
    def get_all_key_variants(self) -> List[str]:
        """Get all key variants from all profiles."""
        all_variants = []
        all_variants.extend(self.environments.key_variants)
        all_variants.extend(self.features.key_variants)
        
        # Add optional profiles
        if self.cost_centers:
            all_variants.extend(self.cost_centers.key_variants)
        if self.teams:
            all_variants.extend(self.teams.key_variants)
        
        return all_variants


def load_metadata_profiles() -> Optional[ProfileConfig]:
    """
    Load metadata profiles from S3 with auto-download.
    
    S3-only loading strategy:
    1. Check S3 configuration
    2. Auto-download from S3 if needed
    3. Return cached profiles or None if S3 not configured
    
    Returns:
        ProfileConfig instance or None if S3 not configured
    """
    # Check if S3 is configured
    s3_configured = env_service.is_configured("CZBILLGEN_S3_BUCKET")
    
    if not s3_configured:
        return None
    
    try:
        return ProfileConfig.from_s3()
    except Exception:
        return None


def ensure_metadata_profiles_available() -> Optional[ProfileConfig]:
    """
    Ensure metadata profiles are available from S3, showing helpful messages if not.
    
    Returns:
        ProfileConfig instance if available, None otherwise
    """
    profiles = load_metadata_profiles()
    
    if profiles is None:
        console.print()
        console.print("❌ S3 metadata profiles not available", style="bold red")
        console.print()
        console.print("📋 Setup required:", style="bold yellow")
        console.print("  1. Configure S3 in ~/.czbg/.env")
        console.print("  2. Ensure metadata_profiles.yaml exists in S3:")
        console.print("     s3://bucket/czbillgen/metadata_profiles/metadata_profiles.yaml")
        console.print()
        return None
    
    return profiles