"""
Safety Stock Optimizer
Replaces hardcoded 95% service level with learned optimal safety stock levels
Optimizes based on product characteristics, demand variability, and cost trade-offs
"""

import pandas as pd
import numpy as np
from typing import Dict, List, Any, Optional, Tuple
import structlog
from datetime import datetime, timedelta
from scipy import stats
from scipy.optimize import minimize_scalar
import warnings
warnings.filterwarnings('ignore')

logger = structlog.get_logger()


class SafetyStockOptimizer:
    """
    Optimizes safety stock levels for inventory management.

    Current problem: Hardcoded 95% service level for all products
    Solution: Learn optimal service levels based on:
    - Product characteristics (shelf life, criticality)
    - Demand variability (coefficient of variation)
    - Cost trade-offs (holding cost vs stockout cost)
    - Historical stockout patterns
    - Supplier reliability

    Approaches:
    1. Statistical approach: Based on demand variability and lead time
    2. Cost-based optimization: Minimize total cost (holding + stockout)
    3. Service level optimization: Product-specific target service levels
    4. Dynamic adjustment: Seasonality and trend awareness
    """

    def __init__(self):
        self.optimal_stocks = {}
        self.learned_service_levels = {}

    async def optimize_safety_stock(
        self,
        tenant_id: str,
        inventory_product_id: str,
        demand_history: pd.DataFrame,
        product_characteristics: Dict[str, Any],
        cost_parameters: Optional[Dict[str, float]] = None,
        supplier_reliability: Optional[float] = None,
        min_history_days: int = 90
    ) -> Dict[str, Any]:
        """
        Calculate optimal safety stock for a product.

        Args:
            tenant_id: Tenant identifier
            inventory_product_id: Product identifier
            demand_history: Historical demand data with columns:
                - date
                - demand_quantity
                - stockout (bool, optional)
                - lead_time_days (optional)
            product_characteristics: Product properties:
                - shelf_life_days: int
                - criticality: str (high, medium, low)
                - unit_cost: float
                - avg_daily_demand: float
            cost_parameters: Optional cost params:
                - holding_cost_per_unit_per_day: float
                - stockout_cost_per_unit: float
            supplier_reliability: Supplier on-time rate (0-1)
            min_history_days: Minimum days of history required

        Returns:
            Dictionary with optimal safety stock and insights
        """
        logger.info(
            "Optimizing safety stock",
            tenant_id=tenant_id,
            inventory_product_id=inventory_product_id,
            history_days=len(demand_history)
        )

        # Validate input
        if len(demand_history) < min_history_days:
            logger.warning(
                "Insufficient demand history",
                inventory_product_id=inventory_product_id,
                days=len(demand_history),
                required=min_history_days
            )
            return self._insufficient_data_response(
                tenant_id, inventory_product_id, product_characteristics
            )

        # Calculate demand statistics
        demand_stats = self._calculate_demand_statistics(demand_history)

        # Calculate optimal safety stock using multiple methods
        statistical_result = self._calculate_statistical_safety_stock(
            demand_stats,
            product_characteristics,
            supplier_reliability
        )

        # Cost-based optimization if cost parameters provided
        if cost_parameters:
            cost_based_result = self._calculate_cost_optimal_safety_stock(
                demand_stats,
                product_characteristics,
                cost_parameters,
                demand_history
            )
        else:
            cost_based_result = None

        # Service level optimization
        service_level_result = self._calculate_service_level_optimal_stock(
            demand_stats,
            product_characteristics,
            demand_history
        )

        # Combine methods and select optimal
        optimal_result = self._select_optimal_safety_stock(
            statistical_result,
            cost_based_result,
            service_level_result,
            product_characteristics
        )

        # Compare with current hardcoded approach (95% service level)
        hardcoded_result = self._calculate_hardcoded_safety_stock(
            demand_stats,
            service_level=0.95
        )

        comparison = self._compare_with_hardcoded(
            optimal_result,
            hardcoded_result,
            cost_parameters
        )

        # Generate insights
        insights = self._generate_safety_stock_insights(
            tenant_id,
            inventory_product_id,
            optimal_result,
            hardcoded_result,
            comparison,
            demand_stats,
            product_characteristics
        )

        # Store optimal stock
        self.optimal_stocks[inventory_product_id] = optimal_result['safety_stock']
        self.learned_service_levels[inventory_product_id] = optimal_result['service_level']

        logger.info(
            "Safety stock optimization complete",
            inventory_product_id=inventory_product_id,
            optimal_stock=optimal_result['safety_stock'],
            optimal_service_level=optimal_result['service_level'],
            improvement_vs_hardcoded=comparison.get('cost_savings_pct', 0)
        )

        return {
            'tenant_id': tenant_id,
            'inventory_product_id': inventory_product_id,
            'optimized_at': datetime.utcnow().isoformat(),
            'history_days': len(demand_history),
            'demand_stats': demand_stats,
            'optimal_result': optimal_result,
            'hardcoded_result': hardcoded_result,
            'comparison': comparison,
            'insights': insights
        }

    def _calculate_demand_statistics(
        self,
        demand_history: pd.DataFrame
    ) -> Dict[str, float]:
        """
        Calculate comprehensive demand statistics.

        Args:
            demand_history: Historical demand data

        Returns:
            Dictionary of demand statistics
        """
        # Ensure date column
        if 'date' not in demand_history.columns:
            demand_history = demand_history.copy()
            demand_history['date'] = pd.to_datetime(demand_history.index)

        demand_history['date'] = pd.to_datetime(demand_history['date'])

        # Basic statistics
        mean_demand = demand_history['demand_quantity'].mean()
        std_demand = demand_history['demand_quantity'].std()
        cv_demand = std_demand / mean_demand if mean_demand > 0 else 0

        # Lead time statistics (if available)
        if 'lead_time_days' in demand_history.columns:
            mean_lead_time = demand_history['lead_time_days'].mean()
            std_lead_time = demand_history['lead_time_days'].std()
        else:
            mean_lead_time = 3.0  # Default assumption
            std_lead_time = 0.5

        # Stockout rate (if available)
        if 'stockout' in demand_history.columns:
            stockout_rate = demand_history['stockout'].mean()
            stockout_frequency = demand_history['stockout'].sum()
        else:
            stockout_rate = 0.05  # Assume 5% if not tracked
            stockout_frequency = 0

        # Demand distribution characteristics
        skewness = demand_history['demand_quantity'].skew()
        kurtosis = demand_history['demand_quantity'].kurtosis()

        # Recent trend (last 30 days vs overall)
        if len(demand_history) >= 60:
            recent_mean = demand_history.tail(30)['demand_quantity'].mean()
            trend = (recent_mean - mean_demand) / mean_demand if mean_demand > 0 else 0
        else:
            trend = 0

        return {
            'mean_demand': float(mean_demand),
            'std_demand': float(std_demand),
            'cv_demand': float(cv_demand),
            'min_demand': float(demand_history['demand_quantity'].min()),
            'max_demand': float(demand_history['demand_quantity'].max()),
            'mean_lead_time': float(mean_lead_time),
            'std_lead_time': float(std_lead_time),
            'stockout_rate': float(stockout_rate),
            'stockout_frequency': int(stockout_frequency),
            'skewness': float(skewness),
            'kurtosis': float(kurtosis),
            'trend': float(trend),
            'data_points': int(len(demand_history))
        }

    def _calculate_statistical_safety_stock(
        self,
        demand_stats: Dict[str, float],
        product_characteristics: Dict[str, Any],
        supplier_reliability: Optional[float] = None
    ) -> Dict[str, Any]:
        """
        Calculate safety stock using statistical approach (Classic formula).

        Formula: SS = Z * sqrt(LT * σ_d² + d_avg² * σ_LT²)
        Where:
        - Z: Z-score for desired service level
        - LT: Mean lead time
        - σ_d: Standard deviation of demand
        - d_avg: Average demand
        - σ_LT: Standard deviation of lead time
        """
        # Determine target service level based on product criticality
        criticality = product_characteristics.get('criticality', 'medium').lower()

        if criticality == 'high':
            target_service_level = 0.98  # 98% for critical products
        elif criticality == 'medium':
            target_service_level = 0.95  # 95% for medium
        else:
            target_service_level = 0.90  # 90% for low criticality

        # Adjust for supplier reliability
        if supplier_reliability is not None and supplier_reliability < 0.9:
            # Less reliable suppliers need higher safety stock
            target_service_level = min(0.99, target_service_level + 0.03)

        # Calculate Z-score for target service level
        z_score = stats.norm.ppf(target_service_level)

        # Calculate safety stock
        mean_demand = demand_stats['mean_demand']
        std_demand = demand_stats['std_demand']
        mean_lead_time = demand_stats['mean_lead_time']
        std_lead_time = demand_stats['std_lead_time']

        # Safety stock formula
        variance_component = (
            mean_lead_time * (std_demand ** 2) +
            (mean_demand ** 2) * (std_lead_time ** 2)
        )

        safety_stock = z_score * np.sqrt(variance_component)

        # Ensure non-negative
        safety_stock = max(0, safety_stock)

        return {
            'method': 'statistical',
            'safety_stock': round(safety_stock, 2),
            'service_level': target_service_level,
            'z_score': round(z_score, 2),
            'rationale': f'Based on {target_service_level*100:.0f}% service level for {criticality} criticality product'
        }

    def _calculate_cost_optimal_safety_stock(
        self,
        demand_stats: Dict[str, float],
        product_characteristics: Dict[str, Any],
        cost_parameters: Dict[str, float],
        demand_history: pd.DataFrame
    ) -> Dict[str, Any]:
        """
        Calculate safety stock that minimizes total cost (holding + stockout).

        Total Cost = (Holding Cost × Safety Stock) + (Stockout Cost × Stockout Frequency)
        """
        holding_cost = cost_parameters.get('holding_cost_per_unit_per_day', 0.01)
        stockout_cost = cost_parameters.get('stockout_cost_per_unit', 10.0)

        mean_demand = demand_stats['mean_demand']
        std_demand = demand_stats['std_demand']
        mean_lead_time = demand_stats['mean_lead_time']

        def total_cost(safety_stock):
            """Calculate total cost for given safety stock level."""
            # Holding cost (annual)
            annual_holding_cost = holding_cost * safety_stock * 365

            # Stockout probability and expected stockouts
            # Demand during lead time follows normal distribution
            demand_during_lt_mean = mean_demand * mean_lead_time
            demand_during_lt_std = std_demand * np.sqrt(mean_lead_time)

            # Service level achieved with this safety stock
            if demand_during_lt_std > 0:
                z_score = (safety_stock) / demand_during_lt_std
                service_level = stats.norm.cdf(z_score)
            else:
                service_level = 0.99

            # Stockout probability
            stockout_prob = 1 - service_level

            # Expected annual stockouts (simplified)
            orders_per_year = 365 / mean_lead_time
            expected_stockouts = stockout_prob * orders_per_year * mean_demand

            # Stockout cost (annual)
            annual_stockout_cost = expected_stockouts * stockout_cost

            return annual_holding_cost + annual_stockout_cost

        # Optimize to find minimum total cost
        # Search range: 0 to 5 * mean demand during lead time
        max_search = 5 * mean_demand * mean_lead_time

        result = minimize_scalar(
            total_cost,
            bounds=(0, max_search),
            method='bounded'
        )

        optimal_safety_stock = result.x
        optimal_cost = result.fun

        # Calculate achieved service level
        demand_during_lt_std = std_demand * np.sqrt(mean_lead_time)
        if demand_during_lt_std > 0:
            z_score = optimal_safety_stock / demand_during_lt_std
            achieved_service_level = stats.norm.cdf(z_score)
        else:
            achieved_service_level = 0.99

        return {
            'method': 'cost_optimization',
            'safety_stock': round(optimal_safety_stock, 2),
            'service_level': round(achieved_service_level, 4),
            'annual_total_cost': round(optimal_cost, 2),
            'rationale': f'Minimizes total cost (holding + stockout): €{optimal_cost:.2f}/year'
        }

    def _calculate_service_level_optimal_stock(
        self,
        demand_stats: Dict[str, float],
        product_characteristics: Dict[str, Any],
        demand_history: pd.DataFrame
    ) -> Dict[str, Any]:
        """
        Calculate safety stock based on empirical service level optimization.

        Uses historical stockout data to find optimal service level.
        """
        # If we have stockout history, learn from it
        if 'stockout' in demand_history.columns and demand_history['stockout'].sum() > 0:
            current_stockout_rate = demand_stats['stockout_rate']

            # Target: Reduce stockouts by 50% or achieve 95%, whichever is higher
            target_stockout_rate = min(current_stockout_rate * 0.5, 0.05)
            target_service_level = 1 - target_stockout_rate

        else:
            # No stockout data, use criticality-based default
            criticality = product_characteristics.get('criticality', 'medium').lower()
            target_service_level = {
                'high': 0.98,
                'medium': 0.95,
                'low': 0.90
            }.get(criticality, 0.95)

        # Calculate safety stock for target service level
        z_score = stats.norm.ppf(target_service_level)
        mean_demand = demand_stats['mean_demand']
        std_demand = demand_stats['std_demand']
        mean_lead_time = demand_stats['mean_lead_time']

        safety_stock = z_score * std_demand * np.sqrt(mean_lead_time)
        safety_stock = max(0, safety_stock)

        return {
            'method': 'service_level_optimization',
            'safety_stock': round(safety_stock, 2),
            'service_level': target_service_level,
            'rationale': f'Achieves {target_service_level*100:.0f}% service level based on historical performance'
        }

    def _select_optimal_safety_stock(
        self,
        statistical_result: Dict[str, Any],
        cost_based_result: Optional[Dict[str, Any]],
        service_level_result: Dict[str, Any],
        product_characteristics: Dict[str, Any]
    ) -> Dict[str, Any]:
        """
        Select optimal safety stock from multiple methods.

        Priority:
        1. Cost-based if available and product value is high
        2. Statistical for general case
        3. Service level as validation
        """
        # If cost data available and product is valuable, use cost optimization
        if cost_based_result and product_characteristics.get('unit_cost', 0) > 5:
            selected = cost_based_result
            logger.info("Selected cost-based safety stock (high-value product)")

        # Otherwise use statistical approach
        else:
            selected = statistical_result
            logger.info("Selected statistical safety stock")

        # Add shelf life constraint
        shelf_life = product_characteristics.get('shelf_life_days')
        if shelf_life:
            max_safe_stock = product_characteristics.get('avg_daily_demand', 0) * (shelf_life * 0.5)
            if selected['safety_stock'] > max_safe_stock:
                logger.warning(
                    "Safety stock exceeds shelf life constraint",
                    calculated=selected['safety_stock'],
                    max_allowed=max_safe_stock
                )
                selected['safety_stock'] = round(max_safe_stock, 2)
                selected['constrained_by'] = 'shelf_life'

        return selected

    def _calculate_hardcoded_safety_stock(
        self,
        demand_stats: Dict[str, float],
        service_level: float = 0.95
    ) -> Dict[str, Any]:
        """
        Calculate safety stock using current hardcoded 95% service level.

        Args:
            demand_stats: Demand statistics
            service_level: Hardcoded service level (default 0.95)

        Returns:
            Safety stock result with hardcoded approach
        """
        z_score = stats.norm.ppf(service_level)
        mean_demand = demand_stats['mean_demand']
        std_demand = demand_stats['std_demand']
        mean_lead_time = demand_stats['mean_lead_time']

        safety_stock = z_score * std_demand * np.sqrt(mean_lead_time)
        safety_stock = max(0, safety_stock)

        return {
            'method': 'hardcoded_95_service_level',
            'safety_stock': round(safety_stock, 2),
            'service_level': service_level,
            'rationale': 'Current hardcoded 95% service level for all products'
        }

    def _compare_with_hardcoded(
        self,
        optimal_result: Dict[str, Any],
        hardcoded_result: Dict[str, Any],
        cost_parameters: Optional[Dict[str, float]]
    ) -> Dict[str, Any]:
        """
        Compare optimal safety stock with hardcoded approach.

        Args:
            optimal_result: Optimal safety stock result
            hardcoded_result: Hardcoded approach result
            cost_parameters: Optional cost parameters for savings calculation

        Returns:
            Comparison metrics
        """
        optimal_stock = optimal_result['safety_stock']
        hardcoded_stock = hardcoded_result['safety_stock']

        stock_difference = optimal_stock - hardcoded_stock
        stock_difference_pct = (stock_difference / hardcoded_stock * 100) if hardcoded_stock > 0 else 0

        comparison = {
            'stock_difference': round(stock_difference, 2),
            'stock_difference_pct': round(stock_difference_pct, 2),
            'optimal_service_level': optimal_result['service_level'],
            'hardcoded_service_level': hardcoded_result['service_level'],
            'service_level_difference': round(
                (optimal_result['service_level'] - hardcoded_result['service_level']) * 100, 2
            )
        }

        # Calculate cost savings if cost data available
        if cost_parameters:
            holding_cost = cost_parameters.get('holding_cost_per_unit_per_day', 0.01)
            annual_holding_savings = stock_difference * holding_cost * 365

            comparison['annual_holding_cost_savings'] = round(annual_holding_savings, 2)
            if hardcoded_stock > 0:
                comparison['cost_savings_pct'] = round(
                    (annual_holding_savings / (hardcoded_stock * holding_cost * 365)) * 100, 2
                )

        return comparison

    def _generate_safety_stock_insights(
        self,
        tenant_id: str,
        inventory_product_id: str,
        optimal_result: Dict[str, Any],
        hardcoded_result: Dict[str, Any],
        comparison: Dict[str, Any],
        demand_stats: Dict[str, float],
        product_characteristics: Dict[str, Any]
    ) -> List[Dict[str, Any]]:
        """
        Generate actionable insights from safety stock optimization.

        Args:
            tenant_id: Tenant ID
            inventory_product_id: Product ID
            optimal_result: Optimal safety stock result
            hardcoded_result: Hardcoded result
            comparison: Comparison metrics
            demand_stats: Demand statistics
            product_characteristics: Product characteristics

        Returns:
            List of insights
        """
        insights = []

        stock_diff_pct = comparison['stock_difference_pct']

        # Insight 1: Over-stocking reduction opportunity
        if stock_diff_pct < -10:  # Optimal is >10% lower
            cost_savings = comparison.get('annual_holding_cost_savings', 0)

            insights.append({
                'type': 'optimization',
                'priority': 'high' if abs(stock_diff_pct) > 25 else 'medium',
                'category': 'inventory',
                'title': f'Reduce Safety Stock by {abs(stock_diff_pct):.0f}%',
                'description': f'Product {inventory_product_id} is over-stocked. Optimal safety stock is {optimal_result["safety_stock"]:.1f} units vs current {hardcoded_result["safety_stock"]:.1f}. Reducing to optimal level saves €{abs(cost_savings):.2f}/year in holding costs while maintaining {optimal_result["service_level"]*100:.1f}% service level.',
                'impact_type': 'cost_savings',
                'impact_value': abs(cost_savings),
                'impact_unit': 'euros_per_year',
                'confidence': 85,
                'metrics_json': {
                    'inventory_product_id': inventory_product_id,
                    'current_safety_stock': round(hardcoded_result['safety_stock'], 2),
                    'optimal_safety_stock': round(optimal_result['safety_stock'], 2),
                    'reduction_pct': round(abs(stock_diff_pct), 2),
                    'annual_savings': round(abs(cost_savings), 2),
                    'optimal_service_level': round(optimal_result['service_level'] * 100, 2)
                },
                'actionable': True,
                'recommendation_actions': [
                    {
                        'label': 'Update Safety Stock',
                        'action': 'update_safety_stock',
                        'params': {
                            'inventory_product_id': inventory_product_id,
                            'new_safety_stock': round(optimal_result['safety_stock'], 2)
                        }
                    }
                ],
                'source_service': 'inventory',
                'source_model': 'safety_stock_optimizer'
            })

        # Insight 2: Under-stocking risk
        elif stock_diff_pct > 10:  # Optimal is >10% higher
            insights.append({
                'type': 'alert',
                'priority': 'high' if stock_diff_pct > 25 else 'medium',
                'category': 'inventory',
                'title': f'Increase Safety Stock by {stock_diff_pct:.0f}%',
                'description': f'Product {inventory_product_id} safety stock is too low. Current {hardcoded_result["safety_stock"]:.1f} units provides only {hardcoded_result["service_level"]*100:.0f}% service level. Increase to {optimal_result["safety_stock"]:.1f} for optimal {optimal_result["service_level"]*100:.1f}% service level.',
                'impact_type': 'stockout_risk_reduction',
                'impact_value': stock_diff_pct,
                'impact_unit': 'percentage',
                'confidence': 85,
                'metrics_json': {
                    'inventory_product_id': inventory_product_id,
                    'current_safety_stock': round(hardcoded_result['safety_stock'], 2),
                    'optimal_safety_stock': round(optimal_result['safety_stock'], 2),
                    'increase_pct': round(stock_diff_pct, 2),
                    'current_service_level': round(hardcoded_result['service_level'] * 100, 2),
                    'optimal_service_level': round(optimal_result['service_level'] * 100, 2),
                    'historical_stockout_rate': round(demand_stats['stockout_rate'] * 100, 2)
                },
                'actionable': True,
                'recommendation_actions': [
                    {
                        'label': 'Update Safety Stock',
                        'action': 'update_safety_stock',
                        'params': {
                            'inventory_product_id': inventory_product_id,
                            'new_safety_stock': round(optimal_result['safety_stock'], 2)
                        }
                    }
                ],
                'source_service': 'inventory',
                'source_model': 'safety_stock_optimizer'
            })

        # Insight 3: High demand variability
        if demand_stats['cv_demand'] > 0.5:  # Coefficient of variation > 0.5
            insights.append({
                'type': 'insight',
                'priority': 'medium',
                'category': 'inventory',
                'title': f'High Demand Variability Detected',
                'description': f'Product {inventory_product_id} has high demand variability (CV={demand_stats["cv_demand"]:.2f}). This increases safety stock requirements. Consider demand smoothing strategies or more frequent orders.',
                'impact_type': 'operational_insight',
                'impact_value': demand_stats['cv_demand'],
                'impact_unit': 'coefficient_of_variation',
                'confidence': 90,
                'metrics_json': {
                    'inventory_product_id': inventory_product_id,
                    'cv_demand': round(demand_stats['cv_demand'], 2),
                    'mean_demand': round(demand_stats['mean_demand'], 2),
                    'std_demand': round(demand_stats['std_demand'], 2)
                },
                'actionable': True,
                'recommendation_actions': [
                    {
                        'label': 'Review Demand Patterns',
                        'action': 'analyze_demand_patterns',
                        'params': {'inventory_product_id': inventory_product_id}
                    }
                ],
                'source_service': 'inventory',
                'source_model': 'safety_stock_optimizer'
            })

        # Insight 4: Frequent stockouts
        if demand_stats['stockout_rate'] > 0.1:  # More than 10% stockout rate
            insights.append({
                'type': 'alert',
                'priority': 'critical' if demand_stats['stockout_rate'] > 0.2 else 'high',
                'category': 'inventory',
                'title': f'Frequent Stockouts: {demand_stats["stockout_rate"]*100:.1f}%',
                'description': f'Product {inventory_product_id} experiences frequent stockouts ({demand_stats["stockout_rate"]*100:.1f}% of days). Optimal safety stock of {optimal_result["safety_stock"]:.1f} units should reduce this significantly.',
                'impact_type': 'stockout_frequency',
                'impact_value': demand_stats['stockout_rate'] * 100,
                'impact_unit': 'percentage',
                'confidence': 95,
                'metrics_json': {
                    'inventory_product_id': inventory_product_id,
                    'stockout_rate': round(demand_stats['stockout_rate'] * 100, 2),
                    'stockout_frequency': demand_stats['stockout_frequency'],
                    'optimal_safety_stock': round(optimal_result['safety_stock'], 2)
                },
                'actionable': True,
                'recommendation_actions': [
                    {
                        'label': 'URGENT: Update Safety Stock',
                        'action': 'update_safety_stock',
                        'params': {
                            'inventory_product_id': inventory_product_id,
                            'new_safety_stock': round(optimal_result['safety_stock'], 2)
                        }
                    },
                    {
                        'label': 'Review Supplier Reliability',
                        'action': 'review_supplier',
                        'params': {'inventory_product_id': inventory_product_id}
                    }
                ],
                'source_service': 'inventory',
                'source_model': 'safety_stock_optimizer'
            })

        return insights

    def _insufficient_data_response(
        self,
        tenant_id: str,
        inventory_product_id: str,
        product_characteristics: Dict[str, Any]
    ) -> Dict[str, Any]:
        """Return response when insufficient data available."""
        # Use simple heuristic based on criticality
        criticality = product_characteristics.get('criticality', 'medium').lower()
        avg_daily_demand = product_characteristics.get('avg_daily_demand', 10)

        # Simple rule: 7 days of demand for high, 5 for medium, 3 for low
        safety_stock_days = {'high': 7, 'medium': 5, 'low': 3}.get(criticality, 5)
        fallback_safety_stock = avg_daily_demand * safety_stock_days

        return {
            'tenant_id': tenant_id,
            'inventory_product_id': inventory_product_id,
            'optimized_at': datetime.utcnow().isoformat(),
            'history_days': 0,
            'demand_stats': {},
            'optimal_result': {
                'method': 'fallback_heuristic',
                'safety_stock': round(fallback_safety_stock, 2),
                'service_level': 0.95,
                'rationale': f'Insufficient data. Using {safety_stock_days} days of demand for {criticality} criticality.'
            },
            'hardcoded_result': None,
            'comparison': {},
            'insights': []
        }

    def get_optimal_safety_stock(self, inventory_product_id: str) -> Optional[float]:
        """Get cached optimal safety stock for a product."""
        return self.optimal_stocks.get(inventory_product_id)

    def get_learned_service_level(self, inventory_product_id: str) -> Optional[float]:
        """Get learned optimal service level for a product."""
        return self.learned_service_levels.get(inventory_product_id)
