"""
Profile mapping system for shape-based profile names.

Maps user-friendly shape-based profile names to existing use-case based profile names,
providing backward compatibility while improving usability.
"""

from typing import Dict, Any, List, Optional

from ...profiles.core_profiles import (
    get_variance_profile,
    get_anomaly_profile,
    get_variance_config,
    get_anomaly_config,
    list_variance_profiles,
    list_anomaly_profiles,
)
from ..algorithms.variance import calculate_adjacent_period_variance
from ..algorithms.anomaly import generate_90_day_anomalies
from typing import Callable, Optional
import polars as pl


# Shape-based profile name mappings
VARIANCE_PROFILE_MAPPING = {
    # Shape-based name: existing profile name
    "linear_growth": "gradual_variance_buildup",
    "burst_cycles": "analytics_workload",
    "seasonal_peaks": "holiday_seasonal",
    "periodic_surge": "quarter_end_surge",
    "weekend_heavy": "weekend_spike_cycles",
}

ANOMALY_PROFILE_MAPPING = {
    # Shape-based name: existing profile name
    "exponential_burst": "random_mega_burst",
    "scaling_ramp": "infrastructure_scaling_up",
    "deadline_rush": "end_period_rush",
    "sudden_drop": "immediate_cliff_drop",
    "spike_event": "brief_extreme_event",
}

# Reverse mappings for backward compatibility
REVERSE_VARIANCE_MAPPING = {v: k for k, v in VARIANCE_PROFILE_MAPPING.items()}
REVERSE_ANOMALY_MAPPING = {v: k for k, v in ANOMALY_PROFILE_MAPPING.items()}


