"""
Analytical engine for parameter extraction and trend analysis.
"""

from typing import Dict, Any, Optional
from dataclasses import dataclass, asdict
from datetime import datetime
import pandas as pd
import numpy as np
import json
from scipy import stats
from .base import Engine


@dataclass
class MonthlyParams:
    """Monthly precipitation parameters."""
    p_ww: float  # P(wet|wet)
    p_wd: float  # P(wet|dry) 
    alpha: float  # Gamma shape parameter
    beta: float   # Gamma scale parameter
    
    def to_dict(self) -> Dict[str, float]:
        """Convert to dictionary for JSON serialization."""
        return asdict(self)


@dataclass
class TrendAnalysis:
    """Trend analysis results."""
    seasonal_slopes: Dict[str, Dict[str, float]]  # season -> parameter -> slope
    significance_tests: Dict[str, Dict[str, float]]  # p-values
    trend_confidence: Dict[str, Dict[str, str]]  # significance levels
    regression_type: str = 'linear'  # Type of regression used
    validation_results: Optional[Dict[str, Dict[str, bool]]] = None  # Slope validation
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for JSON serialization."""
        return asdict(self)


@dataclass
class WindowAnalysis:
    """Sliding window analysis results."""
    window_parameters: Dict[str, Dict[int, MonthlyParams]]  # window_id -> month -> params
    window_dates: Dict[str, tuple]  # window_id -> (start_date, end_date)
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for JSON serialization."""
        result = {
            'window_parameters': {},
            'window_dates': {}
        }
        
        # Convert window parameters
        for window_id, month_params in self.window_parameters.items():
            result['window_parameters'][window_id] = {
                str(month): params.to_dict() for month, params in month_params.items()
            }
        
        # Convert window dates to ISO format strings
        for window_id, (start_date, end_date) in self.window_dates.items():
            result['window_dates'][window_id] = [
                start_date.isoformat(),
                end_date.isoformat()
            ]
        
        return result


