"""
Scenario Simulation Operations API - PROFESSIONAL/ENTERPRISE ONLY
Business operations for "what-if" scenario testing and strategic planning
"""

import structlog
from fastapi import APIRouter, Depends, HTTPException, status, Path, Request
from typing import List, Dict, Any
from datetime import date, datetime, timedelta, timezone
import uuid

from app.schemas.forecasts import (
    ScenarioSimulationRequest,
    ScenarioSimulationResponse,
    ScenarioComparisonRequest,
    ScenarioComparisonResponse,
    ScenarioType,
    ScenarioImpact,
    ForecastResponse,
    ForecastRequest
)
from app.services.forecasting_service import EnhancedForecastingService
from shared.auth.decorators import get_current_user_dep
from shared.database.base import create_database_manager
from shared.monitoring.decorators import track_execution_time
from shared.monitoring.metrics import get_metrics_collector
from app.core.config import settings
from shared.routing import RouteBuilder
from shared.auth.access_control import require_user_role, analytics_tier_required
from shared.clients.tenant_client import TenantServiceClient

route_builder = RouteBuilder('forecasting')
logger = structlog.get_logger()
router = APIRouter(tags=["scenario-simulation"])


def get_enhanced_forecasting_service():
    """Dependency injection for EnhancedForecastingService"""
    database_manager = create_database_manager(settings.DATABASE_URL, "forecasting-service")
    return EnhancedForecastingService(database_manager)


