"""
Scenario Planning System
What-if analysis for demand forecasting
"""

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

logger = structlog.get_logger()


class ScenarioType(str, Enum):
    """Types of scenarios"""
    BASELINE = "baseline"
    OPTIMISTIC = "optimistic"
    PESSIMISTIC = "pessimistic"
    CUSTOM = "custom"
    PROMOTION = "promotion"
    EVENT = "event"
    WEATHER = "weather"
    PRICE_CHANGE = "price_change"


class ScenarioPlanner:
    """
    Scenario planning for demand forecasting.

    Allows testing "what-if" scenarios:
    - What if we run a promotion?
    - What if there's a local festival?
    - What if weather is unusually bad?
    - What if we change prices?
    """

    def __init__(self, base_forecaster=None):
        """
        Initialize scenario planner.

        Args:
            base_forecaster: Base forecaster to use for baseline predictions
        """
        self.base_forecaster = base_forecaster

    async def create_scenario(
        self,
        tenant_id: str,
        inventory_product_id: str,
        scenario_name: str,
        scenario_type: ScenarioType,
        start_date: date,
        end_date: date,
        adjustments: Dict[str, Any]
    ) -> Dict[str, Any]:
        """
        Create a forecast scenario with adjustments.

        Args:
            tenant_id: Tenant identifier
            inventory_product_id: Product identifier
            scenario_name: Name for the scenario
            scenario_type: Type of scenario
            start_date: Scenario start date
            end_date: Scenario end date
            adjustments: Dictionary of adjustments to apply

        Returns:
            Scenario forecast results
        """
        logger.info(
            "Creating forecast scenario",
            tenant_id=tenant_id,
            inventory_product_id=inventory_product_id,
            scenario_name=scenario_name,
            scenario_type=scenario_type
        )

        # Generate baseline forecast first
        baseline_forecast = await self._generate_baseline_forecast(
            tenant_id=tenant_id,
            inventory_product_id=inventory_product_id,
            start_date=start_date,
            end_date=end_date
        )

        # Apply scenario adjustments
        scenario_forecast = self._apply_scenario_adjustments(
            baseline_forecast=baseline_forecast,
            adjustments=adjustments,
            scenario_type=scenario_type
        )

        # Calculate impact
        impact_analysis = self._calculate_scenario_impact(
            baseline_forecast=baseline_forecast,
            scenario_forecast=scenario_forecast
        )

        return {
            'scenario_id': f"scenario_{tenant_id}_{inventory_product_id}_{datetime.now().strftime('%Y%m%d%H%M%S')}",
            'scenario_name': scenario_name,
            'scenario_type': scenario_type,
            'tenant_id': tenant_id,
            'inventory_product_id': inventory_product_id,
            'date_range': {
                'start': start_date.isoformat(),
                'end': end_date.isoformat()
            },
            'baseline_forecast': baseline_forecast,
            'scenario_forecast': scenario_forecast,
            'impact_analysis': impact_analysis,
            'adjustments_applied': adjustments,
            'created_at': datetime.now().isoformat()
        }

    async def compare_scenarios(
        self,
        scenarios: List[Dict[str, Any]]
    ) -> Dict[str, Any]:
        """
        Compare multiple scenarios side-by-side.

        Args:
            scenarios: List of scenario results from create_scenario()

        Returns:
            Comparison analysis
        """
        if len(scenarios) < 2:
            return {'error': 'Need at least 2 scenarios to compare'}

        comparison = {
            'scenarios_compared': len(scenarios),
            'scenario_names': [s['scenario_name'] for s in scenarios],
            'comparison_metrics': {}
        }

        # Extract total demand for each scenario
        for scenario in scenarios:
            scenario_name = scenario['scenario_name']
            scenario_forecast = scenario['scenario_forecast']

            total_demand = sum(f['predicted_demand'] for f in scenario_forecast)

            comparison['comparison_metrics'][scenario_name] = {
                'total_demand': total_demand,
                'avg_daily_demand': total_demand / len(scenario_forecast) if scenario_forecast else 0,
                'peak_demand': max(f['predicted_demand'] for f in scenario_forecast) if scenario_forecast else 0
            }

        # Determine best and worst scenarios
        total_demands = {
            name: metrics['total_demand']
            for name, metrics in comparison['comparison_metrics'].items()
        }

        comparison['best_scenario'] = max(total_demands, key=total_demands.get)
        comparison['worst_scenario'] = min(total_demands, key=total_demands.get)

        comparison['demand_range'] = {
            'min': min(total_demands.values()),
            'max': max(total_demands.values()),
            'spread': max(total_demands.values()) - min(total_demands.values())
        }

        return comparison

    async def _generate_baseline_forecast(
        self,
        tenant_id: str,
        inventory_product_id: str,
        start_date: date,
        end_date: date
    ) -> List[Dict[str, Any]]:
        """
        Generate baseline forecast without adjustments.

        Args:
            tenant_id: Tenant identifier
            inventory_product_id: Product identifier
            start_date: Start date
            end_date: End date

        Returns:
            List of daily forecasts
        """
        # Generate date range
        dates = []
        current_date = start_date
        while current_date <= end_date:
            dates.append(current_date)
            current_date += timedelta(days=1)

        # Placeholder forecast (in real implementation, call forecasting service)
        baseline = []
        for forecast_date in dates:
            baseline.append({
                'date': forecast_date.isoformat(),
                'predicted_demand': 100,  # Placeholder
                'confidence_lower': 80,
                'confidence_upper': 120
            })

        return baseline

    def _apply_scenario_adjustments(
        self,
        baseline_forecast: List[Dict[str, Any]],
        adjustments: Dict[str, Any],
        scenario_type: ScenarioType
    ) -> List[Dict[str, Any]]:
        """
        Apply adjustments to baseline forecast.

        Args:
            baseline_forecast: Baseline forecast data
            adjustments: Adjustments to apply
            scenario_type: Type of scenario

        Returns:
            Adjusted forecast
        """
        scenario_forecast = []

        for day_forecast in baseline_forecast:
            adjusted_forecast = day_forecast.copy()

            # Apply different adjustment types
            if 'demand_multiplier' in adjustments:
                # Multiply demand by factor
                multiplier = adjustments['demand_multiplier']
                adjusted_forecast['predicted_demand'] *= multiplier
                adjusted_forecast['confidence_lower'] *= multiplier
                adjusted_forecast['confidence_upper'] *= multiplier

            if 'demand_offset' in adjustments:
                # Add/subtract fixed amount
                offset = adjustments['demand_offset']
                adjusted_forecast['predicted_demand'] += offset
                adjusted_forecast['confidence_lower'] += offset
                adjusted_forecast['confidence_upper'] += offset

            if 'event_impact' in adjustments:
                # Apply event-specific impact
                event_multiplier = adjustments['event_impact']
                adjusted_forecast['predicted_demand'] *= event_multiplier

            if 'weather_impact' in adjustments:
                # Apply weather adjustments
                weather_factor = adjustments['weather_impact']
                adjusted_forecast['predicted_demand'] *= weather_factor

            if 'price_elasticity' in adjustments and 'price_change_percent' in adjustments:
                # Apply price elasticity
                elasticity = adjustments['price_elasticity']
                price_change = adjustments['price_change_percent']
                demand_change = -elasticity * price_change  # Negative correlation
                adjusted_forecast['predicted_demand'] *= (1 + demand_change)

            # Ensure non-negative demand
            adjusted_forecast['predicted_demand'] = max(0, adjusted_forecast['predicted_demand'])
            adjusted_forecast['confidence_lower'] = max(0, adjusted_forecast['confidence_lower'])

            scenario_forecast.append(adjusted_forecast)

        return scenario_forecast

    def _calculate_scenario_impact(
        self,
        baseline_forecast: List[Dict[str, Any]],
        scenario_forecast: List[Dict[str, Any]]
    ) -> Dict[str, Any]:
        """
        Calculate impact of scenario vs baseline.

        Args:
            baseline_forecast: Baseline forecast
            scenario_forecast: Scenario forecast

        Returns:
            Impact analysis
        """
        baseline_total = sum(f['predicted_demand'] for f in baseline_forecast)
        scenario_total = sum(f['predicted_demand'] for f in scenario_forecast)

        difference = scenario_total - baseline_total
        percent_change = (difference / baseline_total * 100) if baseline_total > 0 else 0

        return {
            'baseline_total_demand': baseline_total,
            'scenario_total_demand': scenario_total,
            'absolute_difference': difference,
            'percent_change': percent_change,
            'impact_category': self._categorize_impact(percent_change),
            'days_analyzed': len(baseline_forecast)
        }

    def _categorize_impact(self, percent_change: float) -> str:
        """Categorize impact magnitude"""
        if abs(percent_change) < 5:
            return "minimal"
        elif abs(percent_change) < 15:
            return "moderate"
        elif abs(percent_change) < 30:
            return "significant"
        else:
            return "major"

    def generate_predefined_scenarios(
        self,
        base_scenario: Dict[str, Any]
    ) -> List[Dict[str, Any]]:
        """
        Generate common predefined scenarios for comparison.

        Args:
            base_scenario: Base scenario parameters

        Returns:
            List of scenario configurations
        """
        scenarios = []

        # Baseline scenario
        scenarios.append({
            'scenario_name': 'Baseline',
            'scenario_type': ScenarioType.BASELINE,
            'adjustments': {}
        })

        # Optimistic scenario
        scenarios.append({
            'scenario_name': 'Optimistic',
            'scenario_type': ScenarioType.OPTIMISTIC,
            'adjustments': {
                'demand_multiplier': 1.2,  # 20% increase
                'description': '+20% demand increase'
            }
        })

        # Pessimistic scenario
        scenarios.append({
            'scenario_name': 'Pessimistic',
            'scenario_type': ScenarioType.PESSIMISTIC,
            'adjustments': {
                'demand_multiplier': 0.8,  # 20% decrease
                'description': '-20% demand decrease'
            }
        })

        # Promotion scenario
        scenarios.append({
            'scenario_name': 'Promotion Campaign',
            'scenario_type': ScenarioType.PROMOTION,
            'adjustments': {
                'demand_multiplier': 1.5,  # 50% increase
                'description': '50% promotion boost'
            }
        })

        # Bad weather scenario
        scenarios.append({
            'scenario_name': 'Bad Weather',
            'scenario_type': ScenarioType.WEATHER,
            'adjustments': {
                'weather_impact': 0.7,  # 30% decrease
                'description': 'Bad weather reduces foot traffic'
            }
        })

        # Price increase scenario
        scenarios.append({
            'scenario_name': 'Price Increase 10%',
            'scenario_type': ScenarioType.PRICE_CHANGE,
            'adjustments': {
                'price_elasticity': 1.2,  # Elastic demand
                'price_change_percent': 0.10,  # 10% price increase
                'description': '10% price increase with elastic demand'
            }
        })

        return scenarios
