"""
AI-Enhanced Orchestration Saga
Integrates ML insights into daily workflow orchestration
"""

import pandas as pd
import numpy as np
from typing import Dict, List, Any, Optional, Tuple
from datetime import datetime, timedelta
from uuid import UUID
import structlog

from shared.clients.ai_insights_client import AIInsightsClient

logger = structlog.get_logger()


class AIEnhancedOrchestrator:
    """
    Enhanced orchestration engine that integrates ML insights into daily workflow.

    Workflow:
    1. Pre-Orchestration: Gather all relevant insights for target date
    2. Intelligent Planning: Modify orchestration plan based on insights
    3. Execution: Apply insights with confidence-based decision making
    4. Feedback Tracking: Record outcomes for continuous learning

    Replaces hardcoded logic with learned intelligence from:
    - Demand Forecasting
    - Supplier Performance
    - Safety Stock Optimization
    - Price Forecasting
    - Production Yield Prediction
    - Dynamic Business Rules
    """

    def __init__(
        self,
        ai_insights_base_url: str = "http://ai-insights-service:8000",
        min_confidence_threshold: int = 70
    ):
        self.ai_insights_client = AIInsightsClient(ai_insights_base_url)
        self.min_confidence_threshold = min_confidence_threshold
        self.applied_insights = []  # Track applied insights for feedback

    async def orchestrate_with_ai(
        self,
        tenant_id: str,
        target_date: datetime,
        base_orchestration_plan: Optional[Dict[str, Any]] = None
    ) -> Dict[str, Any]:
        """
        Run AI-enhanced orchestration for a target date.

        Args:
            tenant_id: Tenant identifier
            target_date: Date to orchestrate for
            base_orchestration_plan: Optional base plan to enhance (if None, creates new)

        Returns:
            Enhanced orchestration plan with applied insights and metadata
        """
        logger.info(
            "Starting AI-enhanced orchestration",
            tenant_id=tenant_id,
            target_date=target_date.isoformat()
        )

        # Step 1: Gather insights for target date
        insights = await self._gather_insights(tenant_id, target_date)

        logger.info(
            "Insights gathered",
            demand_forecasts=len(insights['demand_forecasts']),
            supplier_alerts=len(insights['supplier_alerts']),
            inventory_optimizations=len(insights['inventory_optimizations']),
            price_opportunities=len(insights['price_opportunities']),
            yield_predictions=len(insights['yield_predictions']),
            business_rules=len(insights['business_rules'])
        )

        # Step 2: Initialize or load base plan
        if base_orchestration_plan is None:
            orchestration_plan = self._create_base_plan(target_date)
        else:
            orchestration_plan = base_orchestration_plan.copy()

        # Step 3: Apply insights to plan
        enhanced_plan = await self._apply_insights_to_plan(
            orchestration_plan, insights, tenant_id
        )

        # Step 4: Generate execution summary
        execution_summary = self._generate_execution_summary(
            enhanced_plan, insights
        )

        logger.info(
            "AI-enhanced orchestration complete",
            tenant_id=tenant_id,
            insights_applied=execution_summary['total_insights_applied'],
            modifications=execution_summary['total_modifications']
        )

        return {
            'tenant_id': tenant_id,
            'target_date': target_date.isoformat(),
            'orchestrated_at': datetime.utcnow().isoformat(),
            'plan': enhanced_plan,
            'insights_used': insights,
            'execution_summary': execution_summary,
            'applied_insights': self.applied_insights
        }

    async def _gather_insights(
        self,
        tenant_id: str,
        target_date: datetime
    ) -> Dict[str, List[Dict[str, Any]]]:
        """
        Gather all relevant insights for target date from AI Insights Service.

        Returns insights categorized by type:
        - demand_forecasts
        - supplier_alerts
        - inventory_optimizations
        - price_opportunities
        - yield_predictions
        - business_rules
        """
        # Get orchestration-ready insights
        insights = await self.ai_insights_client.get_orchestration_ready_insights(
            tenant_id=UUID(tenant_id),
            target_date=target_date,
            min_confidence=self.min_confidence_threshold
        )

        # Categorize insights by source
        categorized = {
            'demand_forecasts': [],
            'supplier_alerts': [],
            'inventory_optimizations': [],
            'price_opportunities': [],
            'yield_predictions': [],
            'business_rules': [],
            'other': []
        }

        for insight in insights:
            source_model = insight.get('source_model', '')
            category = insight.get('category', '')

            if source_model == 'hybrid_forecaster' or category == 'demand':
                categorized['demand_forecasts'].append(insight)
            elif source_model == 'supplier_performance_predictor':
                categorized['supplier_alerts'].append(insight)
            elif source_model == 'safety_stock_optimizer':
                categorized['inventory_optimizations'].append(insight)
            elif source_model == 'price_forecaster':
                categorized['price_opportunities'].append(insight)
            elif source_model == 'yield_predictor':
                categorized['yield_predictions'].append(insight)
            elif source_model == 'business_rules_engine':
                categorized['business_rules'].append(insight)
            else:
                categorized['other'].append(insight)

        return categorized

    def _create_base_plan(self, target_date: datetime) -> Dict[str, Any]:
        """Create base orchestration plan with default hardcoded values."""
        return {
            'target_date': target_date.isoformat(),
            'procurement': {
                'orders': [],
                'supplier_selections': {},
                'order_quantities': {}
            },
            'inventory': {
                'safety_stock_levels': {},
                'reorder_points': {},
                'transfers': []
            },
            'production': {
                'production_runs': [],
                'recipe_quantities': {},
                'worker_assignments': {}
            },
            'sales': {
                'forecasted_demand': {},
                'pricing_adjustments': {}
            },
            'modifications': [],
            'ai_enhancements': []
        }

    async def _apply_insights_to_plan(
        self,
        plan: Dict[str, Any],
        insights: Dict[str, List[Dict[str, Any]]],
        tenant_id: str
    ) -> Dict[str, Any]:
        """
        Apply categorized insights to orchestration plan.

        Each insight type modifies specific parts of the plan:
        - Demand forecasts → sales forecasts, production quantities
        - Supplier alerts → supplier selection, procurement timing
        - Inventory optimizations → safety stock levels, reorder points
        - Price opportunities → procurement timing, order quantities
        - Yield predictions → production quantities, worker assignments
        - Business rules → cross-cutting modifications
        """
        enhanced_plan = plan.copy()

        # Apply demand forecasts
        if insights['demand_forecasts']:
            enhanced_plan = await self._apply_demand_forecasts(
                enhanced_plan, insights['demand_forecasts'], tenant_id
            )

        # Apply supplier alerts
        if insights['supplier_alerts']:
            enhanced_plan = await self._apply_supplier_alerts(
                enhanced_plan, insights['supplier_alerts'], tenant_id
            )

        # Apply inventory optimizations
        if insights['inventory_optimizations']:
            enhanced_plan = await self._apply_inventory_optimizations(
                enhanced_plan, insights['inventory_optimizations'], tenant_id
            )

        # Apply price opportunities
        if insights['price_opportunities']:
            enhanced_plan = await self._apply_price_opportunities(
                enhanced_plan, insights['price_opportunities'], tenant_id
            )

        # Apply yield predictions
        if insights['yield_predictions']:
            enhanced_plan = await self._apply_yield_predictions(
                enhanced_plan, insights['yield_predictions'], tenant_id
            )

        # Apply business rules (highest priority, can override)
        if insights['business_rules']:
            enhanced_plan = await self._apply_business_rules(
                enhanced_plan, insights['business_rules'], tenant_id
            )

        return enhanced_plan

    async def _apply_demand_forecasts(
        self,
        plan: Dict[str, Any],
        forecasts: List[Dict[str, Any]],
        tenant_id: str
    ) -> Dict[str, Any]:
        """
        Apply demand forecasts to sales and production planning.

        Modifications:
        - Update sales forecasted_demand
        - Adjust production recipe_quantities
        - Record insight application
        """
        for forecast in forecasts:
            if forecast['confidence'] < self.min_confidence_threshold:
                continue

            metrics = forecast.get('metrics_json', {})
            product_id = metrics.get('product_id')
            predicted_demand = metrics.get('predicted_demand')
            forecast_date = metrics.get('forecast_date')

            if not product_id or predicted_demand is None:
                continue

            # Update sales forecast
            plan['sales']['forecasted_demand'][product_id] = {
                'quantity': predicted_demand,
                'confidence': forecast['confidence'],
                'source': 'ai_forecast',
                'insight_id': forecast.get('id')
            }

            # Adjust production quantities (demand + buffer)
            buffer_pct = 1.10  # 10% buffer for uncertainty
            production_quantity = int(predicted_demand * buffer_pct)

            plan['production']['recipe_quantities'][product_id] = {
                'quantity': production_quantity,
                'demand_forecast': predicted_demand,
                'buffer_applied': buffer_pct,
                'source': 'ai_forecast',
                'insight_id': forecast.get('id')
            }

            # Record modification
            plan['modifications'].append({
                'type': 'demand_forecast_applied',
                'insight_id': forecast.get('id'),
                'product_id': product_id,
                'predicted_demand': predicted_demand,
                'production_quantity': production_quantity,
                'confidence': forecast['confidence']
            })

            # Track for feedback
            self.applied_insights.append({
                'insight_id': forecast.get('id'),
                'type': 'demand_forecast',
                'applied_at': datetime.utcnow().isoformat(),
                'tenant_id': tenant_id,
                'metrics': {
                    'product_id': product_id,
                    'predicted_demand': predicted_demand,
                    'production_quantity': production_quantity
                }
            })

            logger.info(
                "Applied demand forecast",
                product_id=product_id,
                predicted_demand=predicted_demand,
                production_quantity=production_quantity
            )

        return plan

    async def _apply_supplier_alerts(
        self,
        plan: Dict[str, Any],
        alerts: List[Dict[str, Any]],
        tenant_id: str
    ) -> Dict[str, Any]:
        """
        Apply supplier performance alerts to procurement decisions.

        Modifications:
        - Switch suppliers for low reliability
        - Adjust lead times for delays
        - Increase order quantities for short deliveries
        """
        for alert in alerts:
            if alert['confidence'] < self.min_confidence_threshold:
                continue

            metrics = alert.get('metrics_json', {})
            supplier_id = metrics.get('supplier_id')
            reliability_score = metrics.get('reliability_score')
            predicted_delay = metrics.get('predicted_delivery_delay_days')

            if not supplier_id:
                continue

            # Low reliability: recommend supplier switch
            if reliability_score and reliability_score < 70:
                plan['procurement']['supplier_selections'][supplier_id] = {
                    'action': 'avoid',
                    'reason': f'Low reliability score: {reliability_score}',
                    'alternative_required': True,
                    'source': 'supplier_alert',
                    'insight_id': alert.get('id')
                }

                plan['modifications'].append({
                    'type': 'supplier_switch_recommended',
                    'insight_id': alert.get('id'),
                    'supplier_id': supplier_id,
                    'reliability_score': reliability_score,
                    'confidence': alert['confidence']
                })

            # Delay predicted: adjust lead time
            if predicted_delay and predicted_delay > 1:
                plan['procurement']['supplier_selections'][supplier_id] = {
                    'action': 'adjust_lead_time',
                    'additional_lead_days': int(predicted_delay),
                    'reason': f'Predicted delay: {predicted_delay} days',
                    'source': 'supplier_alert',
                    'insight_id': alert.get('id')
                }

                plan['modifications'].append({
                    'type': 'lead_time_adjusted',
                    'insight_id': alert.get('id'),
                    'supplier_id': supplier_id,
                    'additional_days': int(predicted_delay),
                    'confidence': alert['confidence']
                })

            # Track for feedback
            self.applied_insights.append({
                'insight_id': alert.get('id'),
                'type': 'supplier_alert',
                'applied_at': datetime.utcnow().isoformat(),
                'tenant_id': tenant_id,
                'metrics': {
                    'supplier_id': supplier_id,
                    'reliability_score': reliability_score,
                    'predicted_delay': predicted_delay
                }
            })

            logger.info(
                "Applied supplier alert",
                supplier_id=supplier_id,
                reliability_score=reliability_score,
                predicted_delay=predicted_delay
            )

        return plan

    async def _apply_inventory_optimizations(
        self,
        plan: Dict[str, Any],
        optimizations: List[Dict[str, Any]],
        tenant_id: str
    ) -> Dict[str, Any]:
        """
        Apply safety stock optimizations to inventory management.

        Modifications:
        - Update safety stock levels (from hardcoded 95% to learned optimal)
        - Adjust reorder points accordingly
        """
        for optimization in optimizations:
            if optimization['confidence'] < self.min_confidence_threshold:
                continue

            metrics = optimization.get('metrics_json', {})
            product_id = metrics.get('inventory_product_id')
            optimal_safety_stock = metrics.get('optimal_safety_stock')
            optimal_service_level = metrics.get('optimal_service_level')

            if not product_id or optimal_safety_stock is None:
                continue

            # Update safety stock level
            plan['inventory']['safety_stock_levels'][product_id] = {
                'quantity': optimal_safety_stock,
                'service_level': optimal_service_level,
                'source': 'ai_optimization',
                'insight_id': optimization.get('id'),
                'replaced_hardcoded': True
            }

            # Adjust reorder point (lead time demand + safety stock)
            # This would use demand forecast if available
            lead_time_demand = metrics.get('lead_time_demand', optimal_safety_stock * 2)
            reorder_point = lead_time_demand + optimal_safety_stock

            plan['inventory']['reorder_points'][product_id] = {
                'quantity': reorder_point,
                'lead_time_demand': lead_time_demand,
                'safety_stock': optimal_safety_stock,
                'source': 'ai_optimization',
                'insight_id': optimization.get('id')
            }

            plan['modifications'].append({
                'type': 'safety_stock_optimized',
                'insight_id': optimization.get('id'),
                'product_id': product_id,
                'optimal_safety_stock': optimal_safety_stock,
                'optimal_service_level': optimal_service_level,
                'confidence': optimization['confidence']
            })

            # Track for feedback
            self.applied_insights.append({
                'insight_id': optimization.get('id'),
                'type': 'inventory_optimization',
                'applied_at': datetime.utcnow().isoformat(),
                'tenant_id': tenant_id,
                'metrics': {
                    'product_id': product_id,
                    'optimal_safety_stock': optimal_safety_stock,
                    'reorder_point': reorder_point
                }
            })

            logger.info(
                "Applied safety stock optimization",
                product_id=product_id,
                optimal_safety_stock=optimal_safety_stock,
                reorder_point=reorder_point
            )

        return plan

    async def _apply_price_opportunities(
        self,
        plan: Dict[str, Any],
        opportunities: List[Dict[str, Any]],
        tenant_id: str
    ) -> Dict[str, Any]:
        """
        Apply price forecasting opportunities to procurement timing.

        Modifications:
        - Advance orders for predicted price increases
        - Delay orders for predicted price decreases
        - Increase quantities for bulk opportunities
        """
        for opportunity in opportunities:
            if opportunity['confidence'] < self.min_confidence_threshold:
                continue

            metrics = opportunity.get('metrics_json', {})
            ingredient_id = metrics.get('ingredient_id')
            recommendation = metrics.get('recommendation')
            expected_price_change = metrics.get('expected_price_change_pct')

            if not ingredient_id or not recommendation:
                continue

            # Buy now: price increasing
            if recommendation == 'buy_now' and expected_price_change and expected_price_change > 5:
                plan['procurement']['order_quantities'][ingredient_id] = {
                    'action': 'increase',
                    'multiplier': 1.5,  # Buy 50% more
                    'reason': f'Price expected to increase {expected_price_change:.1f}%',
                    'source': 'price_forecast',
                    'insight_id': opportunity.get('id')
                }

                plan['modifications'].append({
                    'type': 'bulk_purchase_opportunity',
                    'insight_id': opportunity.get('id'),
                    'ingredient_id': ingredient_id,
                    'expected_price_change': expected_price_change,
                    'quantity_multiplier': 1.5,
                    'confidence': opportunity['confidence']
                })

            # Wait: price decreasing
            elif recommendation == 'wait' and expected_price_change and expected_price_change < -5:
                plan['procurement']['order_quantities'][ingredient_id] = {
                    'action': 'delay',
                    'delay_days': 7,
                    'reason': f'Price expected to decrease {abs(expected_price_change):.1f}%',
                    'source': 'price_forecast',
                    'insight_id': opportunity.get('id')
                }

                plan['modifications'].append({
                    'type': 'procurement_delayed',
                    'insight_id': opportunity.get('id'),
                    'ingredient_id': ingredient_id,
                    'expected_price_change': expected_price_change,
                    'delay_days': 7,
                    'confidence': opportunity['confidence']
                })

            # Track for feedback
            self.applied_insights.append({
                'insight_id': opportunity.get('id'),
                'type': 'price_opportunity',
                'applied_at': datetime.utcnow().isoformat(),
                'tenant_id': tenant_id,
                'metrics': {
                    'ingredient_id': ingredient_id,
                    'recommendation': recommendation,
                    'expected_price_change': expected_price_change
                }
            })

            logger.info(
                "Applied price opportunity",
                ingredient_id=ingredient_id,
                recommendation=recommendation,
                expected_price_change=expected_price_change
            )

        return plan

    async def _apply_yield_predictions(
        self,
        plan: Dict[str, Any],
        predictions: List[Dict[str, Any]],
        tenant_id: str
    ) -> Dict[str, Any]:
        """
        Apply production yield predictions to production planning.

        Modifications:
        - Increase production quantities for low predicted yield
        - Optimize worker assignments
        - Adjust production timing
        """
        for prediction in predictions:
            if prediction['confidence'] < self.min_confidence_threshold:
                continue

            metrics = prediction.get('metrics_json', {})
            recipe_id = metrics.get('recipe_id')
            predicted_yield = metrics.get('predicted_yield')
            expected_waste = metrics.get('expected_waste')

            if not recipe_id or predicted_yield is None:
                continue

            # Low yield: increase production quantity to compensate
            if predicted_yield < 90:
                current_quantity = plan['production']['recipe_quantities'].get(
                    recipe_id, {}
                ).get('quantity', 100)

                # Adjust quantity to account for predicted waste
                adjusted_quantity = int(current_quantity * (100 / predicted_yield))

                plan['production']['recipe_quantities'][recipe_id] = {
                    'quantity': adjusted_quantity,
                    'predicted_yield': predicted_yield,
                    'waste_compensation': adjusted_quantity - current_quantity,
                    'source': 'yield_prediction',
                    'insight_id': prediction.get('id')
                }

                plan['modifications'].append({
                    'type': 'yield_compensation_applied',
                    'insight_id': prediction.get('id'),
                    'recipe_id': recipe_id,
                    'predicted_yield': predicted_yield,
                    'original_quantity': current_quantity,
                    'adjusted_quantity': adjusted_quantity,
                    'confidence': prediction['confidence']
                })

            # Track for feedback
            self.applied_insights.append({
                'insight_id': prediction.get('id'),
                'type': 'yield_prediction',
                'applied_at': datetime.utcnow().isoformat(),
                'tenant_id': tenant_id,
                'metrics': {
                    'recipe_id': recipe_id,
                    'predicted_yield': predicted_yield,
                    'expected_waste': expected_waste
                }
            })

            logger.info(
                "Applied yield prediction",
                recipe_id=recipe_id,
                predicted_yield=predicted_yield
            )

        return plan

    async def _apply_business_rules(
        self,
        plan: Dict[str, Any],
        rules: List[Dict[str, Any]],
        tenant_id: str
    ) -> Dict[str, Any]:
        """
        Apply dynamic business rules to orchestration plan.

        Business rules can override other insights based on business logic.
        """
        for rule in rules:
            if rule['confidence'] < self.min_confidence_threshold:
                continue

            # Business rules are flexible and defined in JSONB
            # Parse recommendation_actions to understand what to apply
            actions = rule.get('recommendation_actions', [])

            for action in actions:
                action_type = action.get('action')
                params = action.get('params', {})

                # Example: Force supplier switch
                if action_type == 'force_supplier_switch':
                    supplier_id = params.get('from_supplier_id')
                    alternate_id = params.get('to_supplier_id')

                    if supplier_id and alternate_id:
                        plan['procurement']['supplier_selections'][supplier_id] = {
                            'action': 'replace',
                            'alternate_supplier': alternate_id,
                            'reason': rule.get('description'),
                            'source': 'business_rule',
                            'insight_id': rule.get('id'),
                            'override': True
                        }

                # Example: Halt production
                elif action_type == 'halt_production':
                    recipe_id = params.get('recipe_id')
                    if recipe_id:
                        plan['production']['recipe_quantities'][recipe_id] = {
                            'quantity': 0,
                            'halted': True,
                            'reason': rule.get('description'),
                            'source': 'business_rule',
                            'insight_id': rule.get('id')
                        }

            plan['modifications'].append({
                'type': 'business_rule_applied',
                'insight_id': rule.get('id'),
                'rule_description': rule.get('description'),
                'confidence': rule['confidence']
            })

            # Track for feedback
            self.applied_insights.append({
                'insight_id': rule.get('id'),
                'type': 'business_rule',
                'applied_at': datetime.utcnow().isoformat(),
                'tenant_id': tenant_id,
                'metrics': {'actions': len(actions)}
            })

            logger.info(
                "Applied business rule",
                rule_description=rule.get('title')
            )

        return plan

    def _generate_execution_summary(
        self,
        plan: Dict[str, Any],
        insights: Dict[str, List[Dict[str, Any]]]
    ) -> Dict[str, Any]:
        """Generate summary of AI-enhanced orchestration execution."""
        total_insights_available = sum(len(v) for v in insights.values())
        total_insights_applied = len(self.applied_insights)
        total_modifications = len(plan.get('modifications', []))

        # Count by type
        insights_by_type = {}
        for category, category_insights in insights.items():
            insights_by_type[category] = {
                'available': len(category_insights),
                'applied': len([
                    i for i in self.applied_insights
                    if i['type'] == category.rstrip('s')  # Remove plural
                ])
            }

        return {
            'total_insights_available': total_insights_available,
            'total_insights_applied': total_insights_applied,
            'total_modifications': total_modifications,
            'application_rate': round(
                (total_insights_applied / total_insights_available * 100)
                if total_insights_available > 0 else 0,
                2
            ),
            'insights_by_type': insights_by_type,
            'modifications_summary': self._summarize_modifications(plan)
        }

    def _summarize_modifications(self, plan: Dict[str, Any]) -> Dict[str, int]:
        """Summarize modifications by type."""
        modifications = plan.get('modifications', [])
        summary = {}

        for mod in modifications:
            mod_type = mod.get('type', 'unknown')
            summary[mod_type] = summary.get(mod_type, 0) + 1

        return summary

    async def record_orchestration_feedback(
        self,
        tenant_id: str,
        target_date: datetime,
        actual_outcomes: Dict[str, Any]
    ) -> Dict[str, Any]:
        """
        Record feedback for applied insights to enable continuous learning.

        Args:
            tenant_id: Tenant identifier
            target_date: Orchestration target date
            actual_outcomes: Actual results:
                - actual_demand: {product_id: actual_quantity}
                - actual_yields: {recipe_id: actual_yield_pct}
                - actual_costs: {ingredient_id: actual_price}
                - supplier_performance: {supplier_id: on_time_delivery}

        Returns:
            Feedback recording results
        """
        logger.info(
            "Recording orchestration feedback",
            tenant_id=tenant_id,
            target_date=target_date.isoformat(),
            applied_insights=len(self.applied_insights)
        )

        feedback_results = []

        for applied in self.applied_insights:
            insight_id = applied.get('insight_id')
            insight_type = applied.get('type')
            metrics = applied.get('metrics', {})

            # Prepare feedback based on type
            feedback_data = {
                'applied': True,
                'applied_at': applied.get('applied_at'),
                'outcome_date': target_date.isoformat()
            }

            # Demand forecast feedback
            if insight_type == 'demand_forecast':
                product_id = metrics.get('product_id')
                predicted_demand = metrics.get('predicted_demand')
                actual_demand = actual_outcomes.get('actual_demand', {}).get(product_id)

                if actual_demand is not None:
                    error = abs(actual_demand - predicted_demand)
                    error_pct = (error / actual_demand * 100) if actual_demand > 0 else 0

                    feedback_data['outcome_metrics'] = {
                        'predicted_demand': predicted_demand,
                        'actual_demand': actual_demand,
                        'error': error,
                        'error_pct': round(error_pct, 2),
                        'accuracy': round(100 - error_pct, 2)
                    }

            # Yield prediction feedback
            elif insight_type == 'yield_prediction':
                recipe_id = metrics.get('recipe_id')
                predicted_yield = metrics.get('predicted_yield')
                actual_yield = actual_outcomes.get('actual_yields', {}).get(recipe_id)

                if actual_yield is not None:
                    error = abs(actual_yield - predicted_yield)

                    feedback_data['outcome_metrics'] = {
                        'predicted_yield': predicted_yield,
                        'actual_yield': actual_yield,
                        'error': round(error, 2),
                        'accuracy': round(100 - (error / actual_yield * 100), 2) if actual_yield > 0 else 0
                    }

            # Record feedback via AI Insights Client
            try:
                await self.ai_insights_client.record_feedback(
                    tenant_id=UUID(tenant_id),
                    insight_id=UUID(insight_id) if insight_id else None,
                    feedback_data=feedback_data
                )

                feedback_results.append({
                    'insight_id': insight_id,
                    'insight_type': insight_type,
                    'status': 'recorded',
                    'feedback': feedback_data
                })

            except Exception as e:
                logger.error(
                    "Error recording feedback",
                    insight_id=insight_id,
                    error=str(e)
                )
                feedback_results.append({
                    'insight_id': insight_id,
                    'insight_type': insight_type,
                    'status': 'failed',
                    'error': str(e)
                })

        logger.info(
            "Feedback recording complete",
            total=len(feedback_results),
            successful=len([r for r in feedback_results if r['status'] == 'recorded'])
        )

        return {
            'tenant_id': tenant_id,
            'target_date': target_date.isoformat(),
            'feedback_recorded_at': datetime.utcnow().isoformat(),
            'total_insights': len(self.applied_insights),
            'feedback_results': feedback_results,
            'successful': len([r for r in feedback_results if r['status'] == 'recorded']),
            'failed': len([r for r in feedback_results if r['status'] == 'failed'])
        }

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