@router.post(
    route_builder.build_analytics_route("scenario-simulation"),
    response_model=ScenarioSimulationResponse
)
@require_user_role(['admin', 'owner'])
@analytics_tier_required
@track_execution_time("scenario_simulation_duration_seconds", "forecasting-service")
async def simulate_scenario(
    request: ScenarioSimulationRequest,
    tenant_id: str = Path(..., description="Tenant ID"),
    request_obj: Request = None,
    current_user: dict = Depends(get_current_user_dep),
    forecasting_service: EnhancedForecastingService = Depends(get_enhanced_forecasting_service)
):
    """
    Run a "what-if" scenario simulation on forecasts

    This endpoint allows users to test how different scenarios might impact demand:
    - Weather events (heatwaves, cold snaps, rain)
    - Competition (new competitors opening nearby)
    - Events (festivals, concerts, sports events)
    - Pricing changes
    - Promotions
    - Supply disruptions

    **ENTERPRISE TIER ONLY - Admin+ role required**
    """
    metrics = get_metrics_collector(request_obj)
    start_time = datetime.now(timezone.utc)

    try:
        logger.info("Starting scenario simulation",
                   tenant_id=tenant_id,
                   scenario_name=request.scenario_name,
                   scenario_type=request.scenario_type.value,
                   products=len(request.inventory_product_ids))

        if metrics:
            metrics.increment_counter(f"scenario_simulations_total")
            metrics.increment_counter(f"scenario_simulations_{request.scenario_type.value}_total")

        # Generate simulation ID
        simulation_id = str(uuid.uuid4())
        end_date = request.start_date + timedelta(days=request.duration_days - 1)

        # Step 1: Generate baseline forecasts
        baseline_forecasts = []
        if request.include_baseline:
            logger.info("Generating baseline forecasts", tenant_id=tenant_id)

            # Get tenant location (city) from tenant service
            location = "default"
            try:
                tenant_client = TenantServiceClient(settings)
                tenant_info = await tenant_client.get_tenant(tenant_id)
                if tenant_info and tenant_info.get('city'):
                    location = tenant_info['city']
                    logger.info("Using tenant location for forecasts", tenant_id=tenant_id, location=location)
            except Exception as e:
                logger.warning("Failed to get tenant location, using default", error=str(e), tenant_id=tenant_id)

            for product_id in request.inventory_product_ids:
                forecast_request = ForecastRequest(
                    inventory_product_id=product_id,
                    forecast_date=request.start_date,
                    forecast_days=request.duration_days,
                    location=location
                )
                multi_day_result = await forecasting_service.generate_multi_day_forecast(
                    tenant_id=tenant_id,
                    request=forecast_request
                )
                # Convert forecast dictionaries to ForecastResponse objects
                forecast_dicts = multi_day_result.get("forecasts", [])
                for forecast_dict in forecast_dicts:
                    if isinstance(forecast_dict, dict):
                        baseline_forecasts.append(ForecastResponse(**forecast_dict))
                    else:
                        baseline_forecasts.append(forecast_dict)

        # Step 2: Apply scenario adjustments to generate scenario forecasts
        scenario_forecasts = await _apply_scenario_adjustments(
            tenant_id=tenant_id,
            request=request,
            baseline_forecasts=baseline_forecasts if request.include_baseline else [],
            forecasting_service=forecasting_service
        )

        # Step 3: Calculate impacts
        product_impacts = _calculate_product_impacts(
            baseline_forecasts,
            scenario_forecasts,
            request.inventory_product_ids
        )

        # Step 4: Calculate totals
        total_baseline_demand = sum(f.predicted_demand for f in baseline_forecasts) if baseline_forecasts else 0
        total_scenario_demand = sum(f.predicted_demand for f in scenario_forecasts)
        overall_impact_percent = (
            ((total_scenario_demand - total_baseline_demand) / total_baseline_demand * 100)
            if total_baseline_demand > 0 else 0
        )

        # Step 5: Generate insights and recommendations
        insights, recommendations, risk_level = _generate_insights(
            request.scenario_type,
            request,
            product_impacts,
            overall_impact_percent
        )

        # Calculate processing time
        processing_time_ms = int((datetime.now(timezone.utc) - start_time).total_seconds() * 1000)

        if metrics:
            metrics.increment_counter("scenario_simulations_success_total")
            metrics.observe_histogram("scenario_simulation_processing_time_ms", processing_time_ms)

        logger.info("Scenario simulation completed successfully",
                   tenant_id=tenant_id,
                   simulation_id=simulation_id,
                   overall_impact=f"{overall_impact_percent:.2f}%",
                   processing_time_ms=processing_time_ms)

        return ScenarioSimulationResponse(
            id=simulation_id,
            tenant_id=tenant_id,
            scenario_name=request.scenario_name,
            scenario_type=request.scenario_type,
            start_date=request.start_date,
            end_date=end_date,
            duration_days=request.duration_days,
            baseline_forecasts=baseline_forecasts if request.include_baseline else None,
            scenario_forecasts=scenario_forecasts,
            total_baseline_demand=total_baseline_demand,
            total_scenario_demand=total_scenario_demand,
            overall_impact_percent=overall_impact_percent,
            product_impacts=product_impacts,
            insights=insights,
            recommendations=recommendations,
            risk_level=risk_level,
            created_at=datetime.now(timezone.utc),
            processing_time_ms=processing_time_ms
        )

    except ValueError as e:
        if metrics:
            metrics.increment_counter("scenario_simulation_validation_errors_total")
        logger.error("Scenario simulation validation error", error=str(e), tenant_id=tenant_id)
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=str(e)
        )
    except Exception as e:
        if metrics:
            metrics.increment_counter("scenario_simulations_errors_total")
        logger.error("Scenario simulation failed", error=str(e), tenant_id=tenant_id)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Scenario simulation failed"
        )


async def _apply_scenario_adjustments(
    tenant_id: str,
    request: ScenarioSimulationRequest,
    baseline_forecasts: List[ForecastResponse],
    forecasting_service: EnhancedForecastingService
) -> List[ForecastResponse]:
    """
    Apply scenario-specific adjustments to forecasts
    """
    scenario_forecasts = []

    # If no baseline, generate fresh forecasts
    if not baseline_forecasts:
        for product_id in request.inventory_product_ids:
            forecast_request = ForecastRequest(
                inventory_product_id=product_id,
                forecast_date=request.start_date,
                forecast_days=request.duration_days,
                location="default"
            )
            multi_day_result = await forecasting_service.generate_multi_day_forecast(
                tenant_id=tenant_id,
                request=forecast_request
            )
            baseline_forecasts = multi_day_result.get("forecasts", [])

    # Apply multipliers based on scenario type
    for forecast in baseline_forecasts:
        adjusted_forecast = forecast.copy()
        multiplier = _get_scenario_multiplier(request)

        # Adjust predicted demand
        adjusted_forecast.predicted_demand *= multiplier
        adjusted_forecast.confidence_lower *= multiplier
        adjusted_forecast.confidence_upper *= multiplier

        scenario_forecasts.append(adjusted_forecast)

    return scenario_forecasts