@dataclass
class ParameterManifest:
    """Complete parameter manifest for simulation."""
    metadata: Dict[str, Any]
    overall_parameters: Dict[int, MonthlyParams]  # month -> parameters
    trend_analysis: Optional[TrendAnalysis]
    sliding_window_stats: Optional[Dict[str, Any]]
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for JSON serialization."""
        result = {
            'metadata': self.metadata.copy(),
            'overall_parameters': {
                str(month): params.to_dict() 
                for month, params in self.overall_parameters.items()
            }
        }
        
        if self.trend_analysis is not None:
            result['trend_analysis'] = self.trend_analysis.to_dict()
        else:
            result['trend_analysis'] = None
            
        result['sliding_window_stats'] = self.sliding_window_stats
        
        return result
    
    def to_json(self, indent: int = 2) -> str:
        """Convert to JSON string."""
        return json.dumps(self.to_dict(), indent=indent, default=str)
    
    def save_to_file(self, filepath: str, indent: int = 2) -> None:
        """Save parameter manifest to JSON file."""
        with open(filepath, 'w') as f:
            json.dump(self.to_dict(), f, indent=indent, default=str)
    
    @classmethod
    def load_from_file(cls, filepath: str) -> 'ParameterManifest':
        """Load parameter manifest from JSON file."""
        with open(filepath, 'r') as f:
            data = json.load(f)
        
        # Reconstruct overall_parameters
        overall_parameters = {}
        for month_str, params_dict in data['overall_parameters'].items():
            month = int(month_str)
            overall_parameters[month] = MonthlyParams(
                p_ww=params_dict['p_ww'],
                p_wd=params_dict['p_wd'],
                alpha=params_dict['alpha'],
                beta=params_dict['beta']
            )
        
        # Reconstruct trend_analysis if present
        trend_analysis = None
        if data.get('trend_analysis') is not None:
            trend_data = data['trend_analysis']
            trend_analysis = TrendAnalysis(
                slope=trend_data['slope'],
                intercept=trend_data['intercept'],
                r_squared=trend_data['r_squared'],
                p_value=trend_data['p_value'],
                std_err=trend_data['std_err']
            )
        
        return cls(
            metadata=data['metadata'],
            overall_parameters=overall_parameters,
            trend_analysis=trend_analysis,
            sliding_window_stats=data.get('sliding_window_stats')
        )


class AnalyticalEngine(Engine):
    """
    Engine for extracting stochastic parameters and detecting trends.
    
    Implements Richardson & Wright (1984) parameter estimation with
    modern enhancements for trend detection and sliding window analysis.
    """
    
    def __init__(self, data: pd.Series, wet_day_threshold: float = 0.01):
        """
        Initialize analytical engine.
        
        Args:
            data: Time series of precipitation data
            wet_day_threshold: Minimum precipitation to classify as wet day (inches)
                              Default: 0.01 inches (standard meteorological threshold)
        """
        self.data = data.copy()
        self.wet_day_threshold = wet_day_threshold
        
        # Set up logging
        import logging
        self.logger = logging.getLogger(__name__)
        
        self.overall_parameters = None
        self.window_analysis = None
        self.trend_analysis = None
        
        # Convert threshold to mm if needed (assuming input is in inches)
        self.wet_day_threshold_mm = wet_day_threshold * 25.4
    
    def initialize(self, **kwargs) -> None:
        """
        Initialize analytical engine.
        
        Args:
            **kwargs: Additional parameters (unused)
        """
        # Ensure data is sorted and clean
        self.data = self.data.sort_index()
        self.data = self.data.fillna(0.0)  # Fill missing with zero precipitation
    
    def reset(self, **kwargs) -> None:
        """
        Reset engine state.
        
        Args:
            **kwargs: Additional parameters (unused)
        """
        self.overall_parameters = None
        self.window_analysis = None
        self.trend_analysis = None
    
    def get_state(self) -> Dict[str, Any]:
        """
        Get current engine state.
        
        Returns:
            Dictionary containing current state
        """
        return {
            'data_length': len(self.data),
            'data_period': (self.data.index.min(), self.data.index.max()),
            'wet_day_threshold': self.wet_day_threshold,
            'has_overall_parameters': self.overall_parameters is not None,
            'has_window_analysis': self.window_analysis is not None,
            'has_trend_analysis': self.trend_analysis is not None
        }
    
    def calculate_monthly_parameters(self) -> Dict[int, MonthlyParams]:
        """
        Calculate overall monthly parameters for entire dataset with robust error handling.
        
        Returns:
            Dictionary mapping month number to parameters
            
        Raises:
            ParameterEstimationError: If parameter calculation fails for critical months
        """
        from ..utils.error_handling import robust_operation, error_recovery_manager
        from ..utils.exceptions import ParameterEstimationError, InsufficientDataError
        
        monthly_params = {}
        failed_months = []
        
        for month in range(1, 13):
            try:
                month_data = self.data[self.data.index.month == month]
                
                # Check data sufficiency with graceful degradation
                if len(month_data) < 10:  # Minimum threshold
                    self.logger.warning(f"Insufficient data for month {month} ({len(month_data)} days)")
                    
                    # Try recovery strategies
                    recovery_success, recovered_params = error_recovery_manager.handle_error_with_recovery(
                        InsufficientDataError(
                            len(month_data), 30, f"Month {month}", "monthly_parameters"
                        ),
                        f"monthly_parameters_month_{month}",
                        {
                            'month': month,
                            'available_data': len(month_data),
                            'fallback_data': self.data,  # Use all data as fallback
                            'fallback_parameters': self._get_fallback_parameters_for_month(month, monthly_params)
                        }
                    )
                    
                    if recovery_success:
                        if isinstance(recovered_params, MonthlyParams):
                            monthly_params[month] = recovered_params
                        else:
                            # Use fallback data (entire dataset)
                            params = self._calculate_parameters_for_period_robust(self.data, f"month_{month}_fallback")
                            monthly_params[month] = params
                        continue
                    else:
                        failed_months.append(month)
                        continue
                
                # Calculate parameters with error handling
                @robust_operation(
                    operation_name=f"parameter_calculation_month_{month}",
                    fallback_value=None,
                    error_types=(ValueError, RuntimeError, ParameterEstimationError),
                    log_errors=True,
                    raise_on_failure=False
                )
                def calculate_month_params():
                    return self._calculate_parameters_for_period(month_data)
                
                params = calculate_month_params()
                
                if params is not None:
                    monthly_params[month] = params
                else:
                    # Use fallback calculation
                    self.logger.warning(f"Primary calculation failed for month {month}, using fallback")
                    fallback_params = self._get_fallback_parameters_for_month(month, monthly_params)
                    monthly_params[month] = fallback_params
                    
            except Exception as e:
                self.logger.error(f"Failed to calculate parameters for month {month}: {e}")
                failed_months.append(month)
                
                # Use fallback parameters
                fallback_params = self._get_fallback_parameters_for_month(month, monthly_params)
                monthly_params[month] = fallback_params
        
        # Validate we have parameters for all months
        if len(monthly_params) < 12:
            missing_months = [m for m in range(1, 13) if m not in monthly_params]
            self.logger.error(f"Missing parameters for months: {missing_months}")
            
            # Fill missing months with interpolated values
            for month in missing_months:
                monthly_params[month] = self._interpolate_missing_month_parameters(month, monthly_params)
        
        # Final validation and quality check
        self._validate_monthly_parameters(monthly_params)
        
        self.overall_parameters = monthly_params
        
        if failed_months:
            self.logger.warning(f"Parameter calculation used fallbacks for months: {failed_months}")
        
        return monthly_params
    
    def _calculate_parameters_for_period_robust(self, data: pd.Series, context: str) -> MonthlyParams:
        """
        Robust parameter calculation with comprehensive error handling.
        """
        from ..utils.error_handling import robust_operation
        from ..utils.exceptions import ParameterEstimationError, ConvergenceError
        
        @robust_operation(
            operation_name=f"parameter_calculation_{context}",
            fallback_value=None,
            error_types=(ValueError, RuntimeError, ParameterEstimationError, ConvergenceError),
            log_errors=True,
            raise_on_failure=False
        )
        def calculate_with_primary_method():
            return self._calculate_parameters_for_period(data)
        
        # Try primary calculation
        params = calculate_with_primary_method()
        
        if params is not None:
            return params
        
        # Try simplified calculation
        try:
            self.logger.info(f"Attempting simplified parameter calculation for {context}")
            return self._calculate_simplified_parameters(data)
        except Exception as e:
            self.logger.error(f"Simplified calculation also failed for {context}: {e}")
            
            # Return default parameters as last resort
            self.logger.warning(f"Using default parameters for {context}")
            return MonthlyParams(p_ww=0.5, p_wd=0.3, alpha=1.0, beta=5.0)
    
    def _calculate_simplified_parameters(self, data: pd.Series) -> MonthlyParams:
        """
        Calculate simplified parameters using basic statistical methods.
        """
        wet_days = data > self.wet_day_threshold
        
        if len(data) == 0:
            return MonthlyParams(p_ww=0.5, p_wd=0.3, alpha=1.0, beta=5.0)
        
        # Simple transition probabilities
        if wet_days.sum() == 0:
            p_ww = 0.0
            p_wd = 0.0
        elif wet_days.sum() == len(wet_days):
            p_ww = 1.0
            p_wd = 1.0
        else:
            # Calculate basic transition probabilities
            wet_wet_count = 0
            wet_dry_count = 0
            dry_wet_count = 0
            
            for i in range(1, len(wet_days)):
                if wet_days.iloc[i-1] and wet_days.iloc[i]:
                    wet_wet_count += 1
                elif wet_days.iloc[i-1] and not wet_days.iloc[i]:
                    wet_dry_count += 1
                elif not wet_days.iloc[i-1] and wet_days.iloc[i]:
                    dry_wet_count += 1
            
            total_wet_days = wet_wet_count + wet_dry_count
            total_dry_days = len(wet_days) - total_wet_days
            
            p_ww = wet_wet_count / max(1, total_wet_days)
            p_wd = dry_wet_count / max(1, total_dry_days)
        
        # Simple gamma parameters using method of moments
        wet_amounts = data[wet_days]
        if len(wet_amounts) > 0:
            mean_wet = wet_amounts.mean()
            var_wet = wet_amounts.var()
            
            if var_wet > 0 and mean_wet > 0:
                # Method of moments for gamma distribution
                beta = var_wet / mean_wet
                alpha = mean_wet / beta
            else:
                alpha = 1.0
                beta = mean_wet if mean_wet > 0 else 5.0
        else:
            alpha = 1.0
            beta = 5.0
        
        # Ensure parameters are within reasonable bounds
        p_ww = max(0.0, min(1.0, p_ww))
        p_wd = max(0.0, min(1.0, p_wd))
        alpha = max(0.1, min(10.0, alpha))
        beta = max(0.1, min(50.0, beta))
        
        return MonthlyParams(p_ww=p_ww, p_wd=p_wd, alpha=alpha, beta=beta)
    
    def _get_fallback_parameters_for_month(self, month: int, 
                                         existing_params: Dict[int, MonthlyParams]) -> MonthlyParams:
        """
        Get fallback parameters for a month using interpolation or defaults.
        """
        if existing_params:
            # Use average of existing parameters
            avg_p_ww = np.mean([p.p_ww for p in existing_params.values()])
            avg_p_wd = np.mean([p.p_wd for p in existing_params.values()])
            avg_alpha = np.mean([p.alpha for p in existing_params.values()])
            avg_beta = np.mean([p.beta for p in existing_params.values()])
            
            return MonthlyParams(p_ww=avg_p_ww, p_wd=avg_p_wd, alpha=avg_alpha, beta=avg_beta)
        else:
            # Use seasonal defaults based on month
            if month in [12, 1, 2]:  # Winter
                return MonthlyParams(p_ww=0.6, p_wd=0.4, alpha=1.2, beta=6.0)
            elif month in [3, 4, 5]:  # Spring
                return MonthlyParams(p_ww=0.5, p_wd=0.3, alpha=1.0, beta=5.0)
            elif month in [6, 7, 8]:  # Summer
                return MonthlyParams(p_ww=0.3, p_wd=0.2, alpha=0.8, beta=4.0)
            else:  # Fall
                return MonthlyParams(p_ww=0.4, p_wd=0.3, alpha=1.0, beta=5.0)
    
    def _interpolate_missing_month_parameters(self, month: int, 
                                            existing_params: Dict[int, MonthlyParams]) -> MonthlyParams:
        """
        Interpolate parameters for missing months using neighboring months.
        """
        if not existing_params:
            return self._get_fallback_parameters_for_month(month, {})
        
        # Find nearest months with parameters
        available_months = sorted(existing_params.keys())
        
        if len(available_months) == 1:
            # Only one month available, use it
            return existing_params[available_months[0]]
        
        # Find closest months for interpolation
        prev_month = None
        next_month = None
        
        for m in available_months:
            if m < month:
                prev_month = m
            elif m > month and next_month is None:
                next_month = m
                break
        
        # Handle wrap-around for December/January
        if prev_month is None:
            prev_month = available_months[-1]  # Use December for January
        if next_month is None:
            next_month = available_months[0]   # Use January for December
        
        # Interpolate parameters
        prev_params = existing_params[prev_month]
        next_params = existing_params[next_month]
        
        # Simple linear interpolation
        weight = 0.5  # Could be more sophisticated based on month distance
        
        interpolated = MonthlyParams(
            p_ww=prev_params.p_ww * (1 - weight) + next_params.p_ww * weight,
            p_wd=prev_params.p_wd * (1 - weight) + next_params.p_wd * weight,
            alpha=prev_params.alpha * (1 - weight) + next_params.alpha * weight,
            beta=prev_params.beta * (1 - weight) + next_params.beta * weight
        )
        
        return interpolated
    
    def _validate_monthly_parameters(self, monthly_params: Dict[int, MonthlyParams]) -> None:
        """
        Validate monthly parameters and apply corrections if needed.
        """
        for month, params in monthly_params.items():
            # Check probability bounds
            if not (0 <= params.p_ww <= 1):
                self.logger.warning(f"Invalid p_ww for month {month}: {params.p_ww}, clamping to [0,1]")
                params.p_ww = max(0.0, min(1.0, params.p_ww))
            
            if not (0 <= params.p_wd <= 1):
                self.logger.warning(f"Invalid p_wd for month {month}: {params.p_wd}, clamping to [0,1]")
                params.p_wd = max(0.0, min(1.0, params.p_wd))
            
            # Check gamma parameters
            if params.alpha <= 0:
                self.logger.warning(f"Invalid alpha for month {month}: {params.alpha}, setting to 1.0")
                params.alpha = 1.0
            
            if params.beta <= 0:
                self.logger.warning(f"Invalid beta for month {month}: {params.beta}, setting to 5.0")
                params.beta = 5.0
    
    def perform_sliding_window_analysis(self, window_years: int = 30) -> WindowAnalysis:
        """
        Perform sliding window analysis to evaluate parameter evolution.
        
        Args:
            window_years: Size of sliding window in years
            
        Returns:
            WindowAnalysis with windowed parameter estimates
        """
        if len(self.data) == 0:
            raise ValueError("No data available for sliding window analysis")
        
        # Store window size for later use in manifest generation
        self._window_years = window_years
        
        # Calculate window size in days
        window_days = window_years * 365
        
        # Get data date range
        start_date = self.data.index.min()
        end_date = self.data.index.max()
        total_days = (end_date - start_date).days
        
        if total_days < window_days:
            raise ValueError(f"Data period ({total_days} days) shorter than window size ({window_days} days)")
        
        window_parameters = {}
        window_dates = {}
        
        # Create overlapping windows (step by 1 year)
        step_days = 365
        window_id = 0
        
        current_start = start_date
        while current_start + pd.Timedelta(days=window_days) <= end_date:
            current_end = current_start + pd.Timedelta(days=window_days)
            
            # Extract window data
            window_data = self.data[
                (self.data.index >= current_start) & 
                (self.data.index <= current_end)
            ]
            
            if len(window_data) >= window_days * 0.8:  # Require 80% data availability
                # Calculate monthly parameters for this window
                window_monthly_params = {}
                for month in range(1, 13):
                    month_data = window_data[window_data.index.month == month]
                    if len(month_data) >= 10:  # Minimum monthly data
                        params = self._calculate_parameters_for_period(month_data)
                        window_monthly_params[month] = params
                
                if window_monthly_params:  # Only store if we have valid parameters
                    window_id_str = f"window_{window_id:03d}"
                    window_parameters[window_id_str] = window_monthly_params
                    window_dates[window_id_str] = (current_start, current_end)
                    window_id += 1
            
            current_start += pd.Timedelta(days=step_days)
        
        self.window_analysis = WindowAnalysis(
            window_parameters=window_parameters,
            window_dates=window_dates
        )
        
        return self.window_analysis
    
    def extract_trends(self, window_results: WindowAnalysis, 
                      regression_type: str = 'linear') -> TrendAnalysis:
        """
        Extract trends from sliding window analysis using linear or polynomial regression.
        
        Args:
            window_results: Results from sliding window analysis
            regression_type: Type of regression ('linear' or 'polynomial')
            
        Returns:
            TrendAnalysis with trend slopes and significance tests
        """
        if not window_results.window_parameters:
            raise ValueError("No window analysis results available for trend extraction")
        
        if regression_type not in ['linear', 'polynomial']:
            raise ValueError("regression_type must be 'linear' or 'polynomial'")
        
        # Organize data by season and parameter
        seasonal_data = {
            'Winter': [12, 1, 2],
            'Spring': [3, 4, 5], 
            'Summer': [6, 7, 8],
            'Fall': [9, 10, 11]
        }
        
        seasonal_slopes = {}
        significance_tests = {}
        trend_confidence = {}
        
        for season, months in seasonal_data.items():
            seasonal_slopes[season] = {}
            significance_tests[season] = {}
            trend_confidence[season] = {}
            
            # Extract time series for each parameter
            param_names = ['p_ww', 'p_wd', 'alpha', 'beta']
            
            for param_name in param_names:
                param_values = []
                window_times = []
                
                for window_id, window_params in window_results.window_parameters.items():
                    # Average parameter across months in season
                    season_values = []
                    for month in months:
                        if month in window_params:
                            param_obj = window_params[month]
                            param_value = getattr(param_obj, param_name)
                            season_values.append(param_value)
                    
                    if season_values:
                        param_values.append(np.mean(season_values))
                        # Use window center time
                        start_date, end_date = window_results.window_dates[window_id]
                        center_time = start_date + (end_date - start_date) / 2
                        window_times.append(center_time.year + center_time.dayofyear / 365.25)
                
                if len(param_values) >= 3:  # Need minimum points for regression
                    slope, p_value = self._perform_regression_analysis(
                        window_times, param_values, regression_type
                    )
                    
                    seasonal_slopes[season][param_name] = slope
                    significance_tests[season][param_name] = p_value
                    
                    # Determine confidence level
                    confidence = self._determine_trend_confidence(p_value)
                    trend_confidence[season][param_name] = confidence
                else:
                    # Insufficient data
                    seasonal_slopes[season][param_name] = 0.0
                    significance_tests[season][param_name] = 1.0
                    trend_confidence[season][param_name] = "Insufficient data"
        
        self.trend_analysis = TrendAnalysis(
            seasonal_slopes=seasonal_slopes,
            significance_tests=significance_tests,
            trend_confidence=trend_confidence,
            regression_type=regression_type
        )
        
        # Validate trend slopes
        validation_results = self.validate_trend_slopes(self.trend_analysis)
        self.trend_analysis.validation_results = validation_results
        
        return self.trend_analysis
    
    def get_parameters_table(self) -> 'pd.DataFrame':
        """
        Get monthly parameters as a formatted DataFrame table.
        
        Returns:
            DataFrame with monthly parameters in table format for display
            
        Example:
            >>> engine = AnalyticalEngine(data)
            >>> engine.initialize()
            >>> table = engine.get_parameters_table()
            >>> table  # Displays nice table in Jupyter
        """
        import pandas as pd
        
        if self.overall_parameters is None:
            self.calculate_monthly_parameters()
        
        param_data = []
        for month, params in self.overall_parameters.items():
            month_name = pd.Timestamp(2000, month, 1).strftime('%B')
            param_data.append({
                'Month': month_name,
                'P(W|W)': round(params.p_ww, 4),
                'P(W|D)': round(params.p_wd, 4),
                'Alpha': round(params.alpha, 4),
                'Beta': round(params.beta, 4)
            })
        
        return pd.DataFrame(param_data)
    
    def plot_trend_analysis(self, show_plots: bool = True, save_path: Optional[str] = None) -> None:
        """
        Create comprehensive trend analysis plots showing seasonal parameter evolution.
        
        Enhanced with consistent styling, improved significance indicators, and automatic
        station name extraction for publication-quality visualizations.
        
        Args:
            show_plots: Whether to display plots (default: True)
            save_path: Optional path to save the plot (e.g., 'trends.png')
            
        Raises:
            ValueError: If trend analysis has not been performed
            
        Example:
            >>> engine = AnalyticalEngine(data)
            >>> engine.initialize()
            >>> engine.perform_comprehensive_trend_analysis()
            >>> engine.plot_trend_analysis()  # Shows plots in Jupyter
        """
        if self.trend_analysis is None:
            raise ValueError("No trend analysis available. Run perform_comprehensive_trend_analysis() first.")
        
        import matplotlib.pyplot as plt
        import numpy as np
        
        # Apply consistent styling
        self._apply_plot_style()
        
        # Get station name for title
        station_name = self._get_station_name_for_plots()
        
        # Create seasonal trend plots for each parameter
        parameters = ['p_ww', 'p_wd', 'alpha', 'beta']
        param_labels = ['P(W|W)', 'P(W|D)', 'Alpha', 'Beta']
        seasons = ['Winter', 'Spring', 'Summer', 'Fall']
        
        # Use consistent seasonal color scheme
        seasonal_colors = self._get_seasonal_colors()
        
        fig, axes = plt.subplots(2, 2, figsize=(15, 12))
        fig.suptitle(f'Seasonal Parameter Trends - {station_name}', 
                    fontsize=16, fontweight='bold', y=0.98)
        
        for i, (param, label) in enumerate(zip(parameters, param_labels)):
            ax = axes[i // 2, i % 2]
            
            # Plot trends for each season
            for season in seasons:
                if season in self.trend_analysis.seasonal_slopes:
                    slope = self.trend_analysis.seasonal_slopes[season].get(param, 0)
                    p_value = self.trend_analysis.significance_tests[season].get(param, 1.0)
                    confidence = self.trend_analysis.trend_confidence[season].get(param, "Not significant")
                    
                    # Create trend line (30-year projection)
                    years = np.arange(1990, 2025)
                    if self.overall_parameters and 1 in self.overall_parameters:
                        baseline = getattr(self.overall_parameters[1], param)
                    else:
                        baseline = 0.5 if param.startswith('p_') else 1.0
                    
                    trend_line = baseline + slope * (years - 2000)
                    
                    # Enhanced significance indicators
                    linestyle, linewidth, alpha, marker = self._get_significance_style(p_value)
                    
                    # Add significance markers to legend
                    significance_marker = self._get_significance_marker(p_value)
                    
                    # Use seasonal color
                    season_color = seasonal_colors[season]
                    
                    ax.plot(years, trend_line, 
                           color=season_color, linestyle=linestyle, linewidth=linewidth,
                           alpha=alpha, marker=marker, markersize=3, markevery=5,
                           label=f'{season} {significance_marker} (slope: {slope:.6f})')
            
            # Enhanced subplot formatting
            ax.set_title(f'{label} Parameter Trends', fontsize=12, fontweight='bold', pad=10)
            ax.set_xlabel('Year', fontsize=11)
            ax.set_ylabel(label, fontsize=11)
            ax.legend(fontsize=9, loc='best', framealpha=0.9)
            ax.grid(True, alpha=0.3, linestyle='-', linewidth=0.5)
            
            # Add significance legend
            self._add_significance_legend(ax)
        
        # Add overall significance summary
        self._add_significance_summary(fig, axes)
        
        plt.tight_layout()
        plt.subplots_adjust(top=0.93, bottom=0.08)
        
        if save_path:
            plt.savefig(save_path, dpi=300, bbox_inches='tight', facecolor='white')
            print(f"Plot saved to: {save_path}")
        
        if show_plots:
            plt.show()
        else:
            plt.close()
    
    def get_annual_precipitation_statistics(self) -> Dict[str, Any]:
        """
        Get comprehensive annual precipitation statistics.
        
        Returns:
            Dictionary containing annual precipitation statistics including:
            - annual_totals: Pandas Series of annual totals
            - mean: Mean annual precipitation
            - std: Standard deviation
            - min/max: Minimum and maximum annual totals
            - cv: Coefficient of variation
            - percentiles: 10th, 25th, 50th, 75th, 90th percentiles
            - wet_day_stats: Wet day statistics (count, frequency, mean amount)
            
        Example:
            >>> engine = AnalyticalEngine(data)
            >>> engine.initialize()
            >>> stats = engine.get_annual_precipitation_statistics()
            >>> print(f"Mean annual: {stats['mean']:.1f} mm")
        """
        import numpy as np
        
        # Calculate annual totals
        annual_totals = self.data.resample('YE').sum()
        
        if len(annual_totals) == 0:
            raise ValueError("No annual data available")
        
        # Calculate statistics
        mean_annual = annual_totals.mean()
        std_annual = annual_totals.std()
        cv = std_annual / mean_annual if mean_annual > 0 else 0
        
        # Percentiles
        percentiles = np.percentile(annual_totals.values, [10, 25, 50, 75, 90])
        
        # Wet day statistics
        wet_days = self.data > self.wet_day_threshold_mm
        total_wet_days = wet_days.sum()
        wet_day_frequency = wet_days.mean()
        
        # Mean precipitation on wet days
        wet_amounts = self.data[wet_days]
        mean_wet_amount = wet_amounts.mean() if len(wet_amounts) > 0 else 0
        
        return {
            'annual_totals': annual_totals,
            'mean': mean_annual,
            'std': std_annual,
            'min': annual_totals.min(),
            'max': annual_totals.max(),
            'cv': cv,
            'percentiles': {
                'p10': percentiles[0],
                'p25': percentiles[1],
                'p50': percentiles[2],  # median
                'p75': percentiles[3],
                'p90': percentiles[4]
            },
            'wet_day_stats': {
                'total_wet_days': total_wet_days,
                'wet_day_frequency': wet_day_frequency,
                'mean_wet_amount': mean_wet_amount,
                'wet_day_threshold': self.wet_day_threshold_mm
            },
            'n_years': len(annual_totals),
            'total_days': len(self.data),
            'data_period': (annual_totals.index.year.min(), annual_totals.index.year.max())
        }
    
    def print_annual_precipitation_statistics(self) -> None:
        """
        Print formatted annual precipitation statistics including wet day information.
        
        Example:
            >>> engine = AnalyticalEngine(data)
            >>> engine.initialize()
            >>> engine.print_annual_precipitation_statistics()
        """
        stats = self.get_annual_precipitation_statistics()
        
        print("=== ANNUAL PRECIPITATION STATISTICS ===")
        print(f"Data period: {stats['data_period'][0]} - {stats['data_period'][1]} ({stats['n_years']} years)")
        print(f"Total days: {stats['total_days']}")
        print(f"Mean: {stats['mean']:.1f} mm")
        print(f"Standard deviation: {stats['std']:.1f} mm")
        print(f"Coefficient of variation: {stats['cv']:.3f}")
        print(f"Range: {stats['min']:.1f} - {stats['max']:.1f} mm")
        print()
        print("WET DAY STATISTICS:")
        wet_stats = stats['wet_day_stats']
        print(f"  Threshold: {wet_stats['wet_day_threshold']:.4f} mm")
        print(f"  Wet days: {wet_stats['total_wet_days']} ({wet_stats['wet_day_frequency']*100:.1f}%)")
        print(f"  Mean wet day amount: {wet_stats['mean_wet_amount']:.2f} mm")
        print()
        print("PERCENTILES:")
        print(f"  10th: {stats['percentiles']['p10']:.1f} mm")
        print(f"  25th: {stats['percentiles']['p25']:.1f} mm")
        print(f"  50th (median): {stats['percentiles']['p50']:.1f} mm")
        print(f"  75th: {stats['percentiles']['p75']:.1f} mm")
        print(f"  90th: {stats['percentiles']['p90']:.1f} mm")
    
    def plot_monthly_parameters(self, 
                               save_path: Optional[str] = None,
                               show_plot: bool = True,
                               figsize: tuple = (15, 6),
                               style: str = 'grouped') -> Optional[str]:
        """
        Create column charts for monthly WGEN parameters.
        
        Creates two side-by-side charts:
        1. Markov chain transition probabilities [P(W|W), P(W|D)]
        2. Gamma distribution parameters [Alpha, Beta]
        
        Args:
            save_path: Optional path to save the plot
            show_plot: Whether to display the plot
            figsize: Figure size as (width, height)
            style: Chart style - 'grouped' (side-by-side) or 'stacked'
            
        Returns:
            Path where plot was saved (if save_path provided)
            
        Example:
            >>> engine = AnalyticalEngine(data)
            >>> engine.initialize()
            >>> engine.calculate_monthly_parameters()
            >>> engine.plot_monthly_parameters(save_path='wgen_params.png')
        """
        if self.overall_parameters is None:
            raise ValueError("Parameters not calculated. Call calculate_monthly_parameters() first.")
        
        try:
            import matplotlib.pyplot as plt
            import numpy as np
        except ImportError:
            raise ImportError("matplotlib is required for plotting. Install with: pip install matplotlib")
        
        # Extract parameter data
        months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
                 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
        
        p_ww = [self.overall_parameters[i+1].p_ww for i in range(12)]
        p_wd = [self.overall_parameters[i+1].p_wd for i in range(12)]
        alpha = [self.overall_parameters[i+1].alpha for i in range(12)]
        beta = [self.overall_parameters[i+1].beta for i in range(12)]
        
        # Create subplots
        fig, (ax1, ax2) = plt.subplots(1, 2, figsize=figsize)
        
        if style == 'grouped':
            # Grouped bar charts
            x = np.arange(len(months))
            width = 0.35
            
            # Chart 1: Transition Probabilities
            bars1 = ax1.bar(x - width/2, p_ww, width, label='P(W|W)', 
                           alpha=0.8, color='steelblue', edgecolor='navy', linewidth=0.5)
            bars2 = ax1.bar(x + width/2, p_wd, width, label='P(W|D)', 
                           alpha=0.8, color='lightcoral', edgecolor='darkred', linewidth=0.5)
            
            ax1.set_xlabel('Month', fontsize=12)
            ax1.set_ylabel('Probability', fontsize=12)
            ax1.set_title('Markov Chain Transition Probabilities', fontsize=14, fontweight='bold')
            ax1.set_xticks(x)
            ax1.set_xticklabels(months)
            ax1.legend(fontsize=11)
            ax1.grid(True, alpha=0.3, axis='y')
            ax1.set_ylim(0, max(max(p_ww), max(p_wd)) * 1.1)
            
            # Add value labels on bars
            for bar in bars1:
                height = bar.get_height()
                ax1.text(bar.get_x() + bar.get_width()/2., height + 0.01,
                        f'{height:.3f}', ha='center', va='bottom', fontsize=8)
            for bar in bars2:
                height = bar.get_height()
                ax1.text(bar.get_x() + bar.get_width()/2., height + 0.01,
                        f'{height:.3f}', ha='center', va='bottom', fontsize=8)
            
            # Chart 2: Gamma Distribution Parameters
            bars3 = ax2.bar(x - width/2, alpha, width, label='Alpha (shape)', 
                           alpha=0.8, color='forestgreen', edgecolor='darkgreen', linewidth=0.5)
            bars4 = ax2.bar(x + width/2, beta, width, label='Beta (scale)', 
                           alpha=0.8, color='orange', edgecolor='darkorange', linewidth=0.5)
            
            ax2.set_xlabel('Month', fontsize=12)
            ax2.set_ylabel('Parameter Value', fontsize=12)
            ax2.set_title('Gamma Distribution Parameters', fontsize=14, fontweight='bold')
            ax2.set_xticks(x)
            ax2.set_xticklabels(months)
            ax2.legend(fontsize=11)
            ax2.grid(True, alpha=0.3, axis='y')
            ax2.set_ylim(0, max(max(alpha), max(beta)) * 1.1)
            
            # Add value labels on bars
            for bar in bars3:
                height = bar.get_height()
                ax2.text(bar.get_x() + bar.get_width()/2., height + 0.1,
                        f'{height:.2f}', ha='center', va='bottom', fontsize=8)
            for bar in bars4:
                height = bar.get_height()
                ax2.text(bar.get_x() + bar.get_width()/2., height + 0.1,
                        f'{height:.2f}', ha='center', va='bottom', fontsize=8)
        
        else:  # stacked style
            # Stacked bar charts (alternative visualization)
            ax1.bar(months, p_ww, label='P(W|W)', alpha=0.8, color='steelblue')
            ax1.bar(months, p_wd, bottom=p_ww, label='P(W|D)', alpha=0.8, color='lightcoral')
            ax1.set_ylabel('Probability')
            ax1.set_title('Transition Probabilities (Stacked)')
            ax1.legend()
            ax1.grid(True, alpha=0.3)
            
            ax2.bar(months, alpha, label='Alpha', alpha=0.8, color='forestgreen')
            ax2.bar(months, beta, bottom=alpha, label='Beta', alpha=0.8, color='orange')
            ax2.set_ylabel('Parameter Value')
            ax2.set_title('Gamma Parameters (Stacked)')
            ax2.legend()
            ax2.grid(True, alpha=0.3)
        
        # Overall formatting
        plt.suptitle(f'Monthly WGEN Parameters\nStation: {self.data.attrs.get("station_name", "Unknown")}', 
                    fontsize=16, fontweight='bold', y=0.98)
        plt.tight_layout()
        plt.subplots_adjust(top=0.85)
        
        # Save plot if requested
        if save_path:
            plt.savefig(save_path, dpi=300, bbox_inches='tight', facecolor='white')
            self.logger.info(f"Parameter plot saved to: {save_path}")
        
        # Show plot if requested
        if show_plot:
            plt.show()
        else:
            plt.close()
        
        return save_path if save_path else None
    
    def generate_comprehensive_trend_report(self, window_years: int = 30, 
                                          save_plots: bool = True, 
                                          output_dir: str = 'output',
                                          include_baseline_stats: bool = False) -> Dict[str, Any]:
        """
        Generate a comprehensive trend analysis report focusing on trends only.
        
        Args:
            window_years: Size of sliding window for seasonal trend analysis
            save_plots: Whether to save trend plots
            output_dir: Directory to save plots
            include_baseline_stats: Whether to include baseline statistics (default: False)
            
        Returns:
            Dictionary containing complete trend analysis results
            
        Example:
            >>> engine = AnalyticalEngine(data)
            >>> engine.initialize()
            >>> # Print baseline stats first
            >>> engine.print_annual_precipitation_statistics()
            >>> # Then get trend analysis
            >>> report = engine.generate_comprehensive_trend_report()
        """
        from pathlib import Path
        
        if save_plots:
            Path(output_dir).mkdir(exist_ok=True)
        
        print()
        
        # 1. Baseline statistics (optional)
        if include_baseline_stats:
            print("BASELINE STATISTICS")
            print("-" * 50)
            self.print_annual_precipitation_statistics()
            print()
        
        # 2. Annual precipitation trends
        print("ANNUAL PRECIPITATION TRENDS")
        print("-" * 50)
        annual_trend_stats = self.analyze_annual_precipitation_trend()
        self.print_annual_precipitation_trend()
        print()
        
        # 3. Seasonal parameter trends
        print("SEASONAL PARAMETER TRENDS")
        print("-" * 50)
        seasonal_trends = self.perform_comprehensive_trend_analysis(window_years=window_years)
        print(f"Sliding window analysis: {window_years}-year windows")
        print(f"Seasons analyzed: {len(seasonal_trends.seasonal_slopes)}")
        print()
        
        # Print seasonal trend details
        for season in ['Winter', 'Spring', 'Summer', 'Fall']:
            if season in seasonal_trends.seasonal_slopes:
                print(f"{season.upper()} TRENDS:")
                season_slopes = seasonal_trends.seasonal_slopes[season]
                season_pvals = seasonal_trends.significance_tests[season]
                season_confidence = seasonal_trends.trend_confidence[season]
                
                for param in ['p_ww', 'p_wd', 'alpha', 'beta']:
                    if param in season_slopes:
                        slope = season_slopes[param]
                        p_val = season_pvals[param]
                        confidence = season_confidence[param]
                        
                        # Add interpretation
                        if param in ['p_ww', 'p_wd']:
                            param_name = f"P(W|W)" if param == 'p_ww' else f"P(W|D)"
                            units = "/year"
                        else:
                            param_name = f"Gamma {param}"
                            units = "/year"
                        
                        print(f"  {param_name}: {slope:+.6f}{units} ({confidence})")
                print()
        
        # 4. Trend validation
        if seasonal_trends.validation_results:
            print("TREND VALIDATION")
            print("-" * 50)
            invalid_trends = []
            for season, season_results in seasonal_trends.validation_results.items():
                for param, is_valid in season_results.items():
                    if not is_valid:
                        slope_val = seasonal_trends.seasonal_slopes[season][param]
                        invalid_trends.append(f"{season} {param}: {slope_val:.6f}")
            
            if invalid_trends:
                print("WARNING: Invalid trend slopes detected:")
                for trend in invalid_trends:
                    print(f"  - {trend}")
            else:
                print("✓ All trend slopes are within reasonable bounds")
            print()
        
        # 5. Generate plots
        if save_plots:
            print("GENERATING PLOTS")
            print("-" * 50)
            annual_plot_path = f"{output_dir}/annual_precipitation_trends.png"
            seasonal_plot_path = f"{output_dir}/seasonal_parameter_trends.png"
            
            self.plot_annual_precipitation(show_plots=False, save_path=annual_plot_path)
            self.plot_trend_analysis(show_plots=False, save_path=seasonal_plot_path)
            
            print(f"✓ Annual precipitation plot saved: {annual_plot_path}")
            print(f"✓ Seasonal parameter trends plot saved: {seasonal_plot_path}")
            print()
        
        # 6. Summary and recommendations
        print("SUMMARY AND RECOMMENDATIONS")
        print("-" * 50)
        
        # Annual trend summary
        annual_slope = annual_trend_stats['slope']
        annual_significant = annual_trend_stats['p_value'] < 0.05
        
        if annual_significant:
            direction = "increasing" if annual_slope > 0 else "decreasing"
            print(f"• Annual precipitation is {direction} significantly at {abs(annual_slope):.1f} mm/year")
        else:
            print(f"• No significant trend in annual precipitation ({annual_slope:+.2f} mm/year)")
        
        # Count significant seasonal trends
        significant_seasonal_trends = 0
        total_seasonal_params = 0
        
        for season in seasonal_trends.seasonal_slopes:
            for param in seasonal_trends.seasonal_slopes[season]:
                total_seasonal_params += 1
                p_val = seasonal_trends.significance_tests[season][param]
                if p_val < 0.05:
                    significant_seasonal_trends += 1
        
        print(f"• {significant_seasonal_trends}/{total_seasonal_params} seasonal parameters show significant trends")
        
        # Recommendations
        if annual_significant or significant_seasonal_trends > 0:
            print("• RECOMMENDATION: Consider using trend_mode=True in simulations")
            print("• Non-stationary climate patterns detected - trend projection recommended")
        else:
            print("• RECOMMENDATION: Stationary simulation approach is appropriate")
            print("• No significant trends detected - baseline parameters sufficient")
        
        return {
            'annual_trends': annual_trend_stats,
            'seasonal_trends': seasonal_trends,
            'baseline_stats': self.get_annual_precipitation_statistics(),
            'summary': {
                'annual_trend_significant': annual_significant,
                'annual_slope': annual_slope,
                'significant_seasonal_trends': significant_seasonal_trends,
                'total_seasonal_params': total_seasonal_params,
                'recommend_trend_mode': annual_significant or significant_seasonal_trends > 0
            }
        }
    
    def print_trend_summary(self) -> None:
        """
        Print a concise trend analysis summary.
        
        Example:
            >>> engine = AnalyticalEngine(data)
            >>> engine.initialize()
            >>> engine.perform_comprehensive_trend_analysis()
            >>> engine.print_trend_summary()
        """
        if self.trend_analysis is None:
            print("No trend analysis available. Run perform_comprehensive_trend_analysis() first.")
            return
        
        # Get annual trend
        annual_stats = self.analyze_annual_precipitation_trend()
        
        print("=== TREND ANALYSIS SUMMARY ===")
        print(f"Annual precipitation trend: {annual_stats['slope']:+.2f} mm/year")
        print(f"Trend significance: {annual_stats['significance_level']}")
        
        # Count significant seasonal trends
        significant_count = 0
        total_count = 0
        
        for season in self.trend_analysis.seasonal_slopes:
            for param in self.trend_analysis.seasonal_slopes[season]:
                total_count += 1
                p_val = self.trend_analysis.significance_tests[season][param]
                if p_val < 0.05:
                    significant_count += 1
        
        print(f"Significant seasonal parameter trends: {significant_count}/{total_count}")
        
        recommend_trend = annual_stats['p_value'] < 0.05 or significant_count > 0
        print(f"Recommendation: {'Use trend_mode=True' if recommend_trend else 'Stationary simulation OK'}")
    
    def analyze_annual_precipitation_trend(self, exclude_partial_years: bool = True) -> Dict[str, Any]:
        """
        Analyze annual precipitation trends with comprehensive statistics.
        
        Args:
            exclude_partial_years: Whether to exclude years with incomplete data (default: True)
        
        Returns:
            Dictionary containing trend analysis results including:
            - slope: Trend slope in mm/year
            - p_value: Statistical significance
            - r_squared: Coefficient of determination
            - confidence_interval: 95% confidence interval for slope
            - total_change: Total change over the data period
            - percent_change: Percentage change over the period
            - significance_level: Text description of significance
            - annual_totals: Annual precipitation totals series
            
        Example:
            >>> engine = AnalyticalEngine(data)
            >>> engine.initialize()
            >>> trend_stats = engine.analyze_annual_precipitation_trend()
            >>> print(f"Trend: {trend_stats['slope']:.2f} mm/year")
        """
        import numpy as np
        from scipy import stats
        import pandas as pd
        
        # Calculate annual totals with optional filtering of partial years
        if exclude_partial_years:
            # Filter out years with incomplete data
            annual_data = []
            years_list = []
            
            for year in range(self.data.index.year.min(), self.data.index.year.max() + 1):
                year_data = self.data[self.data.index.year == year]
                
                # Check if year has reasonable amount of data
                # For leap years: 366 days, for regular years: 365 days
                import calendar
                expected_days = 366 if calendar.isleap(year) else 365
                actual_days = len(year_data)
                
                # Include year if it has at least 90% of expected days
                if actual_days >= expected_days * 0.9:
                    annual_data.append(year_data.sum())
                    years_list.append(year)
            
            years = np.array(years_list)
            totals = np.array(annual_data)
            
            # Create pandas series for consistency
            annual_totals = pd.Series(totals, index=pd.to_datetime([f'{year}-12-31' for year in years]))
            
        else:
            # Original behavior - include all years
            annual_totals = self.data.resample('YE').sum()
            years = annual_totals.index.year.values
            totals = annual_totals.values
        
        if len(years) < 3:
            raise ValueError("Need at least 3 years of data for trend analysis")
        
        # Calculate linear trend
        slope, intercept, r_value, p_value, std_err = stats.linregress(years, totals)
        
        # Calculate 95% confidence interval for slope
        t_val = stats.t.ppf(0.975, len(years) - 2)
        slope_ci = t_val * std_err
        
        # Basic statistics
        mean_annual = np.mean(totals)
        std_annual = np.std(totals)
        
        # Trend interpretation
        total_years = years[-1] - years[0]
        total_change = slope * total_years
        percent_change = (total_change / mean_annual) * 100 if mean_annual > 0 else 0
        
        # Determine significance level
        if p_value < 0.01:
            significance_level = "highly significant (p < 0.01)"
        elif p_value < 0.05:
            significance_level = "significant (p < 0.05)"
        elif p_value < 0.10:
            significance_level = "marginally significant (p < 0.10)"
        else:
            significance_level = "not significant (p ≥ 0.10)"
        
        return {
            'slope': slope,
            'intercept': intercept,
            'r_value': r_value,
            'r_squared': r_value**2,
            'p_value': p_value,
            'std_err': std_err,
            'confidence_interval': (slope - slope_ci, slope + slope_ci),
            'mean_annual': mean_annual,
            'std_annual': std_annual,
            'total_change': total_change,
            'percent_change': percent_change,
            'total_years': total_years,
            'significance_level': significance_level,
            'annual_totals': annual_totals,
            'data_period': (years[0], years[-1]),
            'n_years': len(years)
        }
    
    def print_annual_precipitation_trend(self, exclude_partial_years: bool = True) -> None:
        """
        Print formatted annual precipitation trend analysis.
        
        Args:
            exclude_partial_years: Whether to exclude years with incomplete data (default: True)
        
        Example:
            >>> engine = AnalyticalEngine(data)
            >>> engine.initialize()
            >>> engine.print_annual_precipitation_trend()
        """
        trend_stats = self.analyze_annual_precipitation_trend(exclude_partial_years=exclude_partial_years)
        
        print(f"Data period: {trend_stats['data_period'][0]} - {trend_stats['data_period'][1]} ({trend_stats['n_years']} years)")
        print(f"Mean annual precipitation: {trend_stats['mean_annual']:.1f} ± {trend_stats['std_annual']:.1f} mm")
        print()
        print("TREND STATISTICS:")
        print(f"  Slope: {trend_stats['slope']:+.2f} mm/year")
        ci_low, ci_high = trend_stats['confidence_interval']
        print(f"  95% CI: [{ci_low:+.2f}, {ci_high:+.2f}] mm/year")
        print(f"  R-squared: {trend_stats['r_squared']:.3f}")
        print(f"  P-value: {trend_stats['p_value']:.4f}")
        print(f"  Standard error: {trend_stats['std_err']:.2f}")
        print()
        print("TREND INTERPRETATION:")
        print(f"  Total change over period: {trend_stats['total_change']:+.1f} mm ({trend_stats['percent_change']:+.1f}%)")
        print(f"  Trend significance: {trend_stats['significance_level']}")
        
        # Additional interpretation
        if abs(trend_stats['slope']) > 1.0:
            direction = "increasing" if trend_stats['slope'] > 0 else "decreasing"
            print(f"  Annual precipitation is {direction} at {abs(trend_stats['slope']):.1f} mm/year")
        else:
            print(f"  Annual precipitation shows minimal trend ({trend_stats['slope']:+.2f} mm/year)")
    
    def _get_station_name_for_plots(self) -> str:
        """
        Get station name for plot titles from multiple sources.
        
        Returns:
            Station name string for plot titles, or generic title if not available
        """
        try:
            # First check if the data series has station metadata (from CSV loader)
            if hasattr(self.data, 'attrs') and 'station_name' in self.data.attrs:
                return self.data.attrs['station_name']
            
            # Then check manifest metadata (from GHCNWorkflow)
            manifest = self.generate_parameter_manifest()
            
            # Check for station information in metadata
            if 'station_info' in manifest.metadata:
                station_info = manifest.metadata['station_info']
                if isinstance(station_info, dict) and 'name' in station_info:
                    return station_info['name']
            
            # Check for station_id
            if 'station_id' in manifest.metadata:
                return f"Station {manifest.metadata['station_id']}"
            
            # Check for location information
            if 'location' in manifest.metadata:
                location = manifest.metadata['location']
                if isinstance(location, dict):
                    if 'name' in location:
                        return location['name']
                    elif 'city' in location and 'state' in location:
                        return f"{location['city']}, {location['state']}"
            
            # Fallback to generic name
            return "Precipitation Station"
            
        except Exception:
            # If anything fails, use generic name
            return "Precipitation Station"
    
    def _apply_plot_style(self) -> None:
        """
        Apply consistent styling to all plots for publication quality.
        
        Sets matplotlib parameters for consistent appearance across all visualizations.
        """
        import matplotlib.pyplot as plt
        
        # Set consistent style parameters
        plt.rcParams.update({
            'font.size': 10,
            'font.family': 'sans-serif',
            'font.sans-serif': ['Arial', 'DejaVu Sans', 'Liberation Sans'],
            'axes.titlesize': 12,
            'axes.labelsize': 11,
            'axes.linewidth': 1,
            'axes.edgecolor': 'black',
            'axes.facecolor': 'white',
            'axes.grid': True,
            'grid.alpha': 0.3,
            'grid.linewidth': 0.5,
            'legend.fontsize': 9,
            'legend.framealpha': 0.9,
            'legend.edgecolor': 'gray',
            'xtick.labelsize': 10,
            'ytick.labelsize': 10,
            'figure.facecolor': 'white',
            'savefig.facecolor': 'white',
            'savefig.dpi': 300
        })
    
    def _get_consistent_colors(self) -> list:
        """
        Get consistent color palette for all plots.
        
        Returns:
            List of colors for consistent visualization across all plots
        """
        # Professional color palette suitable for scientific publications
        return [
            '#1f77b4',  # Blue
            '#ff7f0e',  # Orange  
            '#2ca02c',  # Green
            '#d62728',  # Red
            '#9467bd',  # Purple
            '#8c564b',  # Brown
            '#e377c2',  # Pink
            '#7f7f7f',  # Gray
            '#bcbd22',  # Olive
            '#17becf'   # Cyan
        ]
    
    def _get_seasonal_colors(self) -> dict:
        """
        Get consistent seasonal color mapping for trend analysis plots.
        
        Returns:
            Dictionary mapping season names to colors
        """
        return {
            'Winter': '#1f77b4',  # Blue
            'Spring': '#2ca02c',  # Green  
            'Summer': '#ff7f0e',  # Orange
            'Fall': '#d62728'     # Red
        }
    
    def _get_significance_style(self, p_value: float) -> tuple:
        """
        Get line style parameters based on statistical significance.
        
        Args:
            p_value: Statistical significance p-value
            
        Returns:
            Tuple of (linestyle, linewidth, alpha, marker) for plotting
        """
        if p_value < 0.01:
            # Highly significant: solid line, thick, opaque, with markers
            return '-', 2.5, 0.9, 'o'
        elif p_value < 0.05:
            # Significant: solid line, medium thickness, semi-opaque, small markers
            return '-', 2.0, 0.8, 's'
        elif p_value < 0.10:
            # Marginally significant: dashed line, medium thickness, semi-transparent
            return '--', 1.5, 0.6, None
        else:
            # Not significant: dotted line, thin, transparent
            return ':', 1.0, 0.4, None
    
    def _get_significance_marker(self, p_value: float) -> str:
        """
        Get significance marker symbol for legends.
        
        Args:
            p_value: Statistical significance p-value
            
        Returns:
            String marker indicating significance level
        """
        if p_value < 0.01:
            return '***'  # Highly significant
        elif p_value < 0.05:
            return '**'   # Significant
        elif p_value < 0.10:
            return '*'    # Marginally significant
        else:
            return 'ns'   # Not significant
    
    def _get_trend_color(self, slope: float, p_value: float) -> str:
        """
        Get trend line color based on slope direction and significance.
        
        Args:
            slope: Trend slope value
            p_value: Statistical significance p-value
            
        Returns:
            Color string for trend line
        """
        if p_value >= 0.05:
            return '#808080'  # Gray for non-significant trends
        elif slope > 0:
            return '#d62728'  # Red for significant increasing trends
        else:
            return '#2ca02c'  # Green for significant decreasing trends
    
    def _add_significance_legend(self, ax, position: str = 'upper left') -> None:
        """
        Add significance level legend to plot.
        
        Args:
            ax: Matplotlib axes object
            position: Legend position ('upper left', 'lower right', etc.)
        """
        import matplotlib.lines as mlines
        
        # Create legend elements for significance levels
        legend_elements = [
            mlines.Line2D([0], [0], color='black', linestyle='-', linewidth=2.5, 
                         marker='o', markersize=4, label='*** p < 0.01 (highly significant)'),
            mlines.Line2D([0], [0], color='black', linestyle='-', linewidth=2.0, 
                         marker='s', markersize=3, label='** p < 0.05 (significant)'),
            mlines.Line2D([0], [0], color='black', linestyle='--', linewidth=1.5, 
                         label='* p < 0.10 (marginally significant)'),
            mlines.Line2D([0], [0], color='black', linestyle=':', linewidth=1.0, 
                         label='ns p ≥ 0.10 (not significant)')
        ]
        
        # Position mapping
        position_map = {
            'upper left': (0.02, 0.98),
            'upper right': (0.98, 0.98),
            'lower left': (0.02, 0.02),
            'lower right': (0.98, 0.02)
        }
        
        if position in position_map:
            x, y = position_map[position]
            ha = 'left' if x < 0.5 else 'right'
            va = 'top' if y > 0.5 else 'bottom'
            
            # Add text-based legend for significance levels
            sig_text = 'Significance Levels:\n*** p < 0.01\n** p < 0.05\n* p < 0.10\nns p ≥ 0.10'
            bbox_props = dict(boxstyle='round,pad=0.3', facecolor='white', 
                             edgecolor='gray', alpha=0.9, linewidth=0.5)
            ax.text(x, y, sig_text, transform=ax.transAxes, fontsize=8,
                   verticalalignment=va, horizontalalignment=ha, bbox=bbox_props)
    
    def _add_significance_summary(self, fig, axes) -> None:
        """
        Add overall significance summary to multi-panel plots.
        
        Args:
            fig: Matplotlib figure object
            axes: Array of matplotlib axes objects
        """
        if self.trend_analysis is None:
            return
        
        # Count significant trends
        total_trends = 0
        significant_trends = 0
        
        for season_slopes in self.trend_analysis.seasonal_slopes.values():
            for param in season_slopes.keys():
                total_trends += 1
                
        for season_pvals in self.trend_analysis.significance_tests.values():
            for p_val in season_pvals.values():
                if p_val < 0.05:
                    significant_trends += 1
        
        # Add summary text
        summary_text = f'Significant Trends: {significant_trends}/{total_trends} parameters'
        fig.text(0.5, 0.02, summary_text, ha='center', va='bottom', 
                fontsize=10, fontweight='bold', 
                bbox=dict(boxstyle='round,pad=0.3', facecolor='lightblue', alpha=0.7))
    
    def plot_annual_precipitation(self, show_plots: bool = True, save_path: Optional[str] = None, 
                                 exclude_partial_years: bool = True) -> None:
        """
        Plot annual total precipitation over the historical period.
        
        Enhanced with consistent styling, improved significance indicators, and automatic
        station name extraction for publication-quality visualizations.
        
        Shows annual precipitation totals with trend line to visualize
        long-term changes in precipitation patterns.
        
        Args:
            show_plots: Whether to display plots (default: True)
            save_path: Optional path to save the plot (e.g., 'annual_precip.png')
            exclude_partial_years: Whether to exclude years with incomplete data (default: True)
            
        Example:
            >>> engine = AnalyticalEngine(data)
            >>> engine.initialize()
            >>> engine.plot_annual_precipitation()  # Shows plot in Jupyter
        """
        import matplotlib.pyplot as plt
        import numpy as np
        from scipy import stats
        
        # Apply consistent styling
        self._apply_plot_style()
        
        # Get station name for title
        station_name = self._get_station_name_for_plots()
        
        # Calculate annual totals with optional filtering of partial years
        if exclude_partial_years:
            # Filter out years with incomplete data
            annual_data = []
            years_list = []
            
            for year in range(self.data.index.year.min(), self.data.index.year.max() + 1):
                year_data = self.data[self.data.index.year == year]
                
                # Check if year has reasonable amount of data
                # For leap years: 366 days, for regular years: 365 days
                import calendar
                expected_days = 366 if calendar.isleap(year) else 365
                actual_days = len(year_data)
                
                # Include year if it has at least 90% of expected days
                if actual_days >= expected_days * 0.9:
                    annual_data.append(year_data.sum())
                    years_list.append(year)
                else:
                    # Log excluded years for transparency
                    self.logger.info(f"Excluding year {year}: {actual_days}/{expected_days} days ({actual_days/expected_days*100:.1f}%)")
            
            years = np.array(years_list)
            totals = np.array(annual_data)
            
            if len(years) == 0:
                raise ValueError("No complete years found in dataset")
                
        else:
            # Original behavior - include all years
            annual_totals = self.data.groupby(self.data.index.year).sum()
            years = annual_totals.index.values
            totals = annual_totals.values
        
        # Create the plot with consistent styling
        fig, ax = plt.subplots(1, 1, figsize=(12, 7))
        
        # Use consistent colors
        colors = self._get_consistent_colors()
        primary_color = colors[0]  # Use first color as primary
        
        # Plot annual totals with enhanced styling
        ax.plot(years, totals, 'o-', color=primary_color, alpha=0.8, 
               linewidth=2, markersize=5, markerfacecolor='white', 
               markeredgecolor=primary_color, markeredgewidth=1.5)
        
        # Add trend line with enhanced significance indicators
        if len(years) > 2:
            slope, intercept, r_value, p_value, std_err = stats.linregress(years, totals)
            trend_line = slope * years + intercept
            
            # Enhanced significance styling
            trend_linestyle, trend_linewidth, trend_alpha, _ = self._get_significance_style(p_value)
            trend_color = self._get_trend_color(slope, p_value)
            
            # Add confidence interval for significant trends
            if p_value < 0.05:
                # Calculate 95% confidence interval
                t_val = stats.t.ppf(0.975, len(years) - 2)
                ci_width = t_val * std_err * np.sqrt(1 + 1/len(years) + (years - np.mean(years))**2 / np.sum((years - np.mean(years))**2))
                
                ax.fill_between(years, trend_line - ci_width, trend_line + ci_width,
                              alpha=0.2, color=trend_color, label='95% Confidence Interval')
            
            # Plot trend line with significance indicators
            significance_marker = self._get_significance_marker(p_value)
            ax.plot(years, trend_line, color=trend_color, linestyle=trend_linestyle, 
                   linewidth=trend_linewidth, alpha=trend_alpha,
                   label=f'Trend {significance_marker}: {slope:.2f} mm/year (p={p_value:.3f})')
        
        # Enhanced formatting with station name in title
        ax.set_title(f'Annual Precipitation Totals - {station_name}', 
                    fontsize=16, fontweight='bold', pad=15)
        ax.set_xlabel('Year', fontsize=12, fontweight='bold')
        ax.set_ylabel('Annual Precipitation (mm)', fontsize=12, fontweight='bold')
        ax.grid(True, alpha=0.3, linestyle='-', linewidth=0.5)
        
        # Enhanced statistics text box with consistent styling
        mean_annual = np.mean(totals)
        std_annual = np.std(totals)
        min_year = years[np.argmin(totals)]
        max_year = years[np.argmax(totals)]
        cv = std_annual / mean_annual if mean_annual > 0 else 0
        
        stats_text = f'Statistics:\n'
        stats_text += f'Mean: {mean_annual:.0f} mm\n'
        stats_text += f'Std: {std_annual:.0f} mm\n'
        stats_text += f'CV: {cv:.3f}\n'
        stats_text += f'Min: {np.min(totals):.0f} mm ({min_year})\n'
        stats_text += f'Max: {np.max(totals):.0f} mm ({max_year})'
        
        # Consistent text box styling
        bbox_props = dict(boxstyle='round,pad=0.5', facecolor='white', 
                         edgecolor='gray', alpha=0.9, linewidth=1)
        ax.text(0.02, 0.98, stats_text, transform=ax.transAxes, 
               verticalalignment='top', bbox=bbox_props, fontsize=10, fontfamily='monospace')
        
        # Add legend with significance information
        if len(years) > 2 and 'slope' in locals():
            legend = ax.legend(loc='upper right', framealpha=0.9, edgecolor='gray')
            legend.get_frame().set_linewidth(1)
        
        # Add significance legend
        self._add_significance_legend(ax, position='lower right')
        
        plt.tight_layout()
        
        if save_path:
            plt.savefig(save_path, dpi=300, bbox_inches='tight', facecolor='white')
            print(f"Plot saved to: {save_path}")
        
        if show_plots:
            plt.show()
        else:
            plt.close()
    
    def generate_parameter_manifest(self) -> ParameterManifest:
        """
        Generate complete parameter manifest for simulation with enhanced workflow integration.
        
        This method creates a comprehensive parameter manifest that preserves metadata
        from the input data source and ensures seamless integration with PrecipitationSimulator.
        
        Returns:
            ParameterManifest with all analysis results and preserved metadata
        """
        # Ensure we have overall parameters
        if self.overall_parameters is None:
            self.calculate_monthly_parameters()
        
        # Start with base metadata
        metadata = {
            'analysis_date': datetime.now().isoformat(),
            'data_period': {
                'start': self.data.index.min().isoformat(),
                'end': self.data.index.max().isoformat(),
                'total_days': len(self.data)
            },
            'wet_day_threshold': self.wet_day_threshold,
            'wet_day_threshold_mm': self.wet_day_threshold_mm,
            'data_completeness': float(1 - self.data.isna().sum() / len(self.data)),
            'total_data_points': len(self.data),
            'workflow_version': '1.0',
            'engine_type': 'analytical'
        }
        
        # Preserve metadata from input data (for workflow integration)
        if hasattr(self.data, 'attrs') and self.data.attrs:
            # Extract key metadata from data source
            source_metadata = self.data.attrs.copy()
            
            # Preserve station information
            if 'station_name' in source_metadata:
                metadata['station_name'] = source_metadata['station_name']
            if 'station_id' in source_metadata:
                metadata['station_id'] = source_metadata['station_id']
            if 'coordinates' in source_metadata:
                metadata['coordinates'] = source_metadata['coordinates']
            if 'latitude' in source_metadata:
                metadata['latitude'] = source_metadata['latitude']
            if 'longitude' in source_metadata:
                metadata['longitude'] = source_metadata['longitude']
            
            # Preserve data source information
            if 'source' in source_metadata:
                metadata['data_source'] = source_metadata['source']
            if 'file_path' in source_metadata:
                metadata['source_file'] = source_metadata['file_path']
            if 'original_units' in source_metadata:
                metadata['original_units'] = source_metadata['original_units']
            
            # Preserve workflow metadata
            if 'workflow_ready' in source_metadata:
                metadata['workflow_ready'] = source_metadata['workflow_ready']
            if 'data_type' in source_metadata:
                metadata['data_type'] = source_metadata['data_type']
            
            # Store complete source metadata for reference
            metadata['source_metadata'] = source_metadata
        else:
            # Default values when no source metadata available
            metadata.update({
                'station_name': 'Unknown Station',
                'data_source': 'unknown',
                'workflow_ready': True
            })
        
        # Add computed statistics for validation
        metadata['computed_statistics'] = {
            'annual_mean': float(self.data.resample('YE').sum().mean()),
            'annual_std': float(self.data.resample('YE').sum().std()),
            'wet_day_frequency': float((self.data > self.wet_day_threshold_mm).mean()),
            'mean_wet_day_amount': float(self.data[self.data > self.wet_day_threshold_mm].mean()) if (self.data > self.wet_day_threshold_mm).any() else 0.0,
            'max_daily_precip': float(self.data.max()),
            'total_precipitation': float(self.data.sum())
        }
        
        # Calculate sliding window stats if available
        sliding_window_stats = None
        if self.window_analysis:
            sliding_window_stats = {
                'window_count': len(self.window_analysis.window_parameters),
                'window_years': getattr(self, '_window_years', 30),
                'analysis_method': 'overlapping_windows',
                'temporal_coverage': {
                    'first_window_start': min(self.window_analysis.window_dates.values(), key=lambda x: x[0])[0].isoformat(),
                    'last_window_end': max(self.window_analysis.window_dates.values(), key=lambda x: x[1])[1].isoformat()
                }
            }
            
            # Add trend analysis metadata if available
            if self.trend_analysis:
                sliding_window_stats.update({
                    'regression_type': self.trend_analysis.regression_type,
                    'trend_validation_performed': self.trend_analysis.validation_results is not None,
                    'significant_trends': self._count_significant_trends()
                })
        
        # Add parameter quality metrics
        metadata['parameter_quality'] = self._assess_parameter_quality()
        
        return ParameterManifest(
            metadata=metadata,
            overall_parameters=self.overall_parameters,
            trend_analysis=self.trend_analysis,
            sliding_window_stats=sliding_window_stats
        )
    
    def _count_significant_trends(self) -> Dict[str, int]:
        """Count significant trends by parameter and season."""
        if not self.trend_analysis:
            return {}
        
        significant_counts = {}
        for season, params in self.trend_analysis.significance_tests.items():
            significant_counts[season] = sum(
                1 for p_value in params.values() if p_value < 0.05
            )
        
        return significant_counts
    
    def _assess_parameter_quality(self) -> Dict[str, Any]:
        """Assess quality of calculated parameters."""
        if not self.overall_parameters:
            return {'quality_score': 0.0, 'issues': ['No parameters calculated']}
        
        issues = []
        quality_metrics = {}
        
        # Check parameter ranges
        for month, params in self.overall_parameters.items():
            if not (0 <= params.p_ww <= 1):
                issues.append(f"Month {month}: P(W|W) out of range [0,1]")
            if not (0 <= params.p_wd <= 1):
                issues.append(f"Month {month}: P(W|D) out of range [0,1]")
            if params.alpha <= 0:
                issues.append(f"Month {month}: Alpha parameter not positive")
            if params.beta <= 0:
                issues.append(f"Month {month}: Beta parameter not positive")
        
        # Calculate quality score
        quality_score = max(0.0, 1.0 - len(issues) * 0.1)
        
        # Add parameter stability metrics
        p_ww_values = [params.p_ww for params in self.overall_parameters.values()]
        p_wd_values = [params.p_wd for params in self.overall_parameters.values()]
        
        quality_metrics.update({
            'quality_score': quality_score,
            'issues': issues,
            'parameter_stability': {
                'p_ww_cv': float(np.std(p_ww_values) / np.mean(p_ww_values)) if np.mean(p_ww_values) > 0 else 0.0,
                'p_wd_cv': float(np.std(p_wd_values) / np.mean(p_wd_values)) if np.mean(p_wd_values) > 0 else 0.0
            },
            'data_adequacy': len(self.data) >= 365 * 10  # At least 10 years of data
        })
        
        return quality_metrics
    
    def perform_comprehensive_trend_analysis(self, window_years: int = 30, 
                                           regression_type: str = 'linear') -> TrendAnalysis:
        """
        Perform comprehensive trend analysis including sliding window analysis and trend extraction.
        
        Args:
            window_years: Size of sliding window in years
            regression_type: Type of regression ('linear' or 'polynomial')
            
        Returns:
            TrendAnalysis with complete trend information
        """
        # First perform sliding window analysis
        window_results = self.perform_sliding_window_analysis(window_years)
        
        # Then extract trends from the windowed results
        trend_results = self.extract_trends(window_results, regression_type)
        
        return trend_results
    
    def _calculate_parameters_for_period(self, data: pd.Series) -> MonthlyParams:
        """
        Calculate WGEN parameters for a specific time period.
        
        Args:
            data: Time series data for the period
            
        Returns:
            MonthlyParams with calculated parameters
        """
        if len(data) == 0:
            # Return default parameters for empty data
            return MonthlyParams(p_ww=0.5, p_wd=0.3, alpha=1.0, beta=5.0)
        
        # Classify wet/dry days
        wet_days = data > self.wet_day_threshold_mm
        
        # Calculate transition probabilities
        p_ww = self._calculate_transition_probability(wet_days, True, True)
        p_wd = self._calculate_transition_probability(wet_days, False, True)
        
        # Calculate Gamma parameters for wet days
        wet_amounts = data[wet_days]
        if len(wet_amounts) >= 2:
            alpha, beta = self._fit_gamma_parameters(wet_amounts)
        else:
            # Default parameters for insufficient wet days
            alpha, beta = 1.0, 5.0
        
        return MonthlyParams(p_ww=p_ww, p_wd=p_wd, alpha=alpha, beta=beta)
    
    def _calculate_transition_probability(self, wet_days: pd.Series, 
                                        from_state: bool, to_state: bool) -> float:
        """
        Calculate Markov chain transition probability.
        
        Args:
            wet_days: Boolean series indicating wet days
            from_state: Previous day state (True=wet, False=dry)
            to_state: Current day state (True=wet, False=dry)
            
        Returns:
            Transition probability
        """
        if len(wet_days) < 2:
            return 0.5  # Default probability
        
        # Find transitions
        prev_state = wet_days.shift(1)
        
        # Count transitions from specified state
        from_count = (prev_state == from_state).sum()
        
        if from_count == 0:
            return 0.5  # Default if no instances of from_state
        
        # Count transitions from_state -> to_state
        transition_count = ((prev_state == from_state) & (wet_days == to_state)).sum()
        
        return transition_count / from_count
    
    def _fit_gamma_parameters(self, wet_amounts: pd.Series) -> tuple:
        """
        Fit Gamma distribution parameters using method of moments.
        
        Args:
            wet_amounts: Precipitation amounts on wet days
            
        Returns:
            Tuple of (alpha, beta) parameters
        """
        if len(wet_amounts) == 0:
            return 1.0, 5.0
        
        # Method of moments estimation
        mean_precip = wet_amounts.mean()
        var_precip = wet_amounts.var()
        
        if var_precip <= 0 or mean_precip <= 0:
            return 1.0, 5.0
        
        # Gamma parameters: mean = alpha * beta, var = alpha * beta^2
        # Therefore: beta = var / mean, alpha = mean / beta
        beta = var_precip / mean_precip
        alpha = mean_precip / beta
        
        # Ensure parameters are positive and reasonable
        alpha = max(0.1, min(alpha, 10.0))
        beta = max(0.1, min(beta, 100.0))
        
        return alpha, beta
    
    def _perform_regression_analysis(self, x_values: list, y_values: list, 
                                   regression_type: str) -> tuple:
        """
        Perform regression analysis on parameter time series.
        
        Args:
            x_values: Time values (years)
            y_values: Parameter values
            regression_type: Type of regression ('linear' or 'polynomial')
            
        Returns:
            Tuple of (slope, p_value)
        """
        x_array = np.array(x_values)
        y_array = np.array(y_values)
        
        if regression_type == 'linear':
            # Linear regression using scipy.stats
            slope, intercept, r_value, p_value, std_err = stats.linregress(x_array, y_array)
            return slope, p_value
            
        elif regression_type == 'polynomial':
            # Polynomial regression (degree 2) with significance testing
            if len(x_values) < 4:  # Need at least 4 points for quadratic
                # Fall back to linear
                slope, intercept, r_value, p_value, std_err = stats.linregress(x_array, y_array)
                return slope, p_value
            
            # Fit polynomial (degree 2)
            coeffs = np.polyfit(x_array, y_array, 2)
            poly_func = np.poly1d(coeffs)
            y_pred = poly_func(x_array)
            
            # Calculate R-squared and F-statistic for significance
            ss_res = np.sum((y_array - y_pred) ** 2)
            ss_tot = np.sum((y_array - np.mean(y_array)) ** 2)
            
            if ss_tot == 0:
                r_squared = 1.0
            else:
                r_squared = 1 - (ss_res / ss_tot)
            
            # F-test for polynomial regression significance
            n = len(y_array)
            p = 3  # Number of parameters (intercept + x + x^2)
            
            if n <= p or ss_res == 0:
                p_value = 0.001  # Assume significant if perfect fit
            else:
                f_stat = (r_squared / (p - 1)) / ((1 - r_squared) / (n - p))
                p_value = 1 - stats.f.cdf(f_stat, p - 1, n - p)
            
            # Return linear component of trend (coefficient of x term)
            linear_slope = coeffs[1] if len(coeffs) > 1 else 0.0
            return linear_slope, p_value
        
        else:
            raise ValueError(f"Unknown regression type: {regression_type}")
    
    def _determine_trend_confidence(self, p_value: float) -> str:
        """
        Determine trend confidence level based on p-value.
        
        Args:
            p_value: Statistical significance p-value
            
        Returns:
            String describing confidence level
        """
        if p_value < 0.01:
            return "High (p < 0.01)"
        elif p_value < 0.05:
            return "Medium (p < 0.05)"
        elif p_value < 0.10:
            return "Low (p < 0.10)"
        else:
            return "Not significant"
    
    def validate_trend_slopes(self, trend_analysis: TrendAnalysis) -> Dict[str, Dict[str, bool]]:
        """
        Validate trend slopes to ensure they are within reasonable bounds.
        
        Args:
            trend_analysis: TrendAnalysis object to validate
            
        Returns:
            Dictionary indicating which slopes are valid
        """
        validation_results = {}
        
        # Define reasonable bounds for parameter changes per year
        bounds = {
            'p_ww': (-0.01, 0.01),  # Transition probabilities: ±1% per year max
            'p_wd': (-0.01, 0.01),
            'alpha': (-0.1, 0.1),   # Gamma parameters: more flexible bounds
            'beta': (-1.0, 1.0)
        }
        
        for season, season_slopes in trend_analysis.seasonal_slopes.items():
            validation_results[season] = {}
            
            for param_name, slope in season_slopes.items():
                if param_name in bounds:
                    min_bound, max_bound = bounds[param_name]
                    is_valid = min_bound <= slope <= max_bound
                    validation_results[season][param_name] = is_valid
                else:
                    validation_results[season][param_name] = True  # Unknown parameter, assume valid
        
        return validation_results
    
    def generate_analysis_report(self) -> Dict[str, Any]:
        """
        Generate comprehensive analysis report with error handling and validation.
        
        Returns:
            Dictionary containing analysis report with error information
        """
        report = {
            'analysis_status': 'success',
            'errors': [],
            'warnings': [],
            'data_quality': {},
            'analysis_results': {}
        }
        
        try:
            # Data quality assessment
            report['data_quality'] = {
                'total_data_points': len(self.data),
                'data_completeness': 1 - self.data.isna().sum() / len(self.data) if len(self.data) > 0 else 0,
                'data_period': [
                    self.data.index.min().isoformat() if len(self.data) > 0 else None,
                    self.data.index.max().isoformat() if len(self.data) > 0 else None
                ],
                'wet_day_fraction': (self.data > self.wet_day_threshold_mm).sum() / len(self.data) if len(self.data) > 0 else 0
            }
            
            # Check data quality
            if len(self.data) == 0:
                report['errors'].append("No data available for analysis")
                report['analysis_status'] = 'failed'
                return report
            
            if report['data_quality']['data_completeness'] < 0.8:
                report['warnings'].append(f"Low data completeness: {report['data_quality']['data_completeness']:.2%}")
            
            if report['data_quality']['wet_day_fraction'] < 0.05:
                report['warnings'].append(f"Very low wet day fraction: {report['data_quality']['wet_day_fraction']:.2%}")
            
            # Overall parameters analysis
            if self.overall_parameters is None:
                self.calculate_monthly_parameters()
            
            report['analysis_results']['overall_parameters_calculated'] = True
            report['analysis_results']['monthly_parameter_count'] = len(self.overall_parameters)
            
            # Sliding window analysis
            if self.window_analysis is not None:
                report['analysis_results']['sliding_window_analysis'] = {
                    'completed': True,
                    'window_count': len(self.window_analysis.window_parameters),
                    'window_years': getattr(self, '_window_years', 'unknown')
                }
            else:
                report['analysis_results']['sliding_window_analysis'] = {'completed': False}
            
            # Trend analysis
            if self.trend_analysis is not None:
                report['analysis_results']['trend_analysis'] = {
                    'completed': True,
                    'regression_type': self.trend_analysis.regression_type,
                    'seasons_analyzed': len(self.trend_analysis.seasonal_slopes),
                    'validation_performed': self.trend_analysis.validation_results is not None
                }
                
                # Check for invalid trend slopes
                if self.trend_analysis.validation_results:
                    invalid_slopes = []
                    for season, season_results in self.trend_analysis.validation_results.items():
                        for param, is_valid in season_results.items():
                            if not is_valid:
                                slope_value = self.trend_analysis.seasonal_slopes[season][param]
                                invalid_slopes.append(f"{season} {param}: {slope_value:.6f}")
                    
                    if invalid_slopes:
                        report['warnings'].append(f"Invalid trend slopes detected: {', '.join(invalid_slopes)}")
            else:
                report['analysis_results']['trend_analysis'] = {'completed': False}
            
        except Exception as e:
            report['analysis_status'] = 'failed'
            report['errors'].append(f"Analysis failed with error: {str(e)}")
        
        return report
    
    def export_results(self, output_dir: str, include_json: bool = True, 
                      include_report: bool = True) -> Dict[str, str]:
        """
        Export analysis results to files with comprehensive error handling.
        
        Args:
            output_dir: Directory to save output files
            include_json: Whether to save parameter manifest as JSON
            include_report: Whether to save analysis report
            
        Returns:
            Dictionary mapping output type to file path
        """
        import os
        from pathlib import Path
        
        output_files = {}
        
        try:
            # Create output directory if it doesn't exist
            Path(output_dir).mkdir(parents=True, exist_ok=True)
            
            # Generate parameter manifest
            manifest = self.generate_parameter_manifest()
            
            if include_json:
                json_path = os.path.join(output_dir, 'parameter_manifest.json')
                manifest.save_to_file(json_path)
                output_files['parameter_manifest'] = json_path
            
            if include_report:
                report = self.generate_analysis_report()
                report_path = os.path.join(output_dir, 'analysis_report.json')
                with open(report_path, 'w') as f:
                    json.dump(report, f, indent=2, default=str)
                output_files['analysis_report'] = report_path
            
        except Exception as e:
            raise RuntimeError(f"Failed to export results: {str(e)}")
        
        return output_files
    
    # ========================================================================
    # SYNTHETIC DATA ANALYSIS CAPABILITIES
    # ========================================================================
    
    def analyze_synthetic_data_ensemble(self, synthetic_results: Dict[str, pd.DataFrame], 
                                      historical_data: Optional[pd.Series] = None,
                                      save_plots: bool = True, 
                                      output_dir: str = 'synthetic_analysis') -> Dict[str, Any]:
        """
        Comprehensive ensemble analysis of synthetic precipitation data.
        
        This method provides complete analysis capabilities for synthetic data generated
        by PrecipitationSimulator, including statistical validation, trend analysis,
        and ensemble statistics across multiple realizations.
        
        Args:
            synthetic_results: Dictionary of simulation results from PrecipitationSimulator
                             (e.g., {'wgen': DataFrame, 'bootstrap_doy': DataFrame})
            historical_data: Optional historical data for validation comparison
            save_plots: Whether to save analysis plots
            output_dir: Directory to save analysis outputs
            
        Returns:
            Dictionary containing comprehensive ensemble analysis results
            
        Example:
            >>> # After running simulation
            >>> simulator = pg.PrecipitationSimulator(parameters=manifest)
            >>> results = simulator.run_monte_carlo_simulation(n_realizations=100)
            >>> 
            >>> # Analyze synthetic data with same engine
            >>> engine = pg.AnalyticalEngine(historical_data)
            >>> ensemble_analysis = engine.analyze_synthetic_data_ensemble(results, historical_data)
        """
        from pathlib import Path
        import matplotlib.pyplot as plt
        
        if save_plots:
            Path(output_dir).mkdir(parents=True, exist_ok=True)
        
        ensemble_analysis = {
            'methods_analyzed': list(synthetic_results.keys()),
            'analysis_date': datetime.now().isoformat(),
            'historical_comparison': historical_data is not None,
            'method_results': {},
            'ensemble_comparison': {},
            'validation_summary': {}
        }
        
        print("=== SYNTHETIC DATA ENSEMBLE ANALYSIS ===")
        print(f"Methods analyzed: {', '.join(synthetic_results.keys())}")
        print(f"Historical comparison: {'Yes' if historical_data is not None else 'No'}")
        print()
        
        # Analyze each simulation method
        for method_name, sim_data in synthetic_results.items():
            print(f"--- {method_name.upper()} METHOD ANALYSIS ---")
            
            method_analysis = self._analyze_single_synthetic_method(
                sim_data, method_name, historical_data
            )
            ensemble_analysis['method_results'][method_name] = method_analysis
            
            # Print summary statistics
            stats = method_analysis['ensemble_statistics']
            print(f"Realizations: {stats['n_realizations']}")
            print(f"Simulation period: {stats['simulation_years']:.1f} years")
            print(f"Annual precipitation: {stats['annual_mean']:.1f} ± {stats['annual_std']:.1f} mm")
            print(f"Wet day frequency: {stats['wet_day_frequency']:.3f} ± {stats['wet_day_frequency_std']:.3f}")
            
            if historical_data is not None:
                validation = method_analysis['historical_validation']
                print(f"Historical validation:")
                print(f"  Annual bias: {validation['annual_bias']:.1f} mm ({validation['annual_bias_percent']:+.1f}%)")
                print(f"  Wet day bias: {validation['wet_day_bias']:+.3f}")
                print(f"  KS test p-value: {validation['ks_test_pvalue']:.4f}")
            print()
        
        # Cross-method ensemble comparison
        if len(synthetic_results) > 1:
            print("--- ENSEMBLE METHOD COMPARISON ---")
            ensemble_comparison = self._compare_ensemble_methods(synthetic_results)
            ensemble_analysis['ensemble_comparison'] = ensemble_comparison
            
            # Print comparison summary
            for comparison in ensemble_comparison['pairwise_comparisons']:
                method1, method2 = comparison['methods']
                print(f"{method1} vs {method2}:")
                print(f"  Annual correlation: {comparison['annual_correlation']:.3f}")
                print(f"  Mean difference: {comparison['mean_annual_difference']:.1f} mm")
                print(f"  Wet day correlation: {comparison['wet_day_correlation']:.3f}")
            print()
        
        # Overall validation summary
        if historical_data is not None:
            print("--- VALIDATION SUMMARY ---")
            validation_summary = self._generate_validation_summary(ensemble_analysis['method_results'])
            ensemble_analysis['validation_summary'] = validation_summary
            
            print(f"Best performing method (annual): {validation_summary['best_annual_method']}")
            print(f"Best performing method (wet days): {validation_summary['best_wet_day_method']}")
            print(f"Overall recommendation: {validation_summary['recommended_method']}")
            print()
        
        # Generate comprehensive plots
        if save_plots:
            print("--- GENERATING ANALYSIS PLOTS ---")
            plot_paths = self._create_synthetic_analysis_plots(
                synthetic_results, historical_data, ensemble_analysis, output_dir
            )
            ensemble_analysis['plot_paths'] = plot_paths
            
            for plot_type, path in plot_paths.items():
                print(f"  {plot_type}: {path}")
            print()
        
        return ensemble_analysis
    
    def _analyze_single_synthetic_method(self, sim_data: pd.DataFrame, 
                                       method_name: str,
                                       historical_data: Optional[pd.Series] = None) -> Dict[str, Any]:
        """Analyze a single synthetic data method."""
        import numpy as np
        from scipy import stats
        
        n_realizations = sim_data.shape[1]
        simulation_years = len(sim_data) / 365.25
        
        # Calculate ensemble statistics
        annual_totals = sim_data.resample('YE').sum()
        monthly_totals = sim_data.resample('ME').sum()
        
        # Wet day analysis
        wet_days = sim_data > self.wet_day_threshold_mm
        wet_day_frequencies = wet_days.resample('YE').mean()
        
        # Ensemble statistics across realizations
        ensemble_stats = {
            'n_realizations': n_realizations,
            'simulation_years': simulation_years,
            'annual_mean': annual_totals.mean().mean(),
            'annual_std': annual_totals.mean().std(),
            'annual_cv': annual_totals.mean().std() / annual_totals.mean().mean(),
            'wet_day_frequency': wet_day_frequencies.mean().mean(),
            'wet_day_frequency_std': wet_day_frequencies.mean().std(),
            'monthly_climatology': monthly_totals.groupby(monthly_totals.index.month).mean().mean(axis=1).to_dict(),
            'annual_variability': {
                'between_realization_std': annual_totals.mean().std(),
                'within_realization_mean_std': annual_totals.std().mean(),
                'total_variance_ratio': annual_totals.mean().std() / annual_totals.std().mean()
            }
        }
        
        # Individual realization analysis
        realization_stats = []
        for i in range(min(10, n_realizations)):  # Analyze first 10 realizations in detail
            real_data = sim_data.iloc[:, i]
            real_annual = real_data.resample('YE').sum()
            real_wet_freq = (real_data > self.wet_day_threshold_mm).mean()
            
            realization_stats.append({
                'realization': i,
                'annual_mean': real_annual.mean(),
                'annual_std': real_annual.std(),
                'wet_day_frequency': real_wet_freq,
                'max_daily': real_data.max(),
                'dry_spell_max': self._calculate_max_dry_spell(real_data)
            })
        
        method_analysis = {
            'method_name': method_name,
            'ensemble_statistics': ensemble_stats,
            'sample_realizations': realization_stats
        }
        
        # Historical validation if available
        if historical_data is not None:
            validation = self._validate_against_historical(sim_data, historical_data)
            method_analysis['historical_validation'] = validation
        
        return method_analysis
    
    def _validate_against_historical(self, sim_data: pd.DataFrame, 
                                   historical_data: pd.Series) -> Dict[str, Any]:
        """Validate synthetic data against historical observations."""
        from scipy import stats
        import numpy as np
        
        # Historical statistics
        hist_annual = historical_data.resample('YE').sum()
        hist_monthly = historical_data.resample('ME').sum()
        hist_wet_freq = (historical_data > self.wet_day_threshold_mm).mean()
        
        # Synthetic statistics (ensemble mean)
        sim_annual = sim_data.resample('YE').sum().mean(axis=1)
        sim_monthly = sim_data.resample('ME').sum().mean(axis=1)
        sim_wet_freq = (sim_data > self.wet_day_threshold_mm).mean().mean()
        
        # Annual validation
        annual_bias = sim_annual.mean() - hist_annual.mean()
        annual_bias_percent = (annual_bias / hist_annual.mean()) * 100
        
        # Wet day validation
        wet_day_bias = sim_wet_freq - hist_wet_freq
        
        # Statistical tests
        # Kolmogorov-Smirnov test for annual distributions
        ks_stat, ks_pvalue = stats.ks_2samp(hist_annual.values, sim_annual.values)
        
        # Monthly correlation
        # Align monthly data for correlation
        hist_monthly_clim = hist_monthly.groupby(hist_monthly.index.month).mean()
        sim_monthly_clim = sim_monthly.groupby(sim_monthly.index.month).mean()
        
        monthly_correlation = np.corrcoef(hist_monthly_clim.values, sim_monthly_clim.values)[0, 1]
        
        # Extreme value validation (95th percentile)
        hist_p95 = historical_data[historical_data > 0].quantile(0.95)
        sim_p95 = sim_data[sim_data > 0].quantile(0.95, axis=1).mean()
        extreme_bias = sim_p95 - hist_p95
        extreme_bias_percent = (extreme_bias / hist_p95) * 100
        
        return {
            'annual_bias': annual_bias,
            'annual_bias_percent': annual_bias_percent,
            'wet_day_bias': wet_day_bias,
            'ks_test_statistic': ks_stat,
            'ks_test_pvalue': ks_pvalue,
            'monthly_correlation': monthly_correlation,
            'extreme_bias': extreme_bias,
            'extreme_bias_percent': extreme_bias_percent,
            'validation_quality': self._assess_validation_quality(annual_bias_percent, wet_day_bias, ks_pvalue)
        }
    
    def _assess_validation_quality(self, annual_bias_percent: float, 
                                 wet_day_bias: float, ks_pvalue: float) -> str:
        """Assess overall validation quality."""
        issues = 0
        
        if abs(annual_bias_percent) > 10:
            issues += 1
        if abs(wet_day_bias) > 0.05:
            issues += 1
        if ks_pvalue < 0.05:
            issues += 1
        
        if issues == 0:
            return "Excellent"
        elif issues == 1:
            return "Good"
        elif issues == 2:
            return "Fair"
        else:
            return "Poor"
    
    def _compare_ensemble_methods(self, synthetic_results: Dict[str, pd.DataFrame]) -> Dict[str, Any]:
        """Compare different ensemble methods."""
        import numpy as np
        from itertools import combinations
        
        methods = list(synthetic_results.keys())
        comparisons = []
        
        for method1, method2 in combinations(methods, 2):
            data1 = synthetic_results[method1]
            data2 = synthetic_results[method2]
            
            # Annual correlation
            annual1 = data1.resample('YE').sum().mean(axis=1)
            annual2 = data2.resample('YE').sum().mean(axis=1)
            annual_corr = np.corrcoef(annual1.values, annual2.values)[0, 1]
            
            # Mean difference
            mean_diff = annual1.mean() - annual2.mean()
            
            # Wet day correlation
            wet1 = (data1 > self.wet_day_threshold_mm).mean(axis=1)
            wet2 = (data2 > self.wet_day_threshold_mm).mean(axis=1)
            wet_corr = np.corrcoef(wet1.values, wet2.values)[0, 1]
            
            comparisons.append({
                'methods': (method1, method2),
                'annual_correlation': annual_corr,
                'mean_annual_difference': mean_diff,
                'wet_day_correlation': wet_corr
            })
        
        return {
            'pairwise_comparisons': comparisons,
            'method_consistency': np.mean([c['annual_correlation'] for c in comparisons])
        }
    
    def _generate_validation_summary(self, method_results: Dict[str, Any]) -> Dict[str, Any]:
        """Generate overall validation summary."""
        if not any('historical_validation' in result for result in method_results.values()):
            return {'message': 'No historical data available for validation'}
        
        # Find best performing methods
        annual_biases = {}
        wet_day_biases = {}
        
        for method, results in method_results.items():
            if 'historical_validation' in results:
                validation = results['historical_validation']
                annual_biases[method] = abs(validation['annual_bias_percent'])
                wet_day_biases[method] = abs(validation['wet_day_bias'])
        
        best_annual = min(annual_biases.keys(), key=lambda k: annual_biases[k])
        best_wet_day = min(wet_day_biases.keys(), key=lambda k: wet_day_biases[k])
        
        # Overall recommendation (simple scoring)
        method_scores = {}
        for method in annual_biases.keys():
            score = annual_biases[method] + wet_day_biases[method] * 100  # Weight wet day bias
            method_scores[method] = score
        
        recommended = min(method_scores.keys(), key=lambda k: method_scores[k])
        
        return {
            'best_annual_method': best_annual,
            'best_wet_day_method': best_wet_day,
            'recommended_method': recommended,
            'method_scores': method_scores
        }
    
    def _calculate_max_dry_spell(self, data: pd.Series) -> int:
        """Calculate maximum dry spell length."""
        dry_days = data <= self.wet_day_threshold_mm
        
        max_spell = 0
        current_spell = 0
        
        for is_dry in dry_days:
            if is_dry:
                current_spell += 1
                max_spell = max(max_spell, current_spell)
            else:
                current_spell = 0
        
        return max_spell
    
    def _create_synthetic_analysis_plots(self, synthetic_results: Dict[str, pd.DataFrame],
                                       historical_data: Optional[pd.Series],
                                       ensemble_analysis: Dict[str, Any],
                                       output_dir: str) -> Dict[str, str]:
        """Create comprehensive synthetic data analysis plots with consistent styling."""
        import matplotlib.pyplot as plt
        import numpy as np
        from pathlib import Path
        
        # Apply consistent styling
        self._apply_plot_style()
        
        plot_paths = {}
        colors = self._get_consistent_colors()
        
        # Get station name for titles
        station_name = self._get_station_name_for_plots()
        
        # 1. Annual precipitation comparison
        fig, axes = plt.subplots(2, 2, figsize=(15, 12))
        fig.suptitle(f'Synthetic Data Ensemble Analysis - {station_name}', 
                    fontsize=16, fontweight='bold', y=0.98)
        
        # Annual totals distribution
        ax = axes[0, 0]
        annual_data = []
        labels = []
        box_colors = []
        
        if historical_data is not None:
            hist_annual = historical_data.resample('YE').sum()
            annual_data.append(hist_annual.values)
            labels.append('Historical')
            box_colors.append(colors[0])
        
        color_idx = 1 if historical_data is not None else 0
        for method, sim_data in synthetic_results.items():
            sim_annual = sim_data.resample('YE').sum()
            # Use ensemble mean for comparison
            annual_data.append(sim_annual.mean(axis=1).values)
            labels.append(f'{method.title()} (Ensemble)')
            box_colors.append(colors[color_idx % len(colors)])
            color_idx += 1
        
        bp = ax.boxplot(annual_data, labels=labels, patch_artist=True)
        for patch, color in zip(bp['boxes'], box_colors):
            patch.set_facecolor(color)
            patch.set_alpha(0.7)
        
        ax.set_ylabel('Annual Precipitation (mm)', fontweight='bold')
        ax.set_title('Annual Precipitation Distribution Comparison', fontweight='bold', pad=10)
        ax.grid(True, alpha=0.3, linestyle='-', linewidth=0.5)
        
        # Monthly climatology comparison
        ax = axes[0, 1]
        months = range(1, 13)
        
        if historical_data is not None:
            hist_monthly_clim = historical_data.groupby(historical_data.index.month).mean()
            ax.plot(months, hist_monthly_clim.values, 'o-', color=colors[0], 
                   label='Historical', linewidth=2.5, markersize=6)
        
        color_idx = 1 if historical_data is not None else 0
        for method, sim_data in synthetic_results.items():
            sim_monthly_clim = sim_data.groupby(sim_data.index.month).mean().mean(axis=1)
            ax.plot(months, sim_monthly_clim.values, 'o-', color=colors[color_idx % len(colors)],
                   label=f'{method.title()}', alpha=0.8, linewidth=2, markersize=5)
            color_idx += 1
        
        ax.set_xlabel('Month', fontweight='bold')
        ax.set_ylabel('Mean Daily Precipitation (mm)', fontweight='bold')
        ax.set_title('Monthly Climatology Comparison', fontweight='bold', pad=10)
        ax.legend(framealpha=0.9, edgecolor='gray')
        ax.grid(True, alpha=0.3, linestyle='-', linewidth=0.5)
        
        # Ensemble spread analysis
        ax = axes[1, 0]
        if len(synthetic_results) > 0:
            method = list(synthetic_results.keys())[0]  # Use first method
            sim_data = synthetic_results[method]
            annual_sims = sim_data.resample('YE').sum()
            
            # Plot ensemble spread
            years = annual_sims.index.year
            ensemble_mean = annual_sims.mean(axis=1)
            ensemble_std = annual_sims.std(axis=1)
            
            ax.fill_between(years, 
                          ensemble_mean - 2*ensemble_std,
                          ensemble_mean + 2*ensemble_std,
                          alpha=0.2, color=colors[1], label='±2σ envelope')
            ax.fill_between(years,
                          ensemble_mean - ensemble_std,
                          ensemble_mean + ensemble_std,
                          alpha=0.4, color=colors[1], label='±1σ envelope')
            ax.plot(years, ensemble_mean, color=colors[1], 
                   label='Ensemble mean', linewidth=2.5)
            
            if historical_data is not None:
                hist_annual = historical_data.resample('YE').sum()
                ax.plot(hist_annual.index.year, hist_annual.values, 'o-', 
                       color=colors[0], label='Historical', alpha=0.8, linewidth=2, markersize=4)
            
            ax.set_xlabel('Year', fontweight='bold')
            ax.set_ylabel('Annual Precipitation (mm)', fontweight='bold')
            ax.set_title(f'Ensemble Spread Analysis ({method.title()})', fontweight='bold', pad=10)
            ax.legend(framealpha=0.9, edgecolor='gray')
            ax.grid(True, alpha=0.3, linestyle='-', linewidth=0.5)
        
        # Validation metrics (if historical data available)
        ax = axes[1, 1]
        if historical_data is not None and 'method_results' in ensemble_analysis:
            methods = []
            annual_biases = []
            wet_day_biases = []
            
            for method, results in ensemble_analysis['method_results'].items():
                if 'historical_validation' in results:
                    validation = results['historical_validation']
                    methods.append(method.title())
                    annual_biases.append(validation['annual_bias_percent'])
                    wet_day_biases.append(validation['wet_day_bias'] * 100)  # Convert to percentage
            
            x = np.arange(len(methods))
            width = 0.35
            
            bars1 = ax.bar(x - width/2, annual_biases, width, 
                          label='Annual Bias (%)', alpha=0.8, color=colors[2])
            bars2 = ax.bar(x + width/2, wet_day_biases, width, 
                          label='Wet Day Bias (%)', alpha=0.8, color=colors[3])
            
            ax.set_xlabel('Method', fontweight='bold')
            ax.set_ylabel('Bias (%)', fontweight='bold')
            ax.set_title('Validation Metrics vs Historical', fontweight='bold', pad=10)
            ax.set_xticks(x)
            ax.set_xticklabels(methods)
            ax.legend(framealpha=0.9, edgecolor='gray')
            ax.grid(True, alpha=0.3, linestyle='-', linewidth=0.5)
            ax.axhline(y=0, color='black', linestyle='-', alpha=0.8, linewidth=1)
        else:
            ax.text(0.5, 0.5, 'No historical data\navailable for validation', 
                   ha='center', va='center', transform=ax.transAxes, fontsize=12,
                   bbox=dict(boxstyle='round,pad=0.5', facecolor='lightgray', alpha=0.7))
            ax.set_title('Validation Metrics', fontweight='bold', pad=10)
        
        plt.tight_layout()
        plt.subplots_adjust(top=0.93)
        plot_path = Path(output_dir) / 'synthetic_ensemble_analysis.png'
        plt.savefig(plot_path, dpi=300, bbox_inches='tight', facecolor='white')
        plt.close()
        plot_paths['ensemble_analysis'] = str(plot_path)
        
        # 2. Method comparison plot (if multiple methods)
        if len(synthetic_results) > 1:
            # Apply consistent styling
            self._apply_plot_style()
            
            fig, axes = plt.subplots(2, 2, figsize=(15, 10))
            fig.suptitle(f'Method Comparison - {station_name}', 
                        fontsize=16, fontweight='bold', y=0.98)
            
            methods = list(synthetic_results.keys())
            
            # Annual correlation matrix
            ax = axes[0, 0]
            n_methods = len(methods)
            corr_matrix = np.eye(n_methods)
            
            for i, method1 in enumerate(methods):
                for j, method2 in enumerate(methods):
                    if i != j:
                        annual1 = synthetic_results[method1].resample('YE').sum().mean(axis=1)
                        annual2 = synthetic_results[method2].resample('YE').sum().mean(axis=1)
                        corr_matrix[i, j] = np.corrcoef(annual1.values, annual2.values)[0, 1]
            
            im = ax.imshow(corr_matrix, cmap='RdBu_r', vmin=-1, vmax=1)
            ax.set_xticks(range(n_methods))
            ax.set_yticks(range(n_methods))
            ax.set_xticklabels([m.title() for m in methods])
            ax.set_yticklabels([m.title() for m in methods])
            ax.set_title('Annual Precipitation Correlation Matrix', fontweight='bold', pad=10)
            
            # Add correlation values to plot
            for i in range(n_methods):
                for j in range(n_methods):
                    text_color = 'white' if abs(corr_matrix[i, j]) > 0.5 else 'black'
                    ax.text(j, i, f'{corr_matrix[i, j]:.2f}', 
                           ha='center', va='center', color=text_color, fontweight='bold')
            
            plt.colorbar(im, ax=ax)
            
            # Method statistics comparison
            ax = axes[0, 1]
            stats_data = []
            for method in methods:
                annual_data = synthetic_results[method].resample('YE').sum().mean(axis=1)
                stats_data.append(annual_data.values)
            
            bp = ax.boxplot(stats_data, labels=[m.title() for m in methods], patch_artist=True)
            for i, patch in enumerate(bp['boxes']):
                patch.set_facecolor(colors[i % len(colors)])
                patch.set_alpha(0.7)
            
            ax.set_ylabel('Annual Precipitation (mm)', fontweight='bold')
            ax.set_title('Method Comparison - Annual Totals', fontweight='bold', pad=10)
            ax.grid(True, alpha=0.3, linestyle='-', linewidth=0.5)
            
            # Wet day frequency comparison
            ax = axes[1, 0]
            wet_freq_data = []
            for method in methods:
                wet_freq = (synthetic_results[method] > self.wet_day_threshold_mm).mean(axis=1)
                wet_freq_data.append(wet_freq.values)
            
            bp = ax.boxplot(wet_freq_data, labels=[m.title() for m in methods], patch_artist=True)
            for i, patch in enumerate(bp['boxes']):
                patch.set_facecolor(colors[i % len(colors)])
                patch.set_alpha(0.7)
            
            ax.set_ylabel('Wet Day Frequency', fontweight='bold')
            ax.set_title('Method Comparison - Wet Day Frequency', fontweight='bold', pad=10)
            ax.grid(True, alpha=0.3, linestyle='-', linewidth=0.5)
            
            # Ensemble variability comparison
            ax = axes[1, 1]
            variability_data = []
            for method in methods:
                annual_sims = synthetic_results[method].resample('YE').sum()
                # Calculate coefficient of variation across realizations
                cv_values = annual_sims.std(axis=1) / annual_sims.mean(axis=1)
                variability_data.append(cv_values.values)
            
            bp = ax.boxplot(variability_data, labels=[m.title() for m in methods], patch_artist=True)
            for i, patch in enumerate(bp['boxes']):
                patch.set_facecolor(colors[i % len(colors)])
                patch.set_alpha(0.7)
            
            ax.set_ylabel('Coefficient of Variation', fontweight='bold')
            ax.set_title('Method Comparison - Ensemble Variability', fontweight='bold', pad=10)
            ax.grid(True, alpha=0.3, linestyle='-', linewidth=0.5)
            
            plt.tight_layout()
            plt.subplots_adjust(top=0.93)
            plot_path = Path(output_dir) / 'method_comparison.png'
            plt.savefig(plot_path, dpi=300, bbox_inches='tight', facecolor='white')
            plt.close()
            plot_paths['method_comparison'] = str(plot_path)
        
        return plot_paths
    
    def validate_synthetic_vs_historical(self, synthetic_data: pd.Series, 
                                       historical_data: pd.Series,
                                       save_plots: bool = True,
                                       output_dir: str = 'validation') -> Dict[str, Any]:
        """
        Comprehensive validation of synthetic data against historical observations.
        
        This method provides detailed statistical validation between synthetic and
        historical precipitation data, supporting the requirement for comprehensive
        validation between synthetic and historical data.
        
        Args:
            synthetic_data: Single realization of synthetic precipitation data
            historical_data: Historical precipitation observations
            save_plots: Whether to save validation plots
            output_dir: Directory to save validation outputs
            
        Returns:
            Dictionary containing comprehensive validation results
            
        Example:
            >>> # After simulation
            >>> synthetic_series = results['wgen'].iloc[:, 0]  # First realization
            >>> 
            >>> # Validate against historical
            >>> validation = engine.validate_synthetic_vs_historical(synthetic_series, historical_data)
            >>> print(f"Validation quality: {validation['overall_quality']}")
        """
        from pathlib import Path
        from scipy import stats
        import matplotlib.pyplot as plt
        import numpy as np
        
        if save_plots:
            Path(output_dir).mkdir(parents=True, exist_ok=True)
        
        print("=== SYNTHETIC VS HISTORICAL VALIDATION ===")
        
        # Basic statistics comparison
        validation_results = {
            'validation_date': datetime.now().isoformat(),
            'data_periods': {
                'synthetic': (synthetic_data.index.min().isoformat(), synthetic_data.index.max().isoformat()),
                'historical': (historical_data.index.min().isoformat(), historical_data.index.max().isoformat())
            },
            'basic_statistics': {},
            'distribution_tests': {},
            'temporal_analysis': {},
            'extreme_events': {},
            'overall_assessment': {}
        }
        
        # 1. Basic statistics
        print("--- BASIC STATISTICS COMPARISON ---")
        
        # Annual statistics
        syn_annual = synthetic_data.resample('YE').sum()
        hist_annual = historical_data.resample('YE').sum()
        
        # Daily statistics
        syn_daily_stats = {
            'mean': synthetic_data.mean(),
            'std': synthetic_data.std(),
            'max': synthetic_data.max(),
            'wet_day_freq': (synthetic_data > self.wet_day_threshold_mm).mean()
        }
        
        hist_daily_stats = {
            'mean': historical_data.mean(),
            'std': historical_data.std(),
            'max': historical_data.max(),
            'wet_day_freq': (historical_data > self.wet_day_threshold_mm).mean()
        }
        
        basic_stats = {
            'annual_mean': {'synthetic': syn_annual.mean(), 'historical': hist_annual.mean()},
            'annual_std': {'synthetic': syn_annual.std(), 'historical': hist_annual.std()},
            'daily_mean': {'synthetic': syn_daily_stats['mean'], 'historical': hist_daily_stats['mean']},
            'daily_std': {'synthetic': syn_daily_stats['std'], 'historical': hist_daily_stats['std']},
            'wet_day_frequency': {'synthetic': syn_daily_stats['wet_day_freq'], 'historical': hist_daily_stats['wet_day_freq']},
            'max_daily': {'synthetic': syn_daily_stats['max'], 'historical': hist_daily_stats['max']}
        }
        
        # Calculate biases
        for stat_name, values in basic_stats.items():
            bias = values['synthetic'] - values['historical']
            bias_percent = (bias / values['historical']) * 100 if values['historical'] != 0 else 0
            basic_stats[stat_name]['bias'] = bias
            basic_stats[stat_name]['bias_percent'] = bias_percent
        
        validation_results['basic_statistics'] = basic_stats
        
        # Print basic statistics
        print(f"Annual precipitation:")
        print(f"  Historical: {hist_annual.mean():.1f} ± {hist_annual.std():.1f} mm")
        print(f"  Synthetic:  {syn_annual.mean():.1f} ± {syn_annual.std():.1f} mm")
        print(f"  Bias: {basic_stats['annual_mean']['bias']:+.1f} mm ({basic_stats['annual_mean']['bias_percent']:+.1f}%)")
        print()
        
        print(f"Wet day frequency:")
        print(f"  Historical: {hist_daily_stats['wet_day_freq']:.3f}")
        print(f"  Synthetic:  {syn_daily_stats['wet_day_freq']:.3f}")
        print(f"  Bias: {basic_stats['wet_day_frequency']['bias']:+.3f}")
        print()
        
        # 2. Distribution tests
        print("--- DISTRIBUTION TESTS ---")
        
        # Kolmogorov-Smirnov test for annual distributions
        ks_annual_stat, ks_annual_p = stats.ks_2samp(hist_annual.values, syn_annual.values)
        
        # Mann-Whitney U test for daily distributions (non-parametric)
        mw_daily_stat, mw_daily_p = stats.mannwhitneyu(
            historical_data.values, synthetic_data.values, alternative='two-sided'
        )
        
        # Chi-square test for wet/dry day patterns
        hist_wet = (historical_data > self.wet_day_threshold_mm).sum()
        hist_dry = len(historical_data) - hist_wet
        syn_wet = (synthetic_data > self.wet_day_threshold_mm).sum()
        syn_dry = len(synthetic_data) - syn_wet
        
        chi2_stat, chi2_p = stats.chi2_contingency([[hist_wet, hist_dry], [syn_wet, syn_dry]])[:2]
        
        distribution_tests = {
            'ks_annual': {'statistic': ks_annual_stat, 'p_value': ks_annual_p},
            'mannwhitney_daily': {'statistic': mw_daily_stat, 'p_value': mw_daily_p},
            'chi2_wet_dry': {'statistic': chi2_stat, 'p_value': chi2_p}
        }
        
        validation_results['distribution_tests'] = distribution_tests
        
        print(f"Kolmogorov-Smirnov (annual): p = {ks_annual_p:.4f}")
        print(f"Mann-Whitney U (daily): p = {mw_daily_p:.4f}")
        print(f"Chi-square (wet/dry): p = {chi2_p:.4f}")
        print()
        
        # 3. Temporal analysis
        print("--- TEMPORAL ANALYSIS ---")
        
        # Monthly climatology correlation
        hist_monthly_clim = historical_data.groupby(historical_data.index.month).mean()
        syn_monthly_clim = synthetic_data.groupby(synthetic_data.index.month).mean()
        monthly_corr = np.corrcoef(hist_monthly_clim.values, syn_monthly_clim.values)[0, 1]
        
        # Seasonal analysis
        seasons = {'Winter': [12, 1, 2], 'Spring': [3, 4, 5], 'Summer': [6, 7, 8], 'Fall': [9, 10, 11]}
        seasonal_analysis = {}
        
        for season_name, months in seasons.items():
            hist_seasonal = historical_data[historical_data.index.month.isin(months)].resample('YE').sum()
            syn_seasonal = synthetic_data[synthetic_data.index.month.isin(months)].resample('YE').sum()
            
            seasonal_bias = syn_seasonal.mean() - hist_seasonal.mean()
            seasonal_bias_percent = (seasonal_bias / hist_seasonal.mean()) * 100 if hist_seasonal.mean() != 0 else 0
            
            seasonal_analysis[season_name] = {
                'historical_mean': hist_seasonal.mean(),
                'synthetic_mean': syn_seasonal.mean(),
                'bias': seasonal_bias,
                'bias_percent': seasonal_bias_percent
            }
        
        temporal_analysis = {
            'monthly_correlation': monthly_corr,
            'seasonal_analysis': seasonal_analysis
        }
        
        validation_results['temporal_analysis'] = temporal_analysis
        
        print(f"Monthly climatology correlation: {monthly_corr:.3f}")
        print("Seasonal biases:")
        for season, stats in seasonal_analysis.items():
            print(f"  {season}: {stats['bias']:+.1f} mm ({stats['bias_percent']:+.1f}%)")
        print()
        
        # 4. Extreme events analysis
        print("--- EXTREME EVENTS ANALYSIS ---")
        
        # Percentile analysis
        percentiles = [90, 95, 99]
        extreme_analysis = {}
        
        for p in percentiles:
            hist_p = historical_data[historical_data > 0].quantile(p/100)
            syn_p = synthetic_data[synthetic_data > 0].quantile(p/100)
            
            extreme_bias = syn_p - hist_p
            extreme_bias_percent = (extreme_bias / hist_p) * 100 if hist_p != 0 else 0
            
            extreme_analysis[f'p{p}'] = {
                'historical': hist_p,
                'synthetic': syn_p,
                'bias': extreme_bias,
                'bias_percent': extreme_bias_percent
            }
        
        # Maximum dry spell analysis
        hist_max_dry = self._calculate_max_dry_spell(historical_data)
        syn_max_dry = self._calculate_max_dry_spell(synthetic_data)
        
        extreme_analysis['max_dry_spell'] = {
            'historical': hist_max_dry,
            'synthetic': syn_max_dry,
            'bias': syn_max_dry - hist_max_dry
        }
        
        validation_results['extreme_events'] = extreme_analysis
        
        print("Extreme precipitation percentiles:")
        for p in percentiles:
            stats = extreme_analysis[f'p{p}']
            print(f"  {p}th percentile: {stats['bias']:+.1f} mm ({stats['bias_percent']:+.1f}%)")
        
        print(f"Maximum dry spell: {syn_max_dry} vs {hist_max_dry} days (bias: {syn_max_dry - hist_max_dry:+d})")
        print()
        
        # 5. Overall assessment
        print("--- OVERALL ASSESSMENT ---")
        
        # Calculate validation score
        validation_score = self._calculate_validation_score(validation_results)
        overall_quality = self._determine_validation_quality(validation_score)
        
        validation_results['overall_assessment'] = {
            'validation_score': validation_score,
            'quality_rating': overall_quality,
            'recommendations': self._generate_validation_recommendations(validation_results)
        }
        
        print(f"Validation score: {validation_score:.2f}/100")
        print(f"Overall quality: {overall_quality}")
        print("Recommendations:")
        for rec in validation_results['overall_assessment']['recommendations']:
            print(f"  • {rec}")
        print()
        
        # Generate validation plots
        if save_plots:
            print("--- GENERATING VALIDATION PLOTS ---")
            plot_paths = self._create_validation_plots(
                synthetic_data, historical_data, validation_results, output_dir
            )
            validation_results['plot_paths'] = plot_paths
            
            for plot_type, path in plot_paths.items():
                print(f"  {plot_type}: {path}")
        
        return validation_results
    
    def _calculate_validation_score(self, validation_results: Dict[str, Any]) -> float:
        """Calculate overall validation score (0-100)."""
        score = 100.0
        
        # Penalize based on biases
        basic_stats = validation_results['basic_statistics']
        
        # Annual bias penalty (up to -20 points)
        annual_bias = abs(basic_stats['annual_mean']['bias_percent'])
        score -= min(20, annual_bias * 2)
        
        # Wet day bias penalty (up to -15 points)
        wet_day_bias = abs(basic_stats['wet_day_frequency']['bias'])
        score -= min(15, wet_day_bias * 300)
        
        # Distribution test penalties
        dist_tests = validation_results['distribution_tests']
        
        # KS test penalty (up to -20 points)
        if dist_tests['ks_annual']['p_value'] < 0.05:
            score -= 20
        elif dist_tests['ks_annual']['p_value'] < 0.10:
            score -= 10
        
        # Chi-square test penalty (up to -15 points)
        if dist_tests['chi2_wet_dry']['p_value'] < 0.05:
            score -= 15
        elif dist_tests['chi2_wet_dry']['p_value'] < 0.10:
            score -= 7
        
        # Monthly correlation bonus/penalty (up to ±10 points)
        monthly_corr = validation_results['temporal_analysis']['monthly_correlation']
        if monthly_corr > 0.9:
            score += 10
        elif monthly_corr < 0.7:
            score -= 10
        
        # Extreme events penalty (up to -20 points)
        extreme_events = validation_results['extreme_events']
        extreme_penalty = 0
        for p in ['p90', 'p95', 'p99']:
            if p in extreme_events:
                bias_percent = abs(extreme_events[p]['bias_percent'])
                extreme_penalty += min(7, bias_percent * 0.5)
        
        score -= extreme_penalty
        
        return max(0, min(100, score))
    
    def _determine_validation_quality(self, score: float) -> str:
        """Determine validation quality based on score."""
        if score >= 85:
            return "Excellent"
        elif score >= 70:
            return "Good"
        elif score >= 55:
            return "Fair"
        elif score >= 40:
            return "Poor"
        else:
            return "Very Poor"
    
    def _generate_validation_recommendations(self, validation_results: Dict[str, Any]) -> List[str]:
        """Generate recommendations based on validation results."""
        recommendations = []
        
        basic_stats = validation_results['basic_statistics']
        
        # Annual bias recommendations
        annual_bias = basic_stats['annual_mean']['bias_percent']
        if abs(annual_bias) > 10:
            if annual_bias > 0:
                recommendations.append("Synthetic data shows positive annual bias - consider adjusting parameters")
            else:
                recommendations.append("Synthetic data shows negative annual bias - consider adjusting parameters")
        
        # Wet day frequency recommendations
        wet_day_bias = basic_stats['wet_day_frequency']['bias']
        if abs(wet_day_bias) > 0.05:
            if wet_day_bias > 0:
                recommendations.append("Too many wet days in synthetic data - check transition probabilities")
            else:
                recommendations.append("Too few wet days in synthetic data - check transition probabilities")
        
        # Distribution test recommendations
        dist_tests = validation_results['distribution_tests']
        if dist_tests['ks_annual']['p_value'] < 0.05:
            recommendations.append("Annual distributions differ significantly - review parameter estimation")
        
        # Extreme events recommendations
        extreme_events = validation_results['extreme_events']
        for p in ['p95', 'p99']:
            if p in extreme_events and abs(extreme_events[p]['bias_percent']) > 20:
                recommendations.append(f"Large bias in {p} percentile - check Gamma parameter estimation")
        
        # Monthly correlation recommendations
        monthly_corr = validation_results['temporal_analysis']['monthly_correlation']
        if monthly_corr < 0.8:
            recommendations.append("Poor monthly climatology correlation - review seasonal parameter variation")
        
        if not recommendations:
            recommendations.append("Validation results are satisfactory - synthetic data represents historical patterns well")
        
        return recommendations
    
    def _create_validation_plots(self, synthetic_data: pd.Series, 
                               historical_data: pd.Series,
                               validation_results: Dict[str, Any],
                               output_dir: str) -> Dict[str, str]:
        """Create comprehensive validation plots."""
        import matplotlib.pyplot as plt
        import numpy as np
        from pathlib import Path
        
        plot_paths = {}
        
        # 1. Main validation plot
        fig, axes = plt.subplots(2, 3, figsize=(18, 12))
        
        # Annual precipitation comparison
        ax = axes[0, 0]
        hist_annual = historical_data.resample('YE').sum()
        syn_annual = synthetic_data.resample('YE').sum()
        
        ax.boxplot([hist_annual.values, syn_annual.values], 
                  labels=['Historical', 'Synthetic'])
        ax.set_ylabel('Annual Precipitation (mm)')
        ax.set_title('Annual Precipitation Distribution')
        ax.grid(True, alpha=0.3)
        
        # Monthly climatology
        ax = axes[0, 1]
        months = range(1, 13)
        hist_monthly_clim = historical_data.groupby(historical_data.index.month).mean()
        syn_monthly_clim = synthetic_data.groupby(synthetic_data.index.month).mean()
        
        ax.plot(months, hist_monthly_clim.values, 'ko-', label='Historical', linewidth=2)
        ax.plot(months, syn_monthly_clim.values, 'ro-', label='Synthetic', linewidth=2)
        ax.set_xlabel('Month')
        ax.set_ylabel('Mean Daily Precipitation (mm)')
        ax.set_title('Monthly Climatology')
        ax.legend()
        ax.grid(True, alpha=0.3)
        
        # Wet day frequency by month
        ax = axes[0, 2]
        hist_wet_freq = (historical_data > self.wet_day_threshold_mm).groupby(historical_data.index.month).mean()
        syn_wet_freq = (synthetic_data > self.wet_day_threshold_mm).groupby(synthetic_data.index.month).mean()
        
        ax.plot(months, hist_wet_freq.values, 'ko-', label='Historical', linewidth=2)
        ax.plot(months, syn_wet_freq.values, 'ro-', label='Synthetic', linewidth=2)
        ax.set_xlabel('Month')
        ax.set_ylabel('Wet Day Frequency')
        ax.set_title('Seasonal Wet Day Frequency')
        ax.legend()
        ax.grid(True, alpha=0.3)
        
        # Precipitation amount distributions
        ax = axes[1, 0]
        hist_wet = historical_data[historical_data > self.wet_day_threshold_mm]
        syn_wet = synthetic_data[synthetic_data > self.wet_day_threshold_mm]
        
        bins = np.logspace(np.log10(0.1), np.log10(max(hist_wet.max(), syn_wet.max())), 30)
        ax.hist(hist_wet.values, bins=bins, alpha=0.7, label='Historical', density=True)
        ax.hist(syn_wet.values, bins=bins, alpha=0.7, label='Synthetic', density=True)
        ax.set_xscale('log')
        ax.set_xlabel('Precipitation Amount (mm)')
        ax.set_ylabel('Density')
        ax.set_title('Wet Day Amount Distribution')
        ax.legend()
        ax.grid(True, alpha=0.3)
        
        # Q-Q plot for annual precipitation
        ax = axes[1, 1]
        from scipy import stats
        stats.probplot(syn_annual.values, dist=stats.norm, 
                      sparams=(hist_annual.mean(), hist_annual.std()), plot=ax)
        ax.set_title('Q-Q Plot: Annual Precipitation')
        ax.grid(True, alpha=0.3)
        
        # Validation metrics summary
        ax = axes[1, 2]
        metrics = ['Annual\nBias (%)', 'Wet Day\nBias', 'Monthly\nCorr', 'KS Test\np-value']
        values = [
            validation_results['basic_statistics']['annual_mean']['bias_percent'],
            validation_results['basic_statistics']['wet_day_frequency']['bias'] * 100,
            validation_results['temporal_analysis']['monthly_correlation'] * 100,
            validation_results['distribution_tests']['ks_annual']['p_value'] * 100
        ]
        
        colors = ['red' if abs(v) > 10 else 'orange' if abs(v) > 5 else 'green' for v in values[:2]]
        colors.extend(['green' if values[2] > 80 else 'orange' if values[2] > 60 else 'red'])
        colors.append('green' if values[3] > 5 else 'red')
        
        bars = ax.bar(metrics, values, color=colors, alpha=0.7)
        ax.set_ylabel('Value')
        ax.set_title('Validation Metrics Summary')
        ax.grid(True, alpha=0.3)
        ax.axhline(y=0, color='k', linestyle='-', alpha=0.5)
        
        # Add value labels on bars
        for bar, value in zip(bars, values):
            height = bar.get_height()
            ax.text(bar.get_x() + bar.get_width()/2., height + (1 if height >= 0 else -3),
                   f'{value:.1f}', ha='center', va='bottom' if height >= 0 else 'top')
        
        plt.tight_layout()
        plot_path = Path(output_dir) / 'validation_comprehensive.png'
        plt.savefig(plot_path, dpi=300, bbox_inches='tight')
        plt.close()
        plot_paths['comprehensive_validation'] = str(plot_path)
        
        # 2. Time series comparison plot
        fig, axes = plt.subplots(3, 1, figsize=(15, 12))
        
        # Annual time series
        ax = axes[0]
        years_hist = hist_annual.index.year
        years_syn = syn_annual.index.year
        
        ax.plot(years_hist, hist_annual.values, 'ko-', label='Historical', alpha=0.7)
        ax.plot(years_syn, syn_annual.values, 'ro-', label='Synthetic', alpha=0.7)
        ax.set_ylabel('Annual Precipitation (mm)')
        ax.set_title('Annual Precipitation Time Series')
        ax.legend()
        ax.grid(True, alpha=0.3)
        
        # Monthly time series (first 5 years)
        ax = axes[1]
        hist_monthly = historical_data.resample('ME').sum()
        syn_monthly = synthetic_data.resample('ME').sum()
        
        # Show first 60 months (5 years)
        n_months = min(60, len(hist_monthly), len(syn_monthly))
        
        ax.plot(hist_monthly.index[:n_months], hist_monthly.values[:n_months], 
               'k-', label='Historical', linewidth=1.5)
        ax.plot(syn_monthly.index[:n_months], syn_monthly.values[:n_months], 
               'r-', label='Synthetic', linewidth=1.5)
        ax.set_ylabel('Monthly Precipitation (mm)')
        ax.set_title('Monthly Precipitation Time Series (First 5 Years)')
        ax.legend()
        ax.grid(True, alpha=0.3)
        
        # Daily time series (first year)
        ax = axes[2]
        hist_daily_year1 = historical_data.iloc[:365] if len(historical_data) >= 365 else historical_data
        syn_daily_year1 = synthetic_data.iloc[:365] if len(synthetic_data) >= 365 else synthetic_data
        
        ax.plot(hist_daily_year1.index, hist_daily_year1.values, 'k-', label='Historical', alpha=0.7)
        ax.plot(syn_daily_year1.index, syn_daily_year1.values, 'r-', label='Synthetic', alpha=0.7)
        ax.set_xlabel('Date')
        ax.set_ylabel('Daily Precipitation (mm)')
        ax.set_title('Daily Precipitation Time Series (First Year)')
        ax.legend()
        ax.grid(True, alpha=0.3)
        
        plt.tight_layout()
        plot_path = Path(output_dir) / 'validation_timeseries.png'
        plt.savefig(plot_path, dpi=300, bbox_inches='tight')
        plt.close()
        plot_paths['timeseries_validation'] = str(plot_path)
        
        return plot_paths