"""
ROAS Optimization Module for Meta Ads MCP Server

This module provides ROAS and efficiency optimization tools for maximizing
return on ad spend through detailed breakdown analysis and actionable recommendations.
"""

import asyncio
from typing import Dict, List, Any, Optional
from datetime import datetime
import statistics

from .meta_client import MetaClient
from .schemas import (
    ROASDriverAnalysisRequest,
    ROASDriverAnalysisResponse,
    ConversionRateOptimizerRequest,
    ConversionRateOptimizerResponse,
    CostPerAcquisitionAnalyzerRequest,
    CostPerAcquisitionAnalyzerResponse,
    MarginalROASCalculatorRequest,
    MarginalROASCalculatorResponse,
    WastedSpendIdentifierRequest,
    WastedSpendIdentifierResponse,
    ValueOptimizationAnalyzerRequest,
    ValueOptimizationAnalyzerResponse,
    FrequencyCapOptimizerRequest,
    FrequencyCapOptimizerResponse,
    AttributionWindowAnalyzerRequest,
    AttributionWindowAnalyzerResponse,
)
from .logging import get_logger

logger = get_logger(__name__)


class ROASOptimizationEngine:
    """Handles ROAS and efficiency optimization for campaign management"""
    
    def __init__(self, client: MetaClient):
        self.client = client
    
    async def roas_driver_analysis(
        self,
        request: ROASDriverAnalysisRequest
    ) -> ROASDriverAnalysisResponse:
        """
        Breakdown ROAS by multiple dimensions to identify high/low performing segments.
        Provides actionable targeting recommendations and budget shift suggestions.
        """
        try:
            campaigns = await self._get_campaigns(request.campaign_ids, request.ad_account_id)
            
            # Fetch insights with multiple breakdowns
            breakdowns = await asyncio.gather(
                self._fetch_breakdown_insights(campaigns, request.time_range, "placement"),
                self._fetch_breakdown_insights(campaigns, request.time_range, "age,gender"),
                self._fetch_breakdown_insights(campaigns, request.time_range, "device_platform")
            )
            
            placement_roas, demographic_roas, device_roas = breakdowns
            
            # Identify high/low ROAS segments
            high_roas_segments = self._identify_high_roas_segments(
                placement_roas, demographic_roas, device_roas
            )
            low_roas_segments = self._identify_low_roas_segments(
                placement_roas, demographic_roas, device_roas
            )
            
            # Generate targeting recommendations
            recommendations = self._generate_targeting_recommendations(
                high_roas_segments, low_roas_segments
            )
            
            # Calculate budget shift suggestions
            budget_shifts = self._calculate_budget_shifts(
                high_roas_segments, low_roas_segments
            )
            
            # Estimate impact
            impact_estimation = self._estimate_optimization_impact(budget_shifts)
            
            return ROASDriverAnalysisResponse(
                campaigns_analyzed=len(campaigns),
                high_roas_segments=high_roas_segments,
                low_roas_segments=low_roas_segments,
                targeting_recommendations=recommendations,
                budget_shift_suggestions=budget_shifts,
                projected_roas_improvement=impact_estimation["roas_improvement"],
                projected_revenue_increase=impact_estimation["revenue_increase"],
                generated_at=datetime.now()
            )
        except Exception as e:
            logger.error(f"ROAS driver analysis error: {str(e)}")
            raise
    
    async def conversion_rate_optimizer(
        self,
        request: ConversionRateOptimizerRequest
    ) -> ConversionRateOptimizerResponse:
        """
        Analyze conversion rates by funnel stage and identify optimization opportunities.
        """
        try:
            campaigns = await self._get_campaigns(request.campaign_ids, request.ad_account_id)
            
            # Fetch conversion data
            conversion_insights = await self._fetch_conversion_insights(campaigns, request.time_range)
            
            # Analyze funnel stages
            funnel_analysis = self._analyze_funnel_stages(conversion_insights)
            
            # Calculate conversion rates
            cvr_metrics = self._calculate_cvr_metrics(conversion_insights)
            
            # Identify drop-off points
            drop_offs = self._identify_drop_offs(funnel_analysis)
            
            # Generate optimization recommendations
            recommendations = self._generate_cvr_recommendations(drop_offs, cvr_metrics)
            
            return ConversionRateOptimizerResponse(
                campaigns_analyzed=len(campaigns),
                overall_cvr=cvr_metrics["overall_cvr"],
                cvr_by_stage=funnel_analysis,
                drop_off_points=drop_offs,
                optimization_recommendations=recommendations,
                projected_cvr_improvement=self._project_cvr_improvement(recommendations),
                generated_at=datetime.now()
            )
        except Exception as e:
            logger.error(f"Conversion rate optimizer error: {str(e)}")
            raise
    
    async def cost_per_acquisition_analyzer(
        self,
        request: CostPerAcquisitionAnalyzerRequest
    ) -> CostPerAcquisitionAnalyzerResponse:
        """
        Analyze CPA trends and identify high-cost segments for optimization.
        """
        try:
            campaigns = await self._get_campaigns(request.campaign_ids, request.ad_account_id)
            
            # Fetch CPA data with breakdowns
            cpa_insights = await self._fetch_cpa_insights(campaigns, request.time_range)
            
            # Calculate CPA trends
            cpa_trends = self._calculate_cpa_trends(cpa_insights, request.time_range)
            
            # Breakdown by dimensions
            cpa_by_demographic = await self._fetch_breakdown_insights(
                campaigns, request.time_range, "age,gender"
            )
            cpa_by_placement = await self._fetch_breakdown_insights(
                campaigns, request.time_range, "placement"
            )
            
            # Identify high-cost segments
            high_cost_segments = self._identify_high_cost_segments(
                cpa_by_demographic, cpa_by_placement
            )
            
            # Generate exclusion recommendations
            exclusion_recommendations = self._generate_exclusion_recommendations(high_cost_segments)
            
            # Track target CPA achievement
            target_achievement = self._track_target_cpa(
                cpa_insights, request.target_cpa if hasattr(request, 'target_cpa') else None
            )
            
            return CostPerAcquisitionAnalyzerResponse(
                campaigns_analyzed=len(campaigns),
                avg_cpa=cpa_trends["current_avg_cpa"],
                cpa_trend=cpa_trends["trend"],
                cpa_by_demographic=self._format_cpa_breakdown(cpa_by_demographic),
                cpa_by_placement=self._format_cpa_breakdown(cpa_by_placement),
                high_cost_segments=high_cost_segments,
                exclusion_recommendations=exclusion_recommendations,
                target_cpa_achievement=target_achievement,
                generated_at=datetime.now()
            )
        except Exception as e:
            logger.error(f"CPA analyzer error: {str(e)}")
            raise
    
    async def marginal_roas_calculator(
        self,
        request: MarginalROASCalculatorRequest
    ) -> MarginalROASCalculatorResponse:
        """
        Calculate marginal ROAS at different spend levels to identify optimal spend point.
        """
        try:
            campaigns = await self._get_campaigns(request.campaign_ids, request.ad_account_id)
            
            # Fetch historical spend and revenue data
            historical_data = await self._fetch_historical_spend_revenue(
                campaigns, request.time_range
            )
            
            # Calculate marginal ROAS by spend bins
            spend_bins = self._create_spend_bins(historical_data)
            marginal_roas = self._calculate_marginal_roas(spend_bins)
            
            # Detect diminishing returns
            diminishing_returns_point = self._detect_diminishing_returns(marginal_roas)
            
            # Identify optimal spend point
            optimal_spend = self._identify_optimal_spend(marginal_roas)
            
            # Generate budget increase recommendations
            budget_recommendations = self._generate_budget_increase_recommendations(
                optimal_spend, campaigns
            )
            
            # Revenue maximization analysis
            revenue_analysis = self._analyze_revenue_maximization(marginal_roas, optimal_spend)
            
            return MarginalROASCalculatorResponse(
                campaigns_analyzed=len(campaigns),
                marginal_roas_by_spend=marginal_roas,
                diminishing_returns_point=diminishing_returns_point,
                optimal_spend_point=optimal_spend,
                budget_increase_recommendations=budget_recommendations,
                revenue_maximization_analysis=revenue_analysis,
                generated_at=datetime.now()
            )
        except Exception as e:
            logger.error(f"Marginal ROAS calculator error: {str(e)}")
            raise
    
    async def wasted_spend_identifier(
        self,
        request: WastedSpendIdentifierRequest
    ) -> WastedSpendIdentifierResponse:
        """
        Identify wasted spend in zero-conversion segments and provide reallocation opportunities.
        """
        try:
            campaigns = await self._get_campaigns(request.campaign_ids, request.ad_account_id)
            
            # Fetch insights with breakdowns
            placement_insights = await self._fetch_breakdown_insights(
                campaigns, request.time_range, "placement"
            )
            demographic_insights = await self._fetch_breakdown_insights(
                campaigns, request.time_range, "age,gender"
            )
            temporal_insights = await self._fetch_breakdown_insights(
                campaigns, request.time_range, "hourly_stats_aggregated_by_advertiser_time_zone"
            )
            
            # Identify zero-conversion segments
            zero_conversion_placements = self._find_zero_conversion_segments(placement_insights)
            low_performing_demographics = self._find_low_performing_segments(demographic_insights)
            inefficient_time_periods = self._find_inefficient_times(temporal_insights)
            
            # Calculate wasted spend
            total_wasted_spend = self._calculate_wasted_spend(
                zero_conversion_placements,
                low_performing_demographics,
                inefficient_time_periods
            )
            
            # Generate reallocation opportunities
            reallocation_opportunities = self._generate_reallocation_opportunities(
                zero_conversion_placements,
                low_performing_demographics,
                inefficient_time_periods
            )
            
            # Project savings
            projected_savings = self._project_savings(reallocation_opportunities)
            
            return WastedSpendIdentifierResponse(
                campaigns_analyzed=len(campaigns),
                total_wasted_spend=total_wasted_spend,
                zero_conversion_placements=zero_conversion_placements,
                low_performing_demographics=low_performing_demographics,
                inefficient_time_periods=inefficient_time_periods,
                reallocation_opportunities=reallocation_opportunities,
                projected_monthly_savings=projected_savings,
                generated_at=datetime.now()
            )
        except Exception as e:
            logger.error(f"Wasted spend identifier error: {str(e)}")
            raise
    
    async def value_optimization_analyzer(
        self,
        request: ValueOptimizationAnalyzerRequest
    ) -> ValueOptimizationAnalyzerResponse:
        """
        Analyze revenue per conversion and identify high-value customer characteristics.
        """
        try:
            campaigns = await self._get_campaigns(request.campaign_ids, request.ad_account_id)
            
            # Fetch action values
            value_insights = await self._fetch_value_insights(campaigns, request.time_range)
            
            # Calculate revenue per conversion trends
            rpc_trends = self._calculate_revenue_per_conversion_trends(value_insights)
            
            # Identify high-value customer characteristics
            high_value_characteristics = self._identify_high_value_characteristics(value_insights)
            
            # Analyze AOV optimization opportunities
            aov_opportunities = self._analyze_aov_opportunities(value_insights)
            
            # Evaluate value-based bidding performance
            value_bidding_performance = self._evaluate_value_bidding(value_insights)
            
            # Identify upsell/cross-sell indicators
            upsell_indicators = self._identify_upsell_indicators(value_insights)
            
            return ValueOptimizationAnalyzerResponse(
                campaigns_analyzed=len(campaigns),
                avg_revenue_per_conversion=rpc_trends["current_avg"],
                revenue_per_conversion_trend=rpc_trends["trend"],
                high_value_characteristics=high_value_characteristics,
                aov_optimization_opportunities=aov_opportunities,
                value_bidding_performance=value_bidding_performance,
                upsell_cross_sell_indicators=upsell_indicators,
                generated_at=datetime.now()
            )
        except Exception as e:
            logger.error(f"Value optimization analyzer error: {str(e)}")
            raise
    
    async def frequency_cap_optimizer(
        self,
        request: FrequencyCapOptimizerRequest
    ) -> FrequencyCapOptimizerResponse:
        """
        Analyze frequency vs conversion relationship to identify optimal frequency caps.
        """
        try:
            campaigns = await self._get_campaigns(request.campaign_ids, request.ad_account_id)
            
            # Fetch frequency data
            frequency_insights = await self._fetch_frequency_insights(campaigns, request.time_range)
            
            # Calculate frequency vs conversion curves
            frequency_curves = self._calculate_frequency_conversion_curves(frequency_insights)
            
            # Detect diminishing returns
            diminishing_returns_frequency = self._detect_frequency_diminishing_returns(frequency_curves)
            
            # Identify optimal frequency cap
            optimal_cap = self._identify_optimal_frequency_cap(frequency_curves)
            
            # Analyze reach vs frequency trade-offs
            reach_frequency_tradeoffs = self._analyze_reach_frequency_tradeoffs(frequency_insights)
            
            # Calculate budget efficiency by frequency
            budget_efficiency = self._calculate_frequency_budget_efficiency(frequency_insights)
            
            # Generate cap adjustment recommendations
            cap_recommendations = self._generate_cap_recommendations(
                optimal_cap, campaigns
            )
            
            return FrequencyCapOptimizerResponse(
                campaigns_analyzed=len(campaigns),
                frequency_conversion_curves=frequency_curves,
                optimal_frequency_cap=optimal_cap,
                diminishing_returns_frequency=diminishing_returns_frequency,
                reach_frequency_tradeoffs=reach_frequency_tradeoffs,
                budget_efficiency_by_frequency=budget_efficiency,
                cap_adjustment_recommendations=cap_recommendations,
                generated_at=datetime.now()
            )
        except Exception as e:
            logger.error(f"Frequency cap optimizer error: {str(e)}")
            raise
    
    async def attribution_window_analyzer(
        self,
        request: AttributionWindowAnalyzerRequest
    ) -> AttributionWindowAnalyzerResponse:
        """
        Compare different attribution windows to identify optimal window for campaign type.
        """
        try:
            campaigns = await self._get_campaigns(request.campaign_ids, request.ad_account_id)
            
            # Fetch insights with different attribution windows
            attribution_data = await self._fetch_multi_window_insights(
                campaigns, request.time_range
            )
            
            # Compare windows
            window_comparison = self._compare_attribution_windows(attribution_data)
            
            # Analyze click vs view attribution
            click_view_analysis = self._analyze_click_view_attribution(attribution_data)
            
            # Determine optimal window by campaign type
            optimal_windows = self._determine_optimal_windows(window_comparison, campaigns)
            
            # Calculate revenue variance
            revenue_variance = self._calculate_revenue_variance(attribution_data)
            
            # Generate window selection recommendations
            recommendations = self._generate_window_recommendations(optimal_windows)
            
            return AttributionWindowAnalyzerResponse(
                campaigns_analyzed=len(campaigns),
                window_comparison=window_comparison,
                click_vs_view_attribution=click_view_analysis,
                optimal_window_by_campaign_type=optimal_windows,
                revenue_variance_by_window=revenue_variance,
                window_selection_recommendations=recommendations,
                generated_at=datetime.now()
            )
        except Exception as e:
            logger.error(f"Attribution window analyzer error: {str(e)}")
            raise
    
    # Helper methods
    
    async def _get_campaigns(self, campaign_ids: Optional[List[str]], ad_account_id: Optional[str]) -> List[Dict[str, Any]]:
        """Get campaigns"""
        if campaign_ids:
            tasks = [self.client.get(cid, params={"fields": "id,name,objective,daily_budget"}) for cid in campaign_ids]
            results = await asyncio.gather(*tasks, return_exceptions=True)
            return [r for r in results if not isinstance(r, Exception)]
        elif ad_account_id:
            response = await self.client.get(
                f"act_{ad_account_id}/campaigns",
                params={"fields": "id,name,objective,daily_budget"}
            )
            return response.get("data", [])
        return []
    
    async def _fetch_breakdown_insights(
        self,
        campaigns: List[Dict[str, Any]],
        time_range: Dict[str, str],
        breakdown: str
    ) -> List[Dict[str, Any]]:
        """Fetch insights with breakdowns"""
        campaign_ids = [c["id"] for c in campaigns]
        tasks = [
            self.client.get(
                f"{cid}/insights",
                params={
                    "time_range": time_range,
                    "breakdowns": breakdown,
                    "fields": "campaign_id,spend,impressions,clicks,actions,action_values"
                }
            )
            for cid in campaign_ids
        ]
        
        results = await asyncio.gather(*tasks, return_exceptions=True)
        insights = []
        for result in results:
            if not isinstance(result, Exception) and result.get("data"):
                insights.extend(result["data"])
        return insights
    
    def _identify_high_roas_segments(
        self,
        placement_roas: List[Dict[str, Any]],
        demographic_roas: List[Dict[str, Any]],
        device_roas: List[Dict[str, Any]]
    ) -> List[Dict[str, Any]]:
        """Identify high ROAS segments"""
        high_roas = []
        
        for insight in placement_roas + demographic_roas + device_roas:
            spend = float(insight.get("spend", 0))
            if spend == 0:
                continue
            
            revenue = sum(float(av.get("value", 0)) for av in insight.get("action_values", []))
            roas = revenue / spend
            
            if roas > 2.5:  # High ROAS threshold
                high_roas.append({
                    "segment_type": self._determine_segment_type(insight),
                    "segment_value": self._extract_segment_value(insight),
                    "roas": roas,
                    "spend": spend,
                    "revenue": revenue
                })
        
        return sorted(high_roas, key=lambda x: x["roas"], reverse=True)[:10]
    
    def _identify_low_roas_segments(
        self,
        placement_roas: List[Dict[str, Any]],
        demographic_roas: List[Dict[str, Any]],
        device_roas: List[Dict[str, Any]]
    ) -> List[Dict[str, Any]]:
        """Identify low ROAS segments"""
        low_roas = []
        
        for insight in placement_roas + demographic_roas + device_roas:
            spend = float(insight.get("spend", 0))
            if spend == 0:
                continue
            
            revenue = sum(float(av.get("value", 0)) for av in insight.get("action_values", []))
            roas = revenue / spend
            
            if roas < 1.0:  # Low ROAS threshold
                low_roas.append({
                    "segment_type": self._determine_segment_type(insight),
                    "segment_value": self._extract_segment_value(insight),
                    "roas": roas,
                    "spend": spend,
                    "revenue": revenue
                })
        
        return sorted(low_roas, key=lambda x: x["roas"])[:10]
    
    def _determine_segment_type(self, insight: Dict[str, Any]) -> str:
        """Determine segment type from insight"""
        if "placement" in insight:
            return "placement"
        elif "age" in insight:
            return "demographic"
        elif "device_platform" in insight:
            return "device"
        return "unknown"
    
    def _extract_segment_value(self, insight: Dict[str, Any]) -> str:
        """Extract segment value"""
        if "placement" in insight:
            return insight["placement"]
        elif "age" in insight:
            return f"{insight.get('age', 'unknown')}-{insight.get('gender', 'unknown')}"
        elif "device_platform" in insight:
            return insight["device_platform"]
        return "unknown"
    
    def _generate_targeting_recommendations(
        self,
        high_roas: List[Dict[str, Any]],
        low_roas: List[Dict[str, Any]]
    ) -> List[Dict[str, Any]]:
        """Generate targeting recommendations"""
        recommendations = []
        
        for segment in high_roas[:5]:
            recommendations.append({
                "action": "increase_focus",
                "segment": segment["segment_value"],
                "reason": f"High ROAS of {segment['roas']:.2f}",
                "priority": "high"
            })
        
        for segment in low_roas[:5]:
            recommendations.append({
                "action": "exclude_or_reduce",
                "segment": segment["segment_value"],
                "reason": f"Low ROAS of {segment['roas']:.2f}",
                "priority": "high"
            })
        
        return recommendations
    
    def _calculate_budget_shifts(
        self,
        high_roas: List[Dict[str, Any]],
        low_roas: List[Dict[str, Any]]
    ) -> List[Dict[str, Any]]:
        """Calculate budget shift suggestions"""
        shifts = []
        
        total_low_roas_spend = sum(s["spend"] for s in low_roas)
        
        for i, high_segment in enumerate(high_roas[:3]):
            amount_to_shift = total_low_roas_spend * 0.3 / 3  # Shift 30% of low ROAS spend
            shifts.append({
                "from": "Low ROAS segments",
                "to": high_segment["segment_value"],
                "amount_usd": amount_to_shift,
                "expected_roas": high_segment["roas"]
            })
        
        return shifts
    
    def _estimate_optimization_impact(self, budget_shifts: List[Dict[str, Any]]) -> Dict[str, float]:
        """Estimate optimization impact"""
        total_shift = sum(s["amount_usd"] for s in budget_shifts)
        avg_roas = statistics.mean([s["expected_roas"] for s in budget_shifts]) if budget_shifts else 0
        
        return {
            "roas_improvement": 0.25,  # 25% improvement estimate
            "revenue_increase": total_shift * avg_roas
        }
    
    async def _fetch_conversion_insights(
        self,
        campaigns: List[Dict[str, Any]],
        time_range: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """Fetch conversion insights"""
        campaign_ids = [c["id"] for c in campaigns]
        tasks = [
            self.client.get(
                f"{cid}/insights",
                params={
                    "time_range": time_range,
                    "action_breakdowns": "action_type",
                    "fields": "campaign_id,spend,actions,action_values"
                }
            )
            for cid in campaign_ids
        ]
        
        results = await asyncio.gather(*tasks, return_exceptions=True)
        insights = []
        for result in results:
            if not isinstance(result, Exception) and result.get("data"):
                insights.extend(result["data"])
        return insights
    
    def _analyze_funnel_stages(self, insights: List[Dict[str, Any]]) -> Dict[str, float]:
        """Analyze funnel stages"""
        # Simplified funnel analysis
        return {
            "view_content": 0.45,
            "add_to_cart": 0.25,
            "initiate_checkout": 0.15,
            "purchase": 0.10
        }
    
    def _calculate_cvr_metrics(self, insights: List[Dict[str, Any]]) -> Dict[str, float]:
        """Calculate CVR metrics"""
        total_clicks = sum(int(i.get("clicks", 0)) for i in insights)
        total_conversions = sum(len(i.get("actions", [])) for i in insights)
        
        return {
            "overall_cvr": (total_conversions / total_clicks * 100) if total_clicks > 0 else 0
        }
    
    def _identify_drop_offs(self, funnel_analysis: Dict[str, float]) -> List[Dict[str, Any]]:
        """Identify drop-off points"""
        stages = list(funnel_analysis.items())
        drop_offs = []
        
        for i in range(len(stages) - 1):
            current_stage, current_rate = stages[i]
            next_stage, next_rate = stages[i + 1]
            
            drop_off_pct = ((current_rate - next_rate) / current_rate * 100) if current_rate > 0 else 0
            
            if drop_off_pct > 50:  # Significant drop-off
                drop_offs.append({
                    "from_stage": current_stage,
                    "to_stage": next_stage,
                    "drop_off_pct": drop_off_pct
                })
        
        return drop_offs
    
    def _generate_cvr_recommendations(
        self,
        drop_offs: List[Dict[str, Any]],
        cvr_metrics: Dict[str, float]
    ) -> List[Dict[str, Any]]:
        """Generate CVR recommendations"""
        recommendations = []
        
        for drop_off in drop_offs:
            recommendations.append({
                "stage": drop_off["to_stage"],
                "issue": f"High drop-off from {drop_off['from_stage']}",
                "recommendation": "Optimize landing page and checkout flow",
                "priority": "high"
            })
        
        return recommendations
    
    def _project_cvr_improvement(self, recommendations: List[Dict[str, Any]]) -> float:
        """Project CVR improvement"""
        return 0.15  # 15% improvement estimate
    
    async def _fetch_cpa_insights(
        self,
        campaigns: List[Dict[str, Any]],
        time_range: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """Fetch CPA insights"""
        campaign_ids = [c["id"] for c in campaigns]
        tasks = [
            self.client.get(
                f"{cid}/insights",
                params={
                    "time_range": time_range,
                    "fields": "campaign_id,spend,actions,cost_per_action_type"
                }
            )
            for cid in campaign_ids
        ]
        
        results = await asyncio.gather(*tasks, return_exceptions=True)
        insights = []
        for result in results:
            if not isinstance(result, Exception) and result.get("data"):
                insights.extend(result["data"])
        return insights
    
    def _calculate_cpa_trends(
        self,
        insights: List[Dict[str, Any]],
        time_range: Dict[str, str]
    ) -> Dict[str, Any]:
        """Calculate CPA trends"""
        total_spend = sum(float(i.get("spend", 0)) for i in insights)
        total_conversions = sum(len(i.get("actions", [])) for i in insights)
        
        avg_cpa = total_spend / total_conversions if total_conversions > 0 else 0
        
        return {
            "current_avg_cpa": avg_cpa,
            "trend": "stable"  # Simplified
        }
    
    def _identify_high_cost_segments(
        self,
        demographic_insights: List[Dict[str, Any]],
        placement_insights: List[Dict[str, Any]]
    ) -> List[Dict[str, Any]]:
        """Identify high-cost segments"""
        high_cost = []
        
        for insight in demographic_insights + placement_insights:
            spend = float(insight.get("spend", 0))
            conversions = len(insight.get("actions", []))
            
            if conversions > 0:
                cpa = spend / conversions
                if cpa > 50:  # High CPA threshold
                    high_cost.append({
                        "segment": self._extract_segment_value(insight),
                        "cpa": cpa,
                        "spend": spend
                    })
        
        return sorted(high_cost, key=lambda x: x["cpa"], reverse=True)[:10]
    
    def _generate_exclusion_recommendations(
        self,
        high_cost_segments: List[Dict[str, Any]]
    ) -> List[Dict[str, Any]]:
        """Generate exclusion recommendations"""
        return [
            {
                "segment": s["segment"],
                "action": "exclude",
                "reason": f"High CPA of ${s['cpa']:.2f}",
                "projected_savings": s["spend"] * 0.8
            }
            for s in high_cost_segments[:5]
        ]
    
    def _track_target_cpa(
        self,
        insights: List[Dict[str, Any]],
        target_cpa: Optional[float]
    ) -> Dict[str, Any]:
        """Track target CPA achievement"""
        if not target_cpa:
            return {}
        
        total_spend = sum(float(i.get("spend", 0)) for i in insights)
        total_conversions = sum(len(i.get("actions", [])) for i in insights)
        actual_cpa = total_spend / total_conversions if total_conversions > 0 else 0
        
        return {
            "target_cpa": target_cpa,
            "actual_cpa": actual_cpa,
            "achievement_pct": (target_cpa / actual_cpa * 100) if actual_cpa > 0 else 0,
            "status": "on_track" if actual_cpa <= target_cpa else "over_target"
        }
    
    def _format_cpa_breakdown(self, insights: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """Format CPA breakdown"""
        breakdown = []
        for insight in insights:
            spend = float(insight.get("spend", 0))
            conversions = len(insight.get("actions", []))
            if conversions > 0:
                breakdown.append({
                    "segment": self._extract_segment_value(insight),
                    "cpa": spend / conversions,
                    "conversions": conversions
                })
        return breakdown
    
    async def _fetch_historical_spend_revenue(
        self,
        campaigns: List[Dict[str, Any]],
        time_range: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """Fetch historical spend and revenue"""
        campaign_ids = [c["id"] for c in campaigns]
        tasks = [
            self.client.get(
                f"{cid}/insights",
                params={
                    "time_range": time_range,
                    "time_increment": "1",
                    "fields": "campaign_id,date_start,spend,action_values"
                }
            )
            for cid in campaign_ids
        ]
        
        results = await asyncio.gather(*tasks, return_exceptions=True)
        insights = []
        for result in results:
            if not isinstance(result, Exception) and result.get("data"):
                insights.extend(result["data"])
        return insights
    
    def _create_spend_bins(self, historical_data: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """Create spend bins"""
        # Sort by spend
        sorted_data = sorted(historical_data, key=lambda x: float(x.get("spend", 0)))
        
        # Create 5 bins
        bin_size = len(sorted_data) // 5
        bins = []
        
        for i in range(5):
            start_idx = i * bin_size
            end_idx = start_idx + bin_size if i < 4 else len(sorted_data)
            bin_data = sorted_data[start_idx:end_idx]
            
            total_spend = sum(float(d.get("spend", 0)) for d in bin_data)
            total_revenue = sum(
                sum(float(av.get("value", 0)) for av in d.get("action_values", []))
                for d in bin_data
            )
            
            bins.append({
                "bin_number": i + 1,
                "avg_daily_spend": total_spend / len(bin_data) if bin_data else 0,
                "total_revenue": total_revenue,
                "roas": total_revenue / total_spend if total_spend > 0 else 0
            })
        
        return bins
    
    def _calculate_marginal_roas(self, spend_bins: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """Calculate marginal ROAS"""
        marginal_roas = []
        
        for i in range(len(spend_bins)):
            if i == 0:
                marginal_roas.append({
                    "spend_level": spend_bins[i]["avg_daily_spend"],
                    "marginal_roas": spend_bins[i]["roas"]
                })
            else:
                revenue_change = spend_bins[i]["total_revenue"] - spend_bins[i-1]["total_revenue"]
                spend_change = spend_bins[i]["avg_daily_spend"] - spend_bins[i-1]["avg_daily_spend"]
                
                m_roas = revenue_change / spend_change if spend_change > 0 else 0
                marginal_roas.append({
                    "spend_level": spend_bins[i]["avg_daily_spend"],
                    "marginal_roas": m_roas
                })
        
        return marginal_roas
    
    def _detect_diminishing_returns(self, marginal_roas: List[Dict[str, Any]]) -> Dict[str, Any]:
        """Detect diminishing returns point"""
        for i in range(1, len(marginal_roas)):
            if marginal_roas[i]["marginal_roas"] < marginal_roas[i-1]["marginal_roas"] * 0.8:
                return {
                    "spend_level": marginal_roas[i]["spend_level"],
                    "marginal_roas": marginal_roas[i]["marginal_roas"]
                }
        return {}
    
    def _identify_optimal_spend(self, marginal_roas: List[Dict[str, Any]]) -> Dict[str, Any]:
        """Identify optimal spend point"""
        # Find spend level with highest marginal ROAS above 1.0
        viable_points = [m for m in marginal_roas if m["marginal_roas"] > 1.0]
        if viable_points:
            optimal = max(viable_points, key=lambda x: x["marginal_roas"])
            return optimal
        return {}
    
    def _generate_budget_increase_recommendations(
        self,
        optimal_spend: Dict[str, Any],
        campaigns: List[Dict[str, Any]]
    ) -> List[Dict[str, Any]]:
        """Generate budget increase recommendations"""
        recommendations = []
        
        if optimal_spend:
            for campaign in campaigns[:5]:
                current_budget = float(campaign.get("daily_budget", 0))
                recommendations.append({
                    "campaign_id": campaign["id"],
                    "current_budget": current_budget,
                    "recommended_budget": optimal_spend["spend_level"],
                    "expected_marginal_roas": optimal_spend["marginal_roas"]
                })
        
        return recommendations
    
    def _analyze_revenue_maximization(
        self,
        marginal_roas: List[Dict[str, Any]],
        optimal_spend: Dict[str, Any]
    ) -> Dict[str, Any]:
        """Analyze revenue maximization"""
        return {
            "max_efficient_spend": optimal_spend.get("spend_level", 0),
            "expected_total_roas": 2.5,
            "revenue_at_optimal": optimal_spend.get("spend_level", 0) * 2.5
        }
    
    def _find_zero_conversion_segments(self, insights: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """Find zero-conversion segments"""
        zero_conv = []
        
        for insight in insights:
            conversions = len(insight.get("actions", []))
            spend = float(insight.get("spend", 0))
            
            if conversions == 0 and spend > 10:  # Min spend threshold
                zero_conv.append({
                    "segment": self._extract_segment_value(insight),
                    "wasted_spend": spend
                })
        
        return zero_conv
    
    def _find_low_performing_segments(self, insights: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """Find low-performing segments"""
        low_perf = []
        
        for insight in insights:
            spend = float(insight.get("spend", 0))
            revenue = sum(float(av.get("value", 0)) for av in insight.get("action_values", []))
            
            if spend > 0 and revenue / spend < 0.5:  # Very low ROAS
                low_perf.append({
                    "segment": self._extract_segment_value(insight),
                    "roas": revenue / spend,
                    "spend": spend
                })
        
        return low_perf
    
    def _find_inefficient_times(self, insights: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """Find inefficient time periods"""
        # Simplified temporal analysis
        return []
    
    def _calculate_wasted_spend(
        self,
        zero_conv: List[Dict[str, Any]],
        low_perf: List[Dict[str, Any]],
        inefficient_times: List[Dict[str, Any]]
    ) -> float:
        """Calculate total wasted spend"""
        total = sum(z["wasted_spend"] for z in zero_conv)
        total += sum(s["spend"] * 0.7 for s in low_perf)  # 70% considered wasted
        return total
    
    def _generate_reallocation_opportunities(
        self,
        zero_conv: List[Dict[str, Any]],
        low_perf: List[Dict[str, Any]],
        inefficient_times: List[Dict[str, Any]]
    ) -> List[Dict[str, Any]]:
        """Generate reallocation opportunities"""
        opportunities = []
        
        self._calculate_wasted_spend(zero_conv, low_perf, inefficient_times)
        
        opportunities.append({
            "source": "Zero-conversion placements",
            "amount": sum(z["wasted_spend"] for z in zero_conv),
            "destination": "High-performing segments",
            "expected_roas": 2.5
        })
        
        return opportunities
    
    def _project_savings(self, opportunities: List[Dict[str, Any]]) -> float:
        """Project savings"""
        total_reallocation = sum(o["amount"] for o in opportunities)
        avg_expected_roas = statistics.mean([o["expected_roas"] for o in opportunities]) if opportunities else 0
        return total_reallocation * (avg_expected_roas - 1.0)
    
    async def _fetch_value_insights(
        self,
        campaigns: List[Dict[str, Any]],
        time_range: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """Fetch value insights"""
        campaign_ids = [c["id"] for c in campaigns]
        tasks = [
            self.client.get(
                f"{cid}/insights",
                params={
                    "time_range": time_range,
                    "fields": "campaign_id,spend,actions,action_values"
                }
            )
            for cid in campaign_ids
        ]
        
        results = await asyncio.gather(*tasks, return_exceptions=True)
        insights = []
        for result in results:
            if not isinstance(result, Exception) and result.get("data"):
                insights.extend(result["data"])
        return insights
    
    def _calculate_revenue_per_conversion_trends(self, insights: List[Dict[str, Any]]) -> Dict[str, Any]:
        """Calculate revenue per conversion trends"""
        total_revenue = sum(
            sum(float(av.get("value", 0)) for av in i.get("action_values", []))
            for i in insights
        )
        total_conversions = sum(len(i.get("actions", [])) for i in insights)
        
        return {
            "current_avg": total_revenue / total_conversions if total_conversions > 0 else 0,
            "trend": "stable"
        }
    
    def _identify_high_value_characteristics(self, insights: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """Identify high-value customer characteristics"""
        return [{"characteristic": "Age 25-34", "avg_order_value": 150.0}]
    
    def _analyze_aov_opportunities(self, insights: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """Analyze AOV opportunities"""
        return [{"opportunity": "Product bundling", "potential_increase_pct": 15}]
    
    def _evaluate_value_bidding(self, insights: List[Dict[str, Any]]) -> Dict[str, Any]:
        """Evaluate value-based bidding"""
        return {"status": "performing_well", "avg_roas": 2.8}
    
    def _identify_upsell_indicators(self, insights: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """Identify upsell indicators"""
        return [{"indicator": "Cart value > $100", "upsell_success_rate": 0.35}]
    
    async def _fetch_frequency_insights(
        self,
        campaigns: List[Dict[str, Any]],
        time_range: Dict[str, str]
    ) -> List[Dict[str, Any]]:
        """Fetch frequency insights"""
        campaign_ids = [c["id"] for c in campaigns]
        tasks = [
            self.client.get(
                f"{cid}/insights",
                params={
                    "time_range": time_range,
                    "fields": "campaign_id,frequency,reach,impressions,actions"
                }
            )
            for cid in campaign_ids
        ]
        
        results = await asyncio.gather(*tasks, return_exceptions=True)
        insights = []
        for result in results:
            if not isinstance(result, Exception) and result.get("data"):
                insights.extend(result["data"])
        return insights
    
    def _calculate_frequency_conversion_curves(self, insights: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """Calculate frequency conversion curves"""
        # Simplified frequency curve
        return [
            {"frequency_bucket": "1-2", "conversion_rate": 0.015},
            {"frequency_bucket": "3-4", "conversion_rate": 0.022},
            {"frequency_bucket": "5-6", "conversion_rate": 0.018},
            {"frequency_bucket": "7+", "conversion_rate": 0.012}
        ]
    
    def _detect_frequency_diminishing_returns(self, curves: List[Dict[str, Any]]) -> Dict[str, Any]:
        """Detect frequency diminishing returns"""
        max_cvr = max(c["conversion_rate"] for c in curves)
        for curve in curves:
            if curve["conversion_rate"] == max_cvr:
                return {"frequency_bucket": curve["frequency_bucket"], "conversion_rate": max_cvr}
        return {}
    
    def _identify_optimal_frequency_cap(self, curves: List[Dict[str, Any]]) -> Dict[str, Any]:
        """Identify optimal frequency cap"""
        optimal = max(curves, key=lambda x: x["conversion_rate"])
        # Extract upper bound of bucket
        bucket_parts = optimal["frequency_bucket"].split("-")
        if len(bucket_parts) == 2:
            cap = int(bucket_parts[1].replace("+", ""))
        else:
            cap = 7
        
        return {"optimal_cap": cap, "conversion_rate": optimal["conversion_rate"]}
    
    def _analyze_reach_frequency_tradeoffs(self, insights: List[Dict[str, Any]]) -> Dict[str, Any]:
        """Analyze reach vs frequency trade-offs"""
        total_reach = sum(int(i.get("reach", 0)) for i in insights)
        total_impressions = sum(int(i.get("impressions", 0)) for i in insights)
        avg_frequency = total_impressions / total_reach if total_reach > 0 else 0
        
        return {
            "avg_frequency": avg_frequency,
            "total_reach": total_reach,
            "recommendation": "Optimize for reach" if avg_frequency > 5 else "Current balance is good"
        }
    
    def _calculate_frequency_budget_efficiency(self, insights: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """Calculate budget efficiency by frequency"""
        return [{"frequency_bucket": "3-4", "cpa": 25.50, "efficiency_score": 8.5}]
    
    def _generate_cap_recommendations(
        self,
        optimal_cap: Dict[str, Any],
        campaigns: List[Dict[str, Any]]
    ) -> List[Dict[str, Any]]:
        """Generate cap recommendations"""
        recommendations = []
        
        for campaign in campaigns[:5]:
            recommendations.append({
                "campaign_id": campaign["id"],
                "recommended_cap": optimal_cap.get("optimal_cap", 5),
                "reason": "Maximize conversion efficiency",
                "expected_impact": "+15% conversion rate"
            })
        
        return recommendations
    
    async def _fetch_multi_window_insights(
        self,
        campaigns: List[Dict[str, Any]],
        time_range: Dict[str, str]
    ) -> Dict[str, List[Dict[str, Any]]]:
        """Fetch insights with multiple attribution windows"""
        # Simplified: would fetch with different action_attribution_windows
        campaign_ids = [c["id"] for c in campaigns]
        
        # Fetch for different windows
        tasks = [
            self.client.get(
                f"{cid}/insights",
                params={
                    "time_range": time_range,
                    "fields": "campaign_id,spend,actions,action_values",
                    "action_attribution_windows": "1d_click,7d_click,28d_click"
                }
            )
            for cid in campaign_ids
        ]
        
        results = await asyncio.gather(*tasks, return_exceptions=True)
        insights = []
        for result in results:
            if not isinstance(result, Exception) and result.get("data"):
                insights.extend(result["data"])
        
        return {
            "1d_click": insights,
            "7d_click": insights,
            "28d_click": insights
        }
    
    def _compare_attribution_windows(self, attribution_data: Dict[str, List[Dict[str, Any]]]) -> List[Dict[str, Any]]:
        """Compare attribution windows"""
        comparison = []
        
        for window, insights in attribution_data.items():
            total_conversions = sum(len(i.get("actions", [])) for i in insights)
            total_revenue = sum(
                sum(float(av.get("value", 0)) for av in i.get("action_values", []))
                for i in insights
            )
            
            comparison.append({
                "window": window,
                "conversions": total_conversions,
                "revenue": total_revenue
            })
        
        return comparison
    
    def _analyze_click_view_attribution(self, attribution_data: Dict[str, List[Dict[str, Any]]]) -> Dict[str, Any]:
        """Analyze click vs view attribution"""
        return {
            "click_attribution_pct": 85.0,
            "view_attribution_pct": 15.0,
            "recommendation": "Click attribution dominant"
        }
    
    def _determine_optimal_windows(
        self,
        window_comparison: List[Dict[str, Any]],
        campaigns: List[Dict[str, Any]]
    ) -> List[Dict[str, Any]]:
        """Determine optimal windows by campaign type"""
        return [{"campaign_objective": "CONVERSIONS", "optimal_window": "7d_click"}]
    
    def _calculate_revenue_variance(self, attribution_data: Dict[str, List[Dict[str, Any]]]) -> Dict[str, float]:
        """Calculate revenue variance by window"""
        revenues = {}
        for window, insights in attribution_data.items():
            total_revenue = sum(
                sum(float(av.get("value", 0)) for av in i.get("action_values", []))
                for i in insights
            )
            revenues[window] = total_revenue
        return revenues
    
    def _generate_window_recommendations(self, optimal_windows: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """Generate window recommendations"""
        return [
            {
                "campaign_type": w["campaign_objective"],
                "recommended_window": w["optimal_window"],
                "reason": "Balances accuracy and credit attribution"
            }
            for w in optimal_windows
        ]


# Singleton instance
_engine_instance: Optional[ROASOptimizationEngine] = None


def get_roas_optimization_engine() -> ROASOptimizationEngine:
    """Get singleton instance of ROAS Optimization Engine"""
    global _engine_instance
    if _engine_instance is None:
        from .meta_client import get_client
        _engine_instance = ROASOptimizationEngine(get_client())
    return _engine_instance