def _get_scenario_multiplier(request: ScenarioSimulationRequest) -> float:
    """
    Calculate demand multiplier based on scenario type and parameters
    """
    if request.scenario_type == ScenarioType.WEATHER:
        if request.weather_params:
            # Heatwave increases demand for cold items, decreases for hot items
            if request.weather_params.temperature_change and request.weather_params.temperature_change > 10:
                return 1.25  # 25% increase during heatwave
            elif request.weather_params.temperature_change and request.weather_params.temperature_change < -10:
                return 0.85  # 15% decrease during cold snap
            elif request.weather_params.precipitation_change and request.weather_params.precipitation_change > 10:
                return 0.90  # 10% decrease during heavy rain
        return 1.0

    elif request.scenario_type == ScenarioType.COMPETITION:
        if request.competition_params:
            # New competition reduces demand based on market share loss
            return 1.0 - request.competition_params.estimated_market_share_loss
        return 0.85  # Default 15% reduction

    elif request.scenario_type == ScenarioType.EVENT:
        if request.event_params:
            # Events increase demand based on attendance and proximity
            if request.event_params.distance_km < 1.0:
                return 1.5  # 50% increase for very close events
            elif request.event_params.distance_km < 5.0:
                return 1.2  # 20% increase for nearby events
        return 1.15  # Default 15% increase

    elif request.scenario_type == ScenarioType.PRICING:
        if request.pricing_params:
            # Price elasticity: typically -0.5 to -2.0
            # 10% price increase = 5-20% demand decrease
            elasticity = -1.0  # Average elasticity
            return 1.0 + (request.pricing_params.price_change_percent / 100) * elasticity
        return 1.0

    elif request.scenario_type == ScenarioType.PROMOTION:
        if request.promotion_params:
            # Promotions increase traffic and conversion
            traffic_boost = 1.0 + request.promotion_params.expected_traffic_increase
            discount_boost = 1.0 + (request.promotion_params.discount_percent / 100) * 0.5
            return traffic_boost * discount_boost
        return 1.3  # Default 30% increase

    elif request.scenario_type == ScenarioType.SUPPLY_DISRUPTION:
        return 0.6  # 40% reduction due to limited supply

    elif request.scenario_type == ScenarioType.CUSTOM:
        if request.custom_multipliers and 'demand' in request.custom_multipliers:
            return request.custom_multipliers['demand']
        return 1.0

    return 1.0


def _calculate_product_impacts(
    baseline_forecasts: List[ForecastResponse],
    scenario_forecasts: List[ForecastResponse],
    product_ids: List[str]
) -> List[ScenarioImpact]:
    """
    Calculate per-product impact of the scenario
    """
    impacts = []

    for product_id in product_ids:
        baseline_total = sum(
            f.predicted_demand for f in baseline_forecasts
            if f.inventory_product_id == product_id
        )
        scenario_total = sum(
            f.predicted_demand for f in scenario_forecasts
            if f.inventory_product_id == product_id
        )

        if baseline_total > 0:
            change_percent = ((scenario_total - baseline_total) / baseline_total) * 100
        else:
            change_percent = 0

        # Get confidence ranges
        scenario_product_forecasts = [
            f for f in scenario_forecasts if f.inventory_product_id == product_id
        ]
        avg_lower = sum(f.confidence_lower for f in scenario_product_forecasts) / len(scenario_product_forecasts) if scenario_product_forecasts else 0
        avg_upper = sum(f.confidence_upper for f in scenario_product_forecasts) / len(scenario_product_forecasts) if scenario_product_forecasts else 0

        impacts.append(ScenarioImpact(
            inventory_product_id=product_id,
            baseline_demand=baseline_total,
            simulated_demand=scenario_total,
            demand_change_percent=change_percent,
            confidence_range=(avg_lower, avg_upper),
            impact_factors={"primary_driver": "scenario_adjustment"}
        ))

    return impacts