class ProfileMapper:
    """
    Profile mapping service that translates between shape-based and use-case based names.
    
    Provides a clean interface for the new generate package while maintaining
    compatibility with existing profile implementations.
    """

    def get_variance_profile_config(self, profile_name: str) -> Dict[str, Any]:
        """
        Get variance profile configuration by shape-based or use-case based name.
        
        Args:
            profile_name: Shape-based name (e.g., "linear_growth") or use-case name
            
        Returns:
            Profile configuration dictionary
            
        Raises:
            ValueError: If profile name is not found
        """
        # Try shape-based name first
        if profile_name in VARIANCE_PROFILE_MAPPING:
            use_case_name = VARIANCE_PROFILE_MAPPING[profile_name]
            return get_variance_config(use_case_name)
        
        # Try use-case name directly (backward compatibility)
        if profile_name in list_variance_profiles():
            return get_variance_config(profile_name)
        
        # Profile not found
        available_shape_names = list(VARIANCE_PROFILE_MAPPING.keys())
        available_use_case_names = list_variance_profiles()
        
        raise ValueError(
            f"Unknown variance profile: '{profile_name}'. "
            f"Available shape-based names: {available_shape_names}. "
            f"Available use-case names: {available_use_case_names}"
        )

    def get_anomaly_profile_config(self, profile_name: str) -> Dict[str, Any]:
        """
        Get anomaly profile configuration by shape-based or use-case based name.
        
        Args:
            profile_name: Shape-based name (e.g., "exponential_burst") or use-case name
            
        Returns:
            Profile configuration dictionary
            
        Raises:
            ValueError: If profile name is not found
        """
        # Try shape-based name first
        if profile_name in ANOMALY_PROFILE_MAPPING:
            use_case_name = ANOMALY_PROFILE_MAPPING[profile_name]
            return get_anomaly_config(use_case_name)
        
        # Try use-case name directly (backward compatibility)
        if profile_name in list_anomaly_profiles():
            return get_anomaly_config(profile_name)
        
        # Profile not found
        available_shape_names = list(ANOMALY_PROFILE_MAPPING.keys())
        available_use_case_names = list_anomaly_profiles()
        
        raise ValueError(
            f"Unknown anomaly profile: '{profile_name}'. "
            f"Available shape-based names: {available_shape_names}. "
            f"Available use-case names: {available_use_case_names}"
        )

    def list_variance_profile_names(self, shape_based: bool = True) -> List[str]:
        """
        List available variance profile names.
        
        Args:
            shape_based: If True, return shape-based names; otherwise use-case names
            
        Returns:
            List of available profile names
        """
        if shape_based:
            return list(VARIANCE_PROFILE_MAPPING.keys())
        else:
            return list_variance_profiles()

    def list_anomaly_profile_names(self, shape_based: bool = True) -> List[str]:
        """
        List available anomaly profile names.
        
        Args:
            shape_based: If True, return shape-based names; otherwise use-case names
            
        Returns:
            List of available profile names
        """
        if shape_based:
            return list(ANOMALY_PROFILE_MAPPING.keys())
        else:
            return list_anomaly_profiles()

    def get_profile_description(self, profile_name: str, profile_type: str) -> str:
        """
        Get description for a profile by name and type.
        
        Args:
            profile_name: Profile name (shape-based or use-case)
            profile_type: "variance" or "anomaly"
            
        Returns:
            Profile description string
        """
        if profile_type == "variance":
            # Map to use-case name if needed
            if profile_name in VARIANCE_PROFILE_MAPPING:
                use_case_name = VARIANCE_PROFILE_MAPPING[profile_name]
            else:
                use_case_name = profile_name
            
            profile = get_variance_profile(use_case_name)
            return profile.description
            
        elif profile_type == "anomaly":
            # Map to use-case name if needed
            if profile_name in ANOMALY_PROFILE_MAPPING:
                use_case_name = ANOMALY_PROFILE_MAPPING[profile_name]
            else:
                use_case_name = profile_name
            
            profile = get_anomaly_profile(use_case_name)
            return profile.description
            
        else:
            raise ValueError(f"Unknown profile type: '{profile_type}'. Must be 'variance' or 'anomaly'.")

    def convert_to_shape_based_name(self, profile_name: str, profile_type: str) -> str:
        """
        Convert use-case based name to shape-based name.
        
        Args:
            profile_name: Use-case based profile name
            profile_type: "variance" or "anomaly"
            
        Returns:
            Shape-based profile name
        """
        if profile_type == "variance":
            return REVERSE_VARIANCE_MAPPING.get(profile_name, profile_name)
        elif profile_type == "anomaly":
            return REVERSE_ANOMALY_MAPPING.get(profile_name, profile_name)
        else:
            raise ValueError(f"Unknown profile type: '{profile_type}'")

    def convert_to_use_case_name(self, profile_name: str, profile_type: str) -> str:
        """
        Convert shape-based name to use-case based name.
        
        Args:
            profile_name: Shape-based profile name
            profile_type: "variance" or "anomaly"
            
        Returns:
            Use-case based profile name
        """
        if profile_type == "variance":
            return VARIANCE_PROFILE_MAPPING.get(profile_name, profile_name)
        elif profile_type == "anomaly":
            return ANOMALY_PROFILE_MAPPING.get(profile_name, profile_name)
        else:
            raise ValueError(f"Unknown profile type: '{profile_type}'")

    def validate_profile_name(self, profile_name: str, profile_type: str) -> bool:
        """
        Validate that a profile name exists.
        
        Args:
            profile_name: Profile name to validate
            profile_type: "variance" or "anomaly"
            
        Returns:
            True if profile exists, False otherwise
        """
        if profile_type == "variance":
            self.get_variance_profile_config(profile_name)
        elif profile_type == "anomaly":
            self.get_anomaly_profile_config(profile_name)
        else:
            return False
        return True

    def get_profile_summary(self) -> Dict[str, Any]:
        """
        Get summary of all available profiles.
        
        Returns:
            Dictionary with profile summaries
        """
        variance_profiles = []
        for shape_name in self.list_variance_profile_names(shape_based=True):
            use_case_name = self.convert_to_use_case_name(shape_name, "variance")
            description = self.get_profile_description(shape_name, "variance")
            variance_profiles.append({
                "shape_name": shape_name,
                "use_case_name": use_case_name,
                "description": description,
            })
        
        anomaly_profiles = []
        for shape_name in self.list_anomaly_profile_names(shape_based=True):
            use_case_name = self.convert_to_use_case_name(shape_name, "anomaly")
            description = self.get_profile_description(shape_name, "anomaly")
            anomaly_profiles.append({
                "shape_name": shape_name,
                "use_case_name": use_case_name,
                "description": description,
            })
        
        return {
            "variance_profiles": variance_profiles,
            "anomaly_profiles": anomaly_profiles,
            "total_profiles": len(variance_profiles) + len(anomaly_profiles),
        }


# Global instance for easy access
profile_mapper = ProfileMapper()


