"""
Safety Stock Insights Orchestrator
Coordinates safety stock optimization and insight posting
"""

import pandas as pd
from typing import Dict, List, Any, Optional
import structlog
from datetime import datetime
from uuid import UUID
import sys
import os

# Add shared clients to path
sys.path.append(os.path.join(os.path.dirname(__file__), '../../../..'))
from shared.clients.ai_insights_client import AIInsightsClient
from shared.messaging import UnifiedEventPublisher

from app.ml.safety_stock_optimizer import SafetyStockOptimizer

logger = structlog.get_logger()


class SafetyStockInsightsOrchestrator:
    """
    Orchestrates safety stock optimization and insight generation workflow.

    Workflow:
    1. Optimize safety stock from demand history and cost parameters
    2. Generate insights comparing optimal vs hardcoded approach
    3. Post insights to AI Insights Service
    4. Publish recommendation events to RabbitMQ
    5. Provide optimized safety stock levels for inventory management
    """

    def __init__(
        self,
        ai_insights_base_url: str = "http://ai-insights-service:8000",
        event_publisher: Optional[UnifiedEventPublisher] = None
    ):
        self.optimizer = SafetyStockOptimizer()
        self.ai_insights_client = AIInsightsClient(ai_insights_base_url)
        self.event_publisher = event_publisher

    async def optimize_and_post_insights(
        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]:
        """
        Complete workflow: Optimize safety stock and post insights.

        Args:
            tenant_id: Tenant identifier
            inventory_product_id: Product identifier
            demand_history: Historical demand data
            product_characteristics: Product properties
            cost_parameters: Optional cost parameters
            supplier_reliability: Optional supplier on-time rate
            min_history_days: Minimum days of history required

        Returns:
            Workflow results with optimization and posted insights
        """
        logger.info(
            "Starting safety stock optimization workflow",
            tenant_id=tenant_id,
            inventory_product_id=inventory_product_id,
            history_days=len(demand_history)
        )

        # Step 1: Optimize safety stock
        optimization_results = await self.optimizer.optimize_safety_stock(
            tenant_id=tenant_id,
            inventory_product_id=inventory_product_id,
            demand_history=demand_history,
            product_characteristics=product_characteristics,
            cost_parameters=cost_parameters,
            supplier_reliability=supplier_reliability,
            min_history_days=min_history_days
        )

        logger.info(
            "Safety stock optimization complete",
            inventory_product_id=inventory_product_id,
            optimal_stock=optimization_results.get('optimal_result', {}).get('safety_stock'),
            insights_generated=len(optimization_results.get('insights', []))
        )

        # Step 2: Enrich insights with tenant_id and product context
        enriched_insights = self._enrich_insights(
            optimization_results.get('insights', []),
            tenant_id,
            inventory_product_id
        )

        # Step 3: Post insights to AI Insights Service
        if enriched_insights:
            post_results = await self.ai_insights_client.create_insights_bulk(
                tenant_id=UUID(tenant_id),
                insights=enriched_insights
            )

            logger.info(
                "Safety stock insights posted to AI Insights Service",
                inventory_product_id=inventory_product_id,
                total=post_results['total'],
                successful=post_results['successful'],
                failed=post_results['failed']
            )

            # Step 4: Publish recommendation events to RabbitMQ
            created_insights = post_results.get('created_insights', [])
            if created_insights:
                product_context = product_characteristics.copy() if product_characteristics else {}
                product_context['inventory_product_id'] = inventory_product_id
                await self._publish_insight_events(
                    tenant_id=tenant_id,
                    insights=created_insights,
                    product_context=product_context
                )
        else:
            post_results = {'total': 0, 'successful': 0, 'failed': 0}
            logger.info("No insights to post for product", inventory_product_id=inventory_product_id)

        # Step 4: Return comprehensive results
        return {
            'tenant_id': tenant_id,
            'inventory_product_id': inventory_product_id,
            'optimized_at': optimization_results['optimized_at'],
            'history_days': optimization_results['history_days'],
            'optimal_safety_stock': optimization_results.get('optimal_result', {}).get('safety_stock'),
            'optimal_service_level': optimization_results.get('optimal_result', {}).get('service_level'),
            'cost_savings': optimization_results.get('comparison', {}).get('annual_holding_cost_savings'),
            'insights_generated': len(enriched_insights),
            'insights_posted': post_results['successful'],
            'insights_failed': post_results['failed'],
            'created_insights': post_results.get('created_insights', [])
        }

    def _enrich_insights(
        self,
        insights: List[Dict[str, Any]],
        tenant_id: str,
        inventory_product_id: str
    ) -> List[Dict[str, Any]]:
        """
        Enrich insights with required fields for AI Insights Service.

        Args:
            insights: Raw insights from optimizer
            tenant_id: Tenant identifier
            inventory_product_id: Product identifier

        Returns:
            Enriched insights ready for posting
        """
        enriched = []

        for insight in insights:
            # Add required tenant_id
            enriched_insight = insight.copy()
            enriched_insight['tenant_id'] = tenant_id

            # Add product context to metrics
            if 'metrics_json' not in enriched_insight:
                enriched_insight['metrics_json'] = {}

            enriched_insight['metrics_json']['inventory_product_id'] = inventory_product_id

            # Add source metadata
            enriched_insight['source_service'] = 'inventory'
            enriched_insight['source_model'] = 'safety_stock_optimizer'
            enriched_insight['detected_at'] = datetime.utcnow().isoformat()

            enriched.append(enriched_insight)

        return enriched

    async def _publish_insight_events(
        self,
        tenant_id: str,
        insights: List[Dict[str, Any]],
        product_context: Optional[Dict[str, Any]] = None
    ) -> None:
        """
        Publish recommendation events to RabbitMQ for each insight.

        Args:
            tenant_id: Tenant identifier
            insights: List of created insights (with insight_id from AI Insights Service)
            product_context: Optional product context (name, id, etc.)
        """
        if not self.event_publisher:
            logger.warning("Event publisher not configured, skipping event publication")
            return

        for insight in insights:
            try:
                # Determine severity based on confidence and priority
                confidence = insight.get('confidence', 0)
                priority = insight.get('priority', 'medium')

                if priority == 'urgent' or confidence >= 90:
                    severity = 'urgent'
                elif priority == 'high' or confidence >= 70:
                    severity = 'high'
                elif priority == 'medium' or confidence >= 50:
                    severity = 'medium'
                else:
                    severity = 'low'

                # Build event metadata
                event_metadata = {
                    'insight_id': insight.get('id'),
                    'insight_type': insight.get('insight_type'),
                    'inventory_product_id': insight.get('metrics_json', {}).get('inventory_product_id'),
                    'ingredient_name': product_context.get('ingredient_name') if product_context else None,
                    'suggested_safety_stock': insight.get('metrics_json', {}).get('suggested_safety_stock'),
                    'current_safety_stock': insight.get('metrics_json', {}).get('current_safety_stock'),
                    'estimated_savings': insight.get('impact_value'),
                    'confidence': confidence,
                    'recommendation': insight.get('recommendation'),
                    'impact_type': insight.get('impact_type'),
                    'source_service': 'inventory',
                    'source_model': 'safety_stock_optimizer'
                }

                # Remove None values
                event_metadata = {k: v for k, v in event_metadata.items() if v is not None}

                # Publish recommendation event
                await self.event_publisher.publish_recommendation(
                    event_type='ai_safety_stock_optimization',
                    tenant_id=tenant_id,
                    severity=severity,
                    data=event_metadata
                )

                logger.info(
                    "Published safety stock insight recommendation event",
                    tenant_id=tenant_id,
                    insight_id=insight.get('id'),
                    insight_type=insight.get('insight_type'),
                    severity=severity
                )

            except Exception as e:
                logger.error(
                    "Failed to publish insight event",
                    tenant_id=tenant_id,
                    insight_id=insight.get('id'),
                    error=str(e),
                    exc_info=True
                )
                # Don't raise - we don't want to fail the whole workflow if event publishing fails

    async def optimize_all_products(
        self,
        tenant_id: str,
        products_data: Dict[str, Dict[str, Any]],
        min_history_days: int = 90
    ) -> Dict[str, Any]:
        """
        Optimize safety stock for all products for a tenant.

        Args:
            tenant_id: Tenant identifier
            products_data: Dict of {inventory_product_id: {
                'demand_history': DataFrame,
                'product_characteristics': dict,
                'cost_parameters': dict (optional),
                'supplier_reliability': float (optional)
            }}
            min_history_days: Minimum days of history required

        Returns:
            Comprehensive optimization results
        """
        logger.info(
            "Optimizing safety stock for all products",
            tenant_id=tenant_id,
            products=len(products_data)
        )

        all_results = []
        total_insights_posted = 0
        total_cost_savings = 0.0

        # Optimize each product
        for inventory_product_id, product_data in products_data.items():
            try:
                results = await self.optimize_and_post_insights(
                    tenant_id=tenant_id,
                    inventory_product_id=inventory_product_id,
                    demand_history=product_data['demand_history'],
                    product_characteristics=product_data['product_characteristics'],
                    cost_parameters=product_data.get('cost_parameters'),
                    supplier_reliability=product_data.get('supplier_reliability'),
                    min_history_days=min_history_days
                )

                all_results.append(results)
                total_insights_posted += results['insights_posted']

                if results.get('cost_savings'):
                    total_cost_savings += results['cost_savings']

            except Exception as e:
                logger.error(
                    "Error optimizing product",
                    inventory_product_id=inventory_product_id,
                    error=str(e)
                )

        # Generate summary insight
        if total_cost_savings > 0:
            summary_insight = self._generate_portfolio_summary_insight(
                tenant_id, all_results, total_cost_savings
            )

            if summary_insight:
                enriched_summary = self._enrich_insights(
                    [summary_insight], tenant_id, 'all_products'
                )

                post_results = await self.ai_insights_client.create_insights_bulk(
                    tenant_id=UUID(tenant_id),
                    insights=enriched_summary
                )

                total_insights_posted += post_results['successful']

        logger.info(
            "All products optimization complete",
            tenant_id=tenant_id,
            products_optimized=len(all_results),
            total_insights_posted=total_insights_posted,
            total_annual_savings=total_cost_savings
        )

        return {
            'tenant_id': tenant_id,
            'optimized_at': datetime.utcnow().isoformat(),
            'products_optimized': len(all_results),
            'product_results': all_results,
            'total_insights_posted': total_insights_posted,
            'total_annual_cost_savings': round(total_cost_savings, 2)
        }

    def _generate_portfolio_summary_insight(
        self,
        tenant_id: str,
        all_results: List[Dict[str, Any]],
        total_cost_savings: float
    ) -> Optional[Dict[str, Any]]:
        """
        Generate portfolio-level summary insight.

        Args:
            tenant_id: Tenant identifier
            all_results: All product optimization results
            total_cost_savings: Total annual cost savings

        Returns:
            Summary insight or None
        """
        if total_cost_savings < 100:  # Only if meaningful savings
            return None

        products_optimized = len(all_results)
        products_with_savings = len([r for r in all_results if r.get('cost_savings', 0) > 0])

        return {
            'type': 'optimization',
            'priority': 'high' if total_cost_savings > 1000 else 'medium',
            'category': 'inventory',
            'title': f'Portfolio Safety Stock Optimization: €{total_cost_savings:.0f}/year Savings',
            'description': f'Optimized safety stock across {products_optimized} products. {products_with_savings} products have over-stocked inventory. Implementing optimal levels saves €{total_cost_savings:.2f} annually in holding costs while maintaining or improving service levels.',
            'impact_type': 'cost_savings',
            'impact_value': total_cost_savings,
            'impact_unit': 'euros_per_year',
            'confidence': 85,
            'metrics_json': {
                'products_optimized': products_optimized,
                'products_with_savings': products_with_savings,
                'total_annual_savings': round(total_cost_savings, 2)
            },
            'actionable': True,
            'recommendation_actions': [
                {
                    'label': 'Apply All Optimizations',
                    'action': 'apply_all_safety_stock_optimizations',
                    'params': {'tenant_id': tenant_id}
                },
                {
                    'label': 'Review Individual Products',
                    'action': 'review_safety_stock_insights',
                    'params': {'tenant_id': tenant_id}
                }
            ],
            'source_service': 'inventory',
            'source_model': 'safety_stock_optimizer'
        }

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

        Args:
            inventory_product_id: Product identifier

        Returns:
            Optimal safety stock or None if not optimized
        """
        return self.optimizer.get_optimal_safety_stock(inventory_product_id)

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

        Args:
            inventory_product_id: Product identifier

        Returns:
            Optimal service level (0-1) or None if not learned
        """
        return self.optimizer.get_learned_service_level(inventory_product_id)

    async def close(self):
        """Close HTTP client connections."""
        await self.ai_insights_client.close()