def _generate_insights(
    scenario_type: ScenarioType,
    request: ScenarioSimulationRequest,
    impacts: List[ScenarioImpact],
    overall_impact: float
) -> tuple[List[str], List[str], str]:
    """
    Generate AI-powered insights and recommendations
    """
    insights = []
    recommendations = []
    risk_level = "low"

    # Determine risk level
    if abs(overall_impact) > 30:
        risk_level = "high"
    elif abs(overall_impact) > 15:
        risk_level = "medium"

    # Generate scenario-specific insights
    if scenario_type == ScenarioType.WEATHER:
        if request.weather_params:
            if request.weather_params.temperature_change and request.weather_params.temperature_change > 10:
                insights.append(f"Heatwave of +{request.weather_params.temperature_change}°C expected to increase demand by {overall_impact:.1f}%")
                recommendations.append("Increase inventory of cold beverages and refrigerated items")
                recommendations.append("Extend operating hours to capture increased evening traffic")
            elif request.weather_params.temperature_change and request.weather_params.temperature_change < -10:
                insights.append(f"Cold snap of {request.weather_params.temperature_change}°C expected to decrease demand by {abs(overall_impact):.1f}%")
                recommendations.append("Increase production of warm comfort foods")
                recommendations.append("Reduce inventory of cold items")

    elif scenario_type == ScenarioType.COMPETITION:
        insights.append(f"New competitor expected to reduce demand by {abs(overall_impact):.1f}%")
        recommendations.append("Consider launching loyalty program to retain customers")
        recommendations.append("Differentiate with unique product offerings")
        recommendations.append("Focus on customer service excellence")

    elif scenario_type == ScenarioType.EVENT:
        insights.append(f"Local event expected to increase demand by {overall_impact:.1f}%")
        recommendations.append("Increase staffing for the event period")
        recommendations.append("Stock additional inventory of popular items")
        recommendations.append("Consider event-specific promotions")

    elif scenario_type == ScenarioType.PRICING:
        if overall_impact < 0:
            insights.append(f"Price increase expected to reduce demand by {abs(overall_impact):.1f}%")
            recommendations.append("Consider smaller price increases")
            recommendations.append("Communicate value proposition to customers")
        else:
            insights.append(f"Price decrease expected to increase demand by {overall_impact:.1f}%")
            recommendations.append("Ensure adequate inventory to meet increased demand")

    elif scenario_type == ScenarioType.PROMOTION:
        insights.append(f"Promotion expected to increase demand by {overall_impact:.1f}%")
        recommendations.append("Stock additional inventory before promotion starts")
        recommendations.append("Increase staffing during promotion period")
        recommendations.append("Prepare marketing materials and signage")

    # Add product-specific insights
    high_impact_products = [
        impact for impact in impacts
        if abs(impact.demand_change_percent) > 20
    ]
    if high_impact_products:
        insights.append(f"{len(high_impact_products)} products show significant impact (>20% change)")

    # Add general recommendation
    if risk_level == "high":
        recommendations.append("⚠️ High-impact scenario - review and adjust operational plans immediately")
    elif risk_level == "medium":
        recommendations.append("Monitor situation closely and prepare contingency plans")

    return insights, recommendations, risk_level


@router.post(
    route_builder.build_analytics_route("scenario-comparison"),
    response_model=ScenarioComparisonResponse
)
@require_user_role(['viewer', 'member', 'admin', 'owner'])
@analytics_tier_required
async def compare_scenarios(
    request: ScenarioComparisonRequest,
    tenant_id: str = Path(..., description="Tenant ID")
):
    """
    Compare multiple scenario simulations

    **PROFESSIONAL/ENTERPRISE ONLY**

    **STATUS**: Not yet implemented - requires scenario persistence layer

    **Future implementation would**:
    1. Retrieve saved scenarios by ID from database
    2. Use ScenarioPlanner.compare_scenarios() to analyze them
    3. Return comparison matrix with best/worst case analysis

    **Prerequisites**:
    - Scenario storage/retrieval database layer
    - Scenario CRUD endpoints
    - UI for scenario management
    """
    # NOTE: HTTP 501 Not Implemented is the correct response for unimplemented optional features
    # The ML logic exists in scenario_planner.py but requires a persistence layer
    raise HTTPException(
        status_code=status.HTTP_501_NOT_IMPLEMENTED,
        detail="Scenario comparison requires scenario persistence layer (future feature)"
    )
