"""
Multi-Horizon Forecasting System
Generates forecasts for multiple time horizons (7, 14, 30, 90 days)
"""

import pandas as pd
import numpy as np
from typing import Dict, List, Any, Optional, Tuple
from datetime import datetime, timedelta, date
import structlog

logger = structlog.get_logger()


class MultiHorizonForecaster:
    """
    Multi-horizon forecasting with horizon-specific models.

    Horizons:
    - Short-term (1-7 days): High precision, detailed features
    - Medium-term (8-14 days): Balanced approach
    - Long-term (15-30 days): Focus on trends, seasonal patterns
    - Very long-term (31-90 days): Strategic planning, major trends only
    """

    HORIZONS = {
        'short': (1, 7),
        'medium': (8, 14),
        'long': (15, 30),
        'very_long': (31, 90)
    }

    def __init__(self, base_forecaster=None):
        """
        Initialize multi-horizon forecaster.

        Args:
            base_forecaster: Base forecaster (e.g., BakeryForecaster) to use
        """
        self.base_forecaster = base_forecaster

    async def generate_multi_horizon_forecast(
        self,
        tenant_id: str,
        inventory_product_id: str,
        start_date: date,
        horizons: List[str] = None,
        include_confidence_intervals: bool = True
    ) -> Dict[str, Any]:
        """
        Generate forecasts for multiple horizons.

        Args:
            tenant_id: Tenant identifier
            inventory_product_id: Product identifier
            start_date: Start date for forecasts
            horizons: List of horizons to forecast ('short', 'medium', 'long', 'very_long')
            include_confidence_intervals: Include confidence intervals

        Returns:
            Dictionary with forecasts by horizon
        """
        if horizons is None:
            horizons = ['short', 'medium', 'long']

        logger.info(
            "Generating multi-horizon forecast",
            tenant_id=tenant_id,
            inventory_product_id=inventory_product_id,
            horizons=horizons
        )

        results = {
            'tenant_id': tenant_id,
            'inventory_product_id': inventory_product_id,
            'start_date': start_date.isoformat(),
            'generated_at': datetime.now().isoformat(),
            'horizons': {}
        }

        for horizon_name in horizons:
            if horizon_name not in self.HORIZONS:
                logger.warning(f"Unknown horizon: {horizon_name}, skipping")
                continue

            start_day, end_day = self.HORIZONS[horizon_name]

            # Generate forecast for this horizon
            horizon_forecast = await self._generate_horizon_forecast(
                tenant_id=tenant_id,
                inventory_product_id=inventory_product_id,
                start_date=start_date,
                days_ahead=end_day,
                horizon_name=horizon_name,
                include_confidence=include_confidence_intervals
            )

            results['horizons'][horizon_name] = horizon_forecast

        logger.info("Multi-horizon forecast complete",
                   horizons_generated=len(results['horizons']))

        return results

    async def _generate_horizon_forecast(
        self,
        tenant_id: str,
        inventory_product_id: str,
        start_date: date,
        days_ahead: int,
        horizon_name: str,
        include_confidence: bool
    ) -> Dict[str, Any]:
        """
        Generate forecast for a specific horizon.

        Args:
            tenant_id: Tenant identifier
            inventory_product_id: Product identifier
            start_date: Start date
            days_ahead: Number of days ahead
            horizon_name: Horizon name ('short', 'medium', etc.)
            include_confidence: Include confidence intervals

        Returns:
            Forecast data for the horizon
        """
        # Generate date range
        dates = [start_date + timedelta(days=i) for i in range(days_ahead)]

        # Use base forecaster if available
        if self.base_forecaster:
            # Call base forecaster for predictions
            forecasts = []

            for forecast_date in dates:
                try:
                    # This would call the actual forecasting service
                    # For now, we'll return a structured response
                    forecasts.append({
                        'date': forecast_date.isoformat(),
                        'predicted_demand': 0,  # Placeholder
                        'confidence_lower': 0 if include_confidence else None,
                        'confidence_upper': 0 if include_confidence else None
                    })
                except Exception as e:
                    logger.error(f"Failed to generate forecast for {forecast_date}: {e}")

            return {
                'horizon_name': horizon_name,
                'days_ahead': days_ahead,
                'start_date': start_date.isoformat(),
                'end_date': dates[-1].isoformat(),
                'forecasts': forecasts,
                'aggregates': self._calculate_horizon_aggregates(forecasts)
            }
        else:
            logger.warning("No base forecaster available, returning placeholder")
            return {
                'horizon_name': horizon_name,
                'days_ahead': days_ahead,
                'forecasts': [],
                'aggregates': {}
            }

    def _calculate_horizon_aggregates(self, forecasts: List[Dict]) -> Dict[str, float]:
        """
        Calculate aggregate statistics for a horizon.

        Args:
            forecasts: List of daily forecasts

        Returns:
            Aggregate statistics
        """
        if not forecasts:
            return {}

        demands = [f['predicted_demand'] for f in forecasts if f.get('predicted_demand')]

        if not demands:
            return {}

        return {
            'total_demand': sum(demands),
            'avg_daily_demand': np.mean(demands),
            'max_daily_demand': max(demands),
            'min_daily_demand': min(demands),
            'demand_volatility': np.std(demands) if len(demands) > 1 else 0
        }

    def get_horizon_recommendation(
        self,
        horizon_name: str,
        forecast_data: Dict[str, Any]
    ) -> Dict[str, Any]:
        """
        Generate recommendations based on horizon forecast.

        Args:
            horizon_name: Horizon name
            forecast_data: Forecast data for the horizon

        Returns:
            Recommendations dictionary
        """
        aggregates = forecast_data.get('aggregates', {})
        total_demand = aggregates.get('total_demand', 0)
        volatility = aggregates.get('demand_volatility', 0)

        recommendations = {
            'horizon': horizon_name,
            'actions': []
        }

        if horizon_name == 'short':
            # Short-term: Operational recommendations
            if total_demand > 0:
                recommendations['actions'].append(f"Prepare {total_demand:.0f} units for next 7 days")
            if volatility > 10:
                recommendations['actions'].append("High volatility expected - increase safety stock")

        elif horizon_name == 'medium':
            # Medium-term: Procurement planning
            recommendations['actions'].append(f"Order supplies for {total_demand:.0f} units (2-week demand)")
            if aggregates.get('max_daily_demand', 0) > aggregates.get('avg_daily_demand', 0) * 1.5:
                recommendations['actions'].append("Peak demand day detected - plan extra capacity")

        elif horizon_name == 'long':
            # Long-term: Strategic planning
            avg_weekly_demand = total_demand / 4 if total_demand > 0 else 0
            recommendations['actions'].append(f"Monthly demand projection: {total_demand:.0f} units")
            recommendations['actions'].append(f"Average weekly demand: {avg_weekly_demand:.0f} units")

        elif horizon_name == 'very_long':
            # Very long-term: Capacity planning
            recommendations['actions'].append(f"Quarterly demand projection: {total_demand:.0f} units")
            recommendations['actions'].append("Review capacity and staffing needs")

        return recommendations


def get_appropriate_horizons_for_use_case(use_case: str) -> List[str]:
    """
    Get appropriate forecast horizons for a use case.

    Args:
        use_case: Use case name (e.g., 'production_planning', 'procurement', 'strategic')

    Returns:
        List of horizon names
    """
    use_case_horizons = {
        'production_planning': ['short'],
        'procurement': ['short', 'medium'],
        'inventory_optimization': ['short', 'medium'],
        'capacity_planning': ['medium', 'long'],
        'strategic_planning': ['long', 'very_long'],
        'financial_planning': ['long', 'very_long'],
        'all': ['short', 'medium', 'long', 'very_long']
    }

    return use_case_horizons.get(use_case, ['short', 'medium'])