def get_variance_profile_config(profile_name: str) -> Dict[str, Any]:
    """
    Get variance profile configuration by shape-based or use-case based name.
    
    Convenience function that uses the global profile mapper.
    """
    return profile_mapper.get_variance_profile_config(profile_name)


def get_anomaly_profile_config(profile_name: str) -> Dict[str, Any]:
    """
    Get anomaly profile configuration by shape-based or use-case based name.
    
    Convenience function that uses the global profile mapper.
    """
    return profile_mapper.get_anomaly_profile_config(profile_name)


def list_available_variance_profiles() -> List[str]:
    """
    List available variance profiles (shape-based names).
    
    Convenience function that uses the global profile mapper.
    """
    return profile_mapper.list_variance_profile_names(shape_based=True)


def list_available_anomaly_profiles() -> List[str]:
    """
    List available anomaly profiles (shape-based names).
    
    Convenience function that uses the global profile mapper.
    """
    return profile_mapper.list_anomaly_profile_names(shape_based=True)


def validate_profile_combination(variance_profile: str, anomaly_profile: str) -> Dict[str, Any]:
    """
    Validate that a variance and anomaly profile combination is valid.
    
    Args:
        variance_profile: Variance profile name
        anomaly_profile: Anomaly profile name
        
    Returns:
        Validation result dictionary
    """
    result = {
        "valid": True,
        "errors": [],
        "warnings": [],
    }
    
    # Validate variance profile
    if not profile_mapper.validate_profile_name(variance_profile, "variance"):
        result["valid"] = False
        result["errors"].append(f"Invalid variance profile: '{variance_profile}'")
    
    # Validate anomaly profile
    if not profile_mapper.validate_profile_name(anomaly_profile, "anomaly"):
        result["valid"] = False
        result["errors"].append(f"Invalid anomaly profile: '{anomaly_profile}'")
    
    # Check for potential conflicts (if both are valid)
    if result["valid"]:
        # Add any profile combination warnings here
        # For now, all combinations are valid
        pass
    
    return result


def map_variance_profile(profile_name: str) -> Optional[Callable[[pl.DataFrame], pl.DataFrame]]:
    """
    Map a variance profile name to its corresponding algorithm function.
    
    Args:
        profile_name: Shape-based or use-case based profile name
        
    Returns:
        Variance algorithm function or None if not found
    """
    # Get the profile configuration
    config = get_variance_profile_config(profile_name)
    
    # For now, all variance profiles use the same algorithm with different parameters
    # In a more complex implementation, different profiles might use different algorithms
    def variance_function(df: pl.DataFrame) -> pl.DataFrame:
        # Extract data from DataFrame for the algorithm
        base_costs = df["hourly_cost"].to_list()
        timestamps = [str(ts) for ts in df["timestamp"].to_list()]
        
        # Apply variance algorithm - for now use a default resource_id
        # In a full implementation, this would be passed through the context
        adjusted_costs = calculate_adjacent_period_variance(
            base_costs, timestamps, config, "resource_default"
        )
        
        # Return updated DataFrame
        return df.with_columns(
            pl.Series("hourly_cost", adjusted_costs)
        )
    
    return variance_function


def map_anomaly_profile(profile_name: str) -> Optional[Callable[[pl.DataFrame], pl.DataFrame]]:
    """
    Map an anomaly profile name to its corresponding algorithm function.
    
    Args:
        profile_name: Shape-based or use-case based profile name
        
    Returns:
        Anomaly algorithm function or None if not found
    """
    # Get the profile configuration
    config = get_anomaly_profile_config(profile_name)
    
    # For now, all anomaly profiles use the same algorithm with different parameters
    # In a more complex implementation, different profiles might use different algorithms
    def anomaly_function(df: pl.DataFrame) -> pl.DataFrame:
        # Extract data from DataFrame for the algorithm
        costs = df["hourly_cost"].to_list()
        timestamps = [str(ts) for ts in df["timestamp"].to_list()]
        
        # Apply anomaly algorithm - for now use a default resource_id
        # In a full implementation, this would be passed through the context
        adjusted_costs = generate_90_day_anomalies(
            costs, timestamps, config, "resource_default"
        )
        
        # Return updated DataFrame
        return df.with_columns(
            pl.Series("hourly_cost", adjusted_costs)
        )
    
    return anomaly_function