"""
Cost analysis data structures for enhanced dry run functionality.

Provides data classes and utilities for analyzing cost differences between
configured and actual final costs in generated datasets.
"""

from dataclasses import dataclass
from typing import Optional, Dict, Any, List
from datetime import datetime


@dataclass
class CostAnalysisResult:
    """
    Complete cost analysis result from enhanced dry run.

    Contains all metrics needed to help users understand cost impact
    of anomaly profiles and make informed adjustments to total_cost.
    """

    # Core cost metrics
    configured_total_cost: float
    actual_final_cost: float
    cost_multiplier: float  # actual / configured

    # Monthly cost breakdown metrics
    max_monthly_cost: Optional[float] = None
    median_monthly_cost: Optional[float] = None
    monthly_costs: Optional[List[float]] = None

    # Anomaly impact details
    anomaly_type: Optional[str] = None
    anomaly_details: Optional[Dict[str, Any]] = None

    # Generation statistics
    line_item_count: int = 0
    time_range_hours: int = 0
    total_resources: int = 0

    # Time period details
    start_date: Optional[datetime] = None
    end_date: Optional[datetime] = None

    # Processing details
    generation_duration_seconds: float = 0.0
    temp_dataset_name: str = ""

    def __post_init__(self):
        """Validate and compute derived metrics."""
        if self.configured_total_cost <= 0:
            raise ValueError("Configured total cost must be positive")

        if self.actual_final_cost < 0:
            raise ValueError("Actual final cost cannot be negative")

        # Calculate cost multiplier (handle zero configured cost edge case)
        if self.configured_total_cost > 0:
            self.cost_multiplier = self.actual_final_cost / self.configured_total_cost
        else:
            self.cost_multiplier = 0.0

    def is_significant_deviation(self, threshold: float = 1.5) -> bool:
        """
        Check if cost deviation is significant enough to warrant user attention.

        Args:
            threshold: Multiplier threshold for significant deviation

        Returns:
            True if cost multiplier is outside threshold range (1/threshold to threshold)
        """
        return self.cost_multiplier > threshold or self.cost_multiplier < (1.0 / threshold)

    def get_adjustment_guidance(self, target_final_cost: Optional[float] = None) -> str:
        """
        Provide actionable guidance for adjusting total_cost configuration.

        Args:
            target_final_cost: Desired final cost (uses configured cost if not provided)

        Returns:
            User-friendly guidance string
        """
        if target_final_cost is None:
            target_final_cost = self.configured_total_cost

        if self.actual_final_cost <= 0:
            return "Generated cost is zero - check anomaly configuration"

        # Calculate suggested adjustment
        suggested_total_cost = target_final_cost * (self.configured_total_cost / self.actual_final_cost)

        if self.is_significant_deviation():
            return (
                f"To achieve ${target_final_cost:,.2f} final cost, "
                f"set total_cost: {suggested_total_cost:.2f}"
            )
        else:
            return "Current configuration produces expected cost range"

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for serialization or logging."""
        return {
            "configured_total_cost": self.configured_total_cost,
            "actual_final_cost": self.actual_final_cost,
            "cost_multiplier": self.cost_multiplier,
            "max_monthly_cost": self.max_monthly_cost,
            "median_monthly_cost": self.median_monthly_cost,
            "monthly_costs": self.monthly_costs,
            "anomaly_type": self.anomaly_type,
            "anomaly_details": self.anomaly_details,
            "line_item_count": self.line_item_count,
            "time_range_hours": self.time_range_hours,
            "total_resources": self.total_resources,
            "generation_duration_seconds": self.generation_duration_seconds,
            "temp_dataset_name": self.temp_dataset_name,
            "is_significant_deviation": self.is_significant_deviation(),
        }


@dataclass
class AnomalyProfileInfo:
    """
    Information about detected anomaly profiles in job configuration.

    Used for analysis and reporting purposes.
    """

    anomaly_type: str
    intensity: Optional[float] = None
    period: Optional[str] = None
    duration_hours: Optional[int] = None
    estimated_occurrences: int = 0
    auto_calculated_intensity: bool = False

    def __post_init__(self):
        """Validate anomaly profile information."""
        if not self.anomaly_type:
            raise ValueError("Anomaly type is required")

        valid_types = [
            "daily_spike", "daily_step_up", "daily_step_down",
            "hourly_spike", "hourly_step_up", "hourly_step_down"
        ]

        if self.anomaly_type not in valid_types:
            raise ValueError(f"Invalid anomaly type: {self.anomaly_type}")


def detect_anomaly_profiles(job) -> list[AnomalyProfileInfo]:
    """
    Detect and analyze anomaly profiles in job configuration.

    Args:
        job: GenerationJob instance

    Returns:
        List of AnomalyProfileInfo objects for each detected anomaly profile
    """
    anomaly_profiles = []

    for filter_obj in job.filters:
        if not filter_obj.anomaly_profile:
            continue

        # Handle different anomaly profile formats
        if isinstance(filter_obj.anomaly_profile, dict):
            anomaly_type = filter_obj.anomaly_profile.get("type")
            if anomaly_type:
                profile_info = AnomalyProfileInfo(
                    anomaly_type=anomaly_type,
                    intensity=filter_obj.anomaly_profile.get("intensity"),
                    period=filter_obj.anomaly_profile.get("period"),
                    duration_hours=filter_obj.anomaly_profile.get("duration_hours"),
                    auto_calculated_intensity="intensity" not in filter_obj.anomaly_profile
                )
                anomaly_profiles.append(profile_info)
        elif isinstance(filter_obj.anomaly_profile, str) and filter_obj.anomaly_profile:
            # Handle string-based anomaly profile (if supported)
            profile_info = AnomalyProfileInfo(anomaly_type=filter_obj.anomaly_profile)
            anomaly_profiles.append(profile_info)

    return anomaly_profiles


def has_anomaly_profiles(job) -> bool:
    """
    Quick check if job has any anomaly profiles configured.

    Args:
        job: GenerationJob instance

    Returns:
        True if any anomaly profiles are detected
    """
    return len(detect_anomaly_profiles(job)) > 0