"""
Yield Insights Orchestrator
Coordinates yield prediction 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.yield_predictor import YieldPredictor

logger = structlog.get_logger()


class YieldInsightsOrchestrator:
    """
    Orchestrates yield prediction and insight generation workflow.

    Workflow:
    1. Predict yield for upcoming production run or analyze historical performance
    2. Generate insights for yield optimization opportunities
    3. Post insights to AI Insights Service
    4. Publish recommendation events to RabbitMQ
    5. Provide yield predictions for production planning
    """

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

    async def predict_and_post_insights(
        self,
        tenant_id: str,
        recipe_id: str,
        production_history: pd.DataFrame,
        production_context: Dict[str, Any],
        min_history_runs: int = 30
    ) -> Dict[str, Any]:
        """
        Complete workflow: Predict yield and post insights.

        Args:
            tenant_id: Tenant identifier
            recipe_id: Recipe identifier
            production_history: Historical production runs
            production_context: Upcoming production context:
                - staff_assigned (list of staff IDs)
                - planned_start_time
                - batch_size
                - planned_quantity
                - unit_cost (optional)
                - equipment_id (optional)
            min_history_runs: Minimum production runs required

        Returns:
            Workflow results with prediction and posted insights
        """
        logger.info(
            "Starting yield prediction workflow",
            tenant_id=tenant_id,
            recipe_id=recipe_id,
            history_runs=len(production_history)
        )

        # Step 1: Predict yield
        prediction_results = await self.predictor.predict_yield(
            tenant_id=tenant_id,
            recipe_id=recipe_id,
            production_history=production_history,
            production_context=production_context,
            min_history_runs=min_history_runs
        )

        logger.info(
            "Yield prediction complete",
            recipe_id=recipe_id,
            predicted_yield=prediction_results.get('predicted_yield'),
            insights_generated=len(prediction_results.get('insights', []))
        )

        # Step 2: Enrich insights with tenant_id and recipe context
        enriched_insights = self._enrich_insights(
            prediction_results.get('insights', []),
            tenant_id,
            recipe_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(
                "Yield insights posted to AI Insights Service",
                recipe_id=recipe_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:
                recipe_context = production_context.copy() if production_context else {}
                recipe_context['recipe_id'] = recipe_id
                await self._publish_insight_events(
                    tenant_id=tenant_id,
                    insights=created_insights,
                    recipe_context=recipe_context
                )
        else:
            post_results = {'total': 0, 'successful': 0, 'failed': 0}
            logger.info("No insights to post for recipe", recipe_id=recipe_id)

        # Step 4: Return comprehensive results
        return {
            'tenant_id': tenant_id,
            'recipe_id': recipe_id,
            'predicted_at': prediction_results['predicted_at'],
            'history_runs': prediction_results['history_runs'],
            'baseline_yield': prediction_results.get('baseline_yield'),
            'predicted_yield': prediction_results.get('predicted_yield'),
            'prediction_range': prediction_results.get('prediction_range'),
            'expected_waste': prediction_results.get('expected_waste'),
            'confidence': prediction_results['confidence'],
            'factor_analysis': prediction_results.get('factor_analysis'),
            'patterns': prediction_results.get('patterns', []),
            'insights_generated': len(enriched_insights),
            'insights_posted': post_results['successful'],
            'insights_failed': post_results['failed'],
            'created_insights': post_results.get('created_insights', [])
        }

    async def analyze_and_post_insights(
        self,
        tenant_id: str,
        recipe_id: str,
        production_history: pd.DataFrame,
        min_history_runs: int = 30
    ) -> Dict[str, Any]:
        """
        Analyze historical yield performance and post insights (no prediction).

        Args:
            tenant_id: Tenant identifier
            recipe_id: Recipe identifier
            production_history: Historical production runs
            min_history_runs: Minimum production runs required

        Returns:
            Workflow results with analysis and posted insights
        """
        logger.info(
            "Starting yield analysis workflow",
            tenant_id=tenant_id,
            recipe_id=recipe_id,
            history_runs=len(production_history)
        )

        # Step 1: Analyze historical yield
        analysis_results = await self.predictor.analyze_recipe_yield_history(
            tenant_id=tenant_id,
            recipe_id=recipe_id,
            production_history=production_history,
            min_history_runs=min_history_runs
        )

        logger.info(
            "Yield analysis complete",
            recipe_id=recipe_id,
            baseline_yield=analysis_results.get('baseline_stats', {}).get('mean_yield'),
            insights_generated=len(analysis_results.get('insights', []))
        )

        # Step 2: Enrich insights
        enriched_insights = self._enrich_insights(
            analysis_results.get('insights', []),
            tenant_id,
            recipe_id
        )

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

            logger.info(
                "Yield analysis insights posted",
                recipe_id=recipe_id,
                total=post_results['total'],
                successful=post_results['successful']
            )

            # Step 4: Publish recommendation events to RabbitMQ
            created_insights = post_results.get('created_insights', [])
            if created_insights:
                await self._publish_insight_events(
                    tenant_id=tenant_id,
                    insights=created_insights,
                    recipe_context={'recipe_id': recipe_id}
                )
        else:
            post_results = {'total': 0, 'successful': 0, 'failed': 0}

        return {
            'tenant_id': tenant_id,
            'recipe_id': recipe_id,
            'analyzed_at': analysis_results['analyzed_at'],
            'history_runs': analysis_results['history_runs'],
            'baseline_stats': analysis_results.get('baseline_stats'),
            'factor_analysis': analysis_results.get('factor_analysis'),
            'patterns': analysis_results.get('patterns', []),
            'insights_generated': len(enriched_insights),
            'insights_posted': post_results['successful'],
            'created_insights': post_results.get('created_insights', [])
        }

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

        Args:
            insights: Raw insights from predictor
            tenant_id: Tenant identifier
            recipe_id: Recipe 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 recipe context to metrics
            if 'metrics_json' not in enriched_insight:
                enriched_insight['metrics_json'] = {}

            enriched_insight['metrics_json']['recipe_id'] = recipe_id

            # Add source metadata
            enriched_insight['source_service'] = 'production'
            enriched_insight['source_model'] = 'yield_predictor'
            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]],
        recipe_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)
            recipe_context: Optional recipe 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'),  # From AI Insights Service response
                    'insight_type': insight.get('insight_type'),
                    'recipe_id': insight.get('metrics_json', {}).get('recipe_id'),
                    'recipe_name': recipe_context.get('recipe_name') if recipe_context else None,
                    'predicted_yield': insight.get('metrics_json', {}).get('predicted_yield'),
                    'confidence': confidence,
                    'recommendation': insight.get('recommendation'),
                    'impact_type': insight.get('impact_type'),
                    'impact_value': insight.get('impact_value'),
                    'source_service': 'production',
                    'source_model': 'yield_predictor'
                }

                # 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_yield_prediction',
                    tenant_id=tenant_id,
                    severity=severity,
                    data=event_metadata
                )

                logger.info(
                    "Published yield 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 analyze_all_recipes(
        self,
        tenant_id: str,
        recipes_data: Dict[str, pd.DataFrame],
        min_history_runs: int = 30
    ) -> Dict[str, Any]:
        """
        Analyze yield performance for all recipes for a tenant.

        Args:
            tenant_id: Tenant identifier
            recipes_data: Dict of {recipe_id: production_history_df}
            min_history_runs: Minimum production runs required

        Returns:
            Comprehensive analysis results
        """
        logger.info(
            "Analyzing yield for all recipes",
            tenant_id=tenant_id,
            recipes=len(recipes_data)
        )

        all_results = []
        total_insights_posted = 0
        recipes_with_issues = []

        # Analyze each recipe
        for recipe_id, production_history in recipes_data.items():
            try:
                results = await self.analyze_and_post_insights(
                    tenant_id=tenant_id,
                    recipe_id=recipe_id,
                    production_history=production_history,
                    min_history_runs=min_history_runs
                )

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

                # Check for low baseline yield
                baseline_stats = results.get('baseline_stats')
                if baseline_stats and baseline_stats.get('mean_yield', 100) < 90:
                    recipes_with_issues.append({
                        'recipe_id': recipe_id,
                        'mean_yield': baseline_stats['mean_yield'],
                        'std_yield': baseline_stats['std_yield']
                    })

            except Exception as e:
                logger.error(
                    "Error analyzing recipe",
                    recipe_id=recipe_id,
                    error=str(e)
                )

        # Generate portfolio summary insight if there are yield issues
        if len(recipes_with_issues) > 0:
            summary_insight = self._generate_portfolio_summary_insight(
                tenant_id, recipes_with_issues, all_results
            )

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

                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 recipes yield analysis complete",
            tenant_id=tenant_id,
            recipes_analyzed=len(all_results),
            total_insights_posted=total_insights_posted,
            recipes_with_issues=len(recipes_with_issues)
        )

        return {
            'tenant_id': tenant_id,
            'analyzed_at': datetime.utcnow().isoformat(),
            'recipes_analyzed': len(all_results),
            'recipe_results': all_results,
            'total_insights_posted': total_insights_posted,
            'recipes_with_issues': recipes_with_issues
        }

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

        Args:
            tenant_id: Tenant identifier
            recipes_with_issues: Recipes with low yield
            all_results: All recipe analysis results

        Returns:
            Summary insight or None
        """
        if len(recipes_with_issues) == 0:
            return None

        # Calculate average yield and potential improvement
        total_recipes = len(all_results)
        issues_count = len(recipes_with_issues)
        avg_low_yield = sum(r['mean_yield'] for r in recipes_with_issues) / issues_count

        # Estimate waste reduction potential
        # Assuming each recipe produces 1000 units/month, €5/unit cost
        monthly_production = 1000 * issues_count
        current_waste_pct = 100 - avg_low_yield
        target_waste_pct = 5  # Target 95% yield

        if current_waste_pct > target_waste_pct:
            waste_reduction_units = monthly_production * ((current_waste_pct - target_waste_pct) / 100)
            annual_savings = waste_reduction_units * 12 * 5  # €5 per unit

            return {
                'type': 'opportunity',
                'priority': 'high' if issues_count > 3 else 'medium',
                'category': 'production',
                'title': f'Production Yield Optimization: {issues_count} Recipes Below 90%',
                'description': f'{issues_count} of {total_recipes} recipes have average yield below 90% (average {avg_low_yield:.1f}%). Improving to 95% target would reduce waste by {waste_reduction_units:.0f} units/month, saving €{annual_savings:.0f}/year.',
                'impact_type': 'cost_savings',
                'impact_value': annual_savings,
                'impact_unit': 'euros_per_year',
                'confidence': 75,
                'metrics_json': {
                    'recipes_analyzed': total_recipes,
                    'recipes_with_issues': issues_count,
                    'avg_low_yield': round(avg_low_yield, 2),
                    'potential_annual_savings': round(annual_savings, 2),
                    'waste_reduction_units_monthly': round(waste_reduction_units, 2)
                },
                'actionable': True,
                'recommendation_actions': [
                    {
                        'label': 'Review Low-Yield Recipes',
                        'action': 'review_yield_insights',
                        'params': {'tenant_id': tenant_id}
                    },
                    {
                        'label': 'Implement Yield Improvements',
                        'action': 'apply_yield_recommendations',
                        'params': {'tenant_id': tenant_id}
                    }
                ],
                'source_service': 'production',
                'source_model': 'yield_predictor'
            }

        return None

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