#!/usr/bin/env python3
"""
Ad Inventory Forecasting Library - Usage Examples

This script demonstrates the key features of the ad_inventory_forecast library:
1. Generate synthetic ad traffic data
2. Fit the InventoryForecaster for organic baseline forecasting
3. Use AnalogEventForecaster for event-driven spikes
4. Run AutoTuner to select the best model
5. Use DemographicReconciler for segment forecasting
6. Evaluate models with walk-forward validation

Run with: python -m ad_inventory_forecast.examples.usage_example
"""

import numpy as np
import pandas as pd


def main():
    """Run all usage examples."""
    print("=" * 60)
    print("Ad Inventory Forecasting Library - Usage Examples")
    print("=" * 60)

    # Example 1: Generate synthetic data
    print("\n" + "=" * 60)
    print("Example 1: Generate Synthetic Ad Traffic Data")
    print("=" * 60)

    from ad_inventory_forecast.utils.synthetic import generate_dummy_data

    # Generate 2 years of daily ad traffic with trend, seasonality, and events
    y = generate_dummy_data(
        n_days=730,
        base_level=100000,
        trend_slope=0.0005,
        weekly_amplitude=0.2,
        annual_amplitude=0.1,
        noise_level=0.05,
        event_dates=["2023-02-12", "2024-02-11"],  # Super Bowl dates
        event_multiplier=4.0,
        event_duration=2,
        start_date="2023-01-01",
        random_seed=42,
    )

    print(f"Generated {len(y)} days of synthetic data")
    print(f"Date range: {y.index[0].date()} to {y.index[-1].date()}")
    print(f"Mean impressions: {y.mean():,.0f}")
    print(f"Range: {y.min():,.0f} to {y.max():,.0f}")

    # Example 2: InventoryForecaster for organic baseline
    print("\n" + "=" * 60)
    print("Example 2: InventoryForecaster (Growth-Seasonality Decomposition)")
    print("=" * 60)

    from ad_inventory_forecast.models.decomposition import InventoryForecaster

    # Use first 18 months for training
    train_end = "2024-06-30"
    y_train = y.loc[:train_end]
    y_test = y.loc[train_end:].iloc[1:31]  # Next 30 days

    print(f"Training on {len(y_train)} days, testing on {len(y_test)} days")

    # Fit the forecaster
    forecaster = InventoryForecaster(
        model="auto",
        trend="auto",
        seasonal_periods=[7],  # Weekly seasonality
        trend_dampening=True,
    )
    forecaster.fit(y_train)

    # Get diagnostics
    diag = forecaster.get_diagnostics()
    print(f"\nModel selected: {diag['model']}")
    print(f"Trend type: {diag['trend_type']}")
    print(f"Trend strength: {diag['trend_strength']:.2f}")
    print(f"Seasonal strength: {diag['seasonal_strength']:.2f}")

    # Generate forecast (SDK-compliant: returns DataFrame)
    forecast_df = forecaster.predict(horizon=30)

    print(f"\nForecast for next 30 days:")
    print(f"  Mean: {forecast_df['predicted_impressions'].mean():,.0f}")
    print(f"  Range: {forecast_df['predicted_impressions'].min():,.0f} to {forecast_df['predicted_impressions'].max():,.0f}")
    print(f"\nForecast DataFrame columns: {list(forecast_df.columns)}")

    # Calculate accuracy
    from ad_inventory_forecast.backtesting.metrics import calculate_smape, calculate_mase

    forecast_values = forecast_df['predicted_impressions'].values
    if len(y_test) >= len(forecast_df):
        smape = calculate_smape(y_test.values[:len(forecast_df)], forecast_values)
        mase = calculate_mase(
            y_test.values[:len(forecast_df)],
            forecast_values,
            y_train.values,
            period=7,
        )
        print(f"\nAccuracy on test set:")
        print(f"  SMAPE: {smape:.2f}%")
        print(f"  MASE: {mase:.3f}")

    # Example 3: AnalogEventForecaster for event spikes
    print("\n" + "=" * 60)
    print("Example 3: AnalogEventForecaster (Copy Model for Events)")
    print("=" * 60)

    from ad_inventory_forecast.models.analog import AnalogEventForecaster

    # Forecast Super Bowl 2024 using Super Bowl 2023 as analog
    event_forecaster = AnalogEventForecaster(
        source_window=("2023-02-12", "2023-02-13"),  # Super Bowl 2023
        target_window=("2024-02-11", "2024-02-12"),  # Super Bowl 2024
        use_dtw=True,
        scale_by_trend=True,
    )

    event_forecaster.fit(y.loc[:"2024-02-10"])
    event_forecast_df = event_forecaster.predict()

    diag = event_forecaster.get_diagnostics()
    print(f"\nSource event: {diag['source_window']}")
    print(f"Source mean: {diag['source_mean']:,.0f}")
    print(f"Growth factor: {diag['growth_factor']:.3f}")
    print(f"\nTarget event forecast:")
    print(event_forecast_df[["predicted_impressions", "event_label"]])

    # Example 4: AutoTuner for model selection
    print("\n" + "=" * 60)
    print("Example 4: AutoTuner (Automatic Model Selection)")
    print("=" * 60)

    from ad_inventory_forecast.tuning.auto_tuner import AutoTuner

    # Use 1 year of data for faster demo
    y_auto = y.loc["2023-01-01":"2023-12-31"]

    tuner = AutoTuner(
        candidates=None,  # Use default candidates
        metric="smape",
        n_folds=3,
        horizon=14,
        min_train_size=180,
        verbose=True,
    )

    print("\nRunning model selection...")
    result = tuner.select_best(y_auto)

    print(f"\n{result.summary()}")

    # Example 5: Walk-Forward Validation
    print("\n" + "=" * 60)
    print("Example 5: Walk-Forward Cross-Validation")
    print("=" * 60)

    from ad_inventory_forecast.backtesting.validator import WalkForwardValidator

    validator = WalkForwardValidator(
        n_folds=5,
        horizon=14,
        min_train_size=180,
        step_size=14,
        expanding=True,
    )

    backtest_result = validator.validate(
        InventoryForecaster,
        y_auto,
        model_params={"seasonal_periods": [7]},
        verbose=True,
    )

    print(f"\n{backtest_result.summary()}")

    # Example 6: DemographicReconciler
    print("\n" + "=" * 60)
    print("Example 6: DemographicReconciler (Segment Forecasting)")
    print("=" * 60)

    from ad_inventory_forecast.utils.synthetic import generate_multi_series_data
    from ad_inventory_forecast.reconciliation.demographic import DemographicReconciler

    # Generate multi-series data with segments
    segment_data = generate_multi_series_data(
        n_days=365,
        n_series=4,
        series_names=["age_18_24", "age_25_34", "age_35_44", "age_45_plus"],
        correlation=0.6,
        random_seed=42,
    )

    print(f"Generated segment data with {len(segment_data.columns)} columns")
    print(f"Segments: {[c for c in segment_data.columns if c != 'total']}")

    # Fit reconciler
    reconciler = DemographicReconciler(method="top_down", smoothing_alpha=0.3)
    reconciler.fit(segment_data, total_col="total")

    # Get segment ratios
    ratios = reconciler.get_segment_ratios()
    print(f"\nSegment ratios:")
    for seg, ratio in ratios.items():
        print(f"  {seg}: {ratio:.1%}")

    # Reconcile a total forecast
    total_forecast = pd.Series(
        [50000, 52000, 54000, 51000, 53000],
        index=pd.date_range("2024-01-01", periods=5),
    )

    result = reconciler.reconcile(total_forecast)

    print(f"\nReconciled forecasts for {len(total_forecast)} days:")
    print(result.segment_forecasts.round(0))

    # Example 7: Using metrics directly
    print("\n" + "=" * 60)
    print("Example 7: Forecast Accuracy Metrics")
    print("=" * 60)

    from ad_inventory_forecast.backtesting.metrics import calculate_all_metrics

    # Generate sample actual vs predicted
    np.random.seed(42)
    actual = np.array([100, 110, 105, 115, 108, 112, 120])
    predicted = np.array([102, 108, 107, 112, 110, 115, 118])
    training = y_train.values

    metrics = calculate_all_metrics(actual, predicted, training, period=7)

    print("\nMetrics for sample forecast:")
    for name, value in metrics.items():
        if np.isfinite(value):
            print(f"  {name.upper()}: {value:.4f}")

    print("\n" + "=" * 60)
    print("All examples completed successfully!")
    print("=" * 60)


if __name__ == "__main__":
    main()
