"""
Advanced Analysis Bundles for Meta Ads MCP Server

This module provides deep multi-dimensional analysis tools that combine
multiple data sources and analytical techniques for sophisticated insights.
"""

from typing import Dict, List, Any, Optional, Tuple
from datetime import datetime
from dataclasses import dataclass
import statistics
from collections import defaultdict

from .meta_client import MetaClient
from .schemas import (
    PerformanceCohortRequest,
    EfficiencyFrontierRequest,
    CannibalizationRequest,
    TimeDecayRequest,
    IncrementalityRequest,
    ContributionMarginsRequest,
    SaturationPointsRequest,
    PerformanceCohortResponse,
    EfficiencyFrontierResponse,
    CannibalizationResponse,
    TimeDecayResponse,
    IncrementalityResponse,
    ContributionMarginsResponse,
    SaturationPointsResponse,
    CohortAnalysis,
    EfficiencyPoint,
    CannibalizationAnalysis,
    DecayAnalysis,
    IncrementalityAnalysis,
    ContributionMargin,
    SaturationAnalysis,
)
from .logging import get_logger

logger = get_logger(__name__)


@dataclass
class AnalysisContext:
    """Context for analysis operations"""
    ad_account_id: str
    time_range: Dict[str, str]
    analysis_parameters: Dict[str, Any]
    data_sources: List[str]


class AdvancedAnalysisEngine:
    """Handles advanced analysis operations for Meta Ads"""
    
    def __init__(self, client: MetaClient):
        self.client = client
    
    async def analyze_performance_cohort(
        self, 
        request: PerformanceCohortRequest
    ) -> PerformanceCohortResponse:
        """Analyze performance by cohort (launch date, strategy, etc.)"""
        try:
            context = AnalysisContext(
                ad_account_id=request.ad_account_id,
                time_range=request.time_range,
                analysis_parameters=request.analysis_parameters,
                data_sources=request.data_sources
            )
            
            # Get cohort data
            cohort_data = await self._get_cohort_data(context, request.cohort_type)
            
            # Analyze cohorts
            cohort_analyses = []
            for cohort_id, campaigns in cohort_data.items():
                analysis = await self._analyze_cohort_performance(
                    cohort_id, campaigns, request.metrics
                )
                cohort_analyses.append(analysis)
            
            # Calculate cohort insights
            insights = await self._calculate_cohort_insights(cohort_analyses)
            
            return PerformanceCohortResponse(
                cohort_analyses=cohort_analyses,
                insights=insights,
                cohort_type=request.cohort_type,
                time_range=request.time_range,
                generated_at=datetime.utcnow()
            )
            
        except Exception as e:
            logger.error(f"Error analyzing performance cohort: {e}")
            raise
    
    async def analyze_efficiency_frontier(
        self, 
        request: EfficiencyFrontierRequest
    ) -> EfficiencyFrontierResponse:
        """Analyze Pareto frontier for efficiency optimization"""
        try:
            context = AnalysisContext(
                ad_account_id=request.ad_account_id,
                time_range=request.time_range,
                analysis_parameters=request.analysis_parameters,
                data_sources=request.data_sources
            )
            
            # Get performance data
            performance_data = await self._get_performance_data(context, request.object_type)
            
            # Calculate efficiency frontier
            frontier_points = await self._calculate_efficiency_frontier(
                performance_data, request.efficiency_metrics
            )
            
            # Identify optimization opportunities
            opportunities = await self._identify_frontier_opportunities(
                frontier_points, request.optimization_goals
            )
            
            return EfficiencyFrontierResponse(
                frontier_points=frontier_points,
                optimization_opportunities=opportunities,
                efficiency_metrics=request.efficiency_metrics,
                object_type=request.object_type,
                time_range=request.time_range,
                generated_at=datetime.utcnow()
            )
            
        except Exception as e:
            logger.error(f"Error analyzing efficiency frontier: {e}")
            raise
    
    async def analyze_cannibalization(
        self, 
        request: CannibalizationRequest
    ) -> CannibalizationResponse:
        """Find campaigns competing with each other"""
        try:
            context = AnalysisContext(
                ad_account_id=request.ad_account_id,
                time_range=request.time_range,
                analysis_parameters=request.analysis_parameters,
                data_sources=request.data_sources
            )
            
            # Get campaign data
            campaign_data = await self._get_campaign_data(context)
            
            # Analyze audience overlap
            overlap_analysis = await self._analyze_audience_overlap(campaign_data)
            
            # Detect cannibalization
            cannibalization_pairs = await self._detect_cannibalization(
                campaign_data, overlap_analysis, request.cannibalization_threshold
            )
            
            # Calculate impact
            impact_analysis = await self._calculate_cannibalization_impact(
                cannibalization_pairs, request.impact_metrics
            )
            
            return CannibalizationResponse(
                cannibalization_pairs=cannibalization_pairs,
                impact_analysis=impact_analysis,
                overlap_analysis=overlap_analysis,
                cannibalization_threshold=request.cannibalization_threshold,
                time_range=request.time_range,
                generated_at=datetime.utcnow()
            )
            
        except Exception as e:
            logger.error(f"Error analyzing cannibalization: {e}")
            raise
    
    async def analyze_time_decay(
        self, 
        request: TimeDecayRequest
    ) -> TimeDecayResponse:
        """Analyze performance decay over time"""
        try:
            context = AnalysisContext(
                ad_account_id=request.ad_account_id,
                time_range=request.time_range,
                analysis_parameters=request.analysis_parameters,
                data_sources=request.data_sources
            )
            
            # Get time series data
            time_series_data = await self._get_time_series_data(context, request.object_type)
            
            # Analyze decay patterns
            decay_analyses = []
            for entity_id, time_series in time_series_data.items():
                analysis = await self._analyze_entity_decay(
                    entity_id, time_series, request.decay_metrics
                )
                decay_analyses.append(analysis)
            
            # Calculate decay insights
            insights = await self._calculate_decay_insights(decay_analyses)
            
            return TimeDecayResponse(
                decay_analyses=decay_analyses,
                insights=insights,
                decay_metrics=request.decay_metrics,
                object_type=request.object_type,
                time_range=request.time_range,
                generated_at=datetime.utcnow()
            )
            
        except Exception as e:
            logger.error(f"Error analyzing time decay: {e}")
            raise
    
    async def analyze_incrementality(
        self, 
        request: IncrementalityRequest
    ) -> IncrementalityResponse:
        """Analyze true lift vs baseline performance"""
        try:
            context = AnalysisContext(
                ad_account_id=request.ad_account_id,
                time_range=request.time_range,
                analysis_parameters=request.analysis_parameters,
                data_sources=request.data_sources
            )
            
            # Get baseline and treatment data
            baseline_data = await self._get_baseline_data(context, request.baseline_period)
            treatment_data = await self._get_treatment_data(context, request.treatment_period)
            
            # Calculate incrementality
            incrementality_analyses = []
            for entity_id in baseline_data.keys():
                if entity_id in treatment_data:
                    analysis = await self._calculate_incrementality(
                        entity_id, baseline_data[entity_id], treatment_data[entity_id]
                    )
                    incrementality_analyses.append(analysis)
            
            # Calculate overall incrementality
            overall_analysis = await self._calculate_overall_incrementality(
                incrementality_analyses
            )
            
            return IncrementalityResponse(
                incrementality_analyses=incrementality_analyses,
                overall_analysis=overall_analysis,
                baseline_period=request.baseline_period,
                treatment_period=request.treatment_period,
                time_range=request.time_range,
                generated_at=datetime.utcnow()
            )
            
        except Exception as e:
            logger.error(f"Error analyzing incrementality: {e}")
            raise
    
    async def analyze_contribution_margins(
        self, 
        request: ContributionMarginsRequest
    ) -> ContributionMarginsResponse:
        """Analyze true profitability including all costs"""
        try:
            context = AnalysisContext(
                ad_account_id=request.ad_account_id,
                time_range=request.time_range,
                analysis_parameters=request.analysis_parameters,
                data_sources=request.data_sources
            )
            
            # Get performance and cost data
            performance_data = await self._get_performance_data(context, request.object_type)
            cost_data = await self._get_cost_data(context, request.cost_components)
            
            # Calculate contribution margins
            margin_analyses = []
            for entity_id in performance_data.keys():
                if entity_id in cost_data:
                    analysis = await self._calculate_contribution_margin(
                        entity_id, performance_data[entity_id], cost_data[entity_id]
                    )
                    margin_analyses.append(analysis)
            
            # Calculate profitability insights
            insights = await self._calculate_profitability_insights(margin_analyses)
            
            return ContributionMarginsResponse(
                margin_analyses=margin_analyses,
                insights=insights,
                cost_components=request.cost_components,
                object_type=request.object_type,
                time_range=request.time_range,
                generated_at=datetime.utcnow()
            )
            
        except Exception as e:
            logger.error(f"Error analyzing contribution margins: {e}")
            raise
    
    async def analyze_saturation_points(
        self, 
        request: SaturationPointsRequest
    ) -> SaturationPointsResponse:
        """Find budget saturation points for optimal allocation"""
        try:
            context = AnalysisContext(
                ad_account_id=request.ad_account_id,
                time_range=request.time_range,
                analysis_parameters=request.analysis_parameters,
                data_sources=request.data_sources
            )
            
            # Get budget and performance data
            budget_data = await self._get_budget_data(context, request.object_type)
            performance_data = await self._get_performance_data(context, request.object_type)
            
            # Analyze saturation points
            saturation_analyses = []
            for entity_id in budget_data.keys():
                if entity_id in performance_data:
                    analysis = await self._analyze_saturation_point(
                        entity_id, budget_data[entity_id], performance_data[entity_id]
                    )
                    saturation_analyses.append(analysis)
            
            # Calculate optimization recommendations
            recommendations = await self._calculate_saturation_recommendations(
                saturation_analyses, request.optimization_goals
            )
            
            return SaturationPointsResponse(
                saturation_analyses=saturation_analyses,
                recommendations=recommendations,
                optimization_goals=request.optimization_goals,
                object_type=request.object_type,
                time_range=request.time_range,
                generated_at=datetime.utcnow()
            )
            
        except Exception as e:
            logger.error(f"Error analyzing saturation points: {e}")
            raise
    
    async def _get_cohort_data(
        self, 
        context: AnalysisContext, 
        cohort_type: str
    ) -> Dict[str, List[Dict[str, Any]]]:
        """Get data grouped by cohort"""
        try:
            # Get campaigns with cohort information
            campaigns_response = await self.client.graph_request(
                method="GET",
                path=f"{context.ad_account_id}/campaigns",
                params={
                    "fields": "id,name,created_time,objective,status",
                    "limit": 1000
                }
            )
            
            campaigns = campaigns_response.get("data", [])
            
            # Group by cohort type
            cohort_groups = defaultdict(list)
            
            for campaign in campaigns:
                if cohort_type == "launch_date":
                    # Group by launch date (month)
                    created_time = campaign.get("created_time", "")
                    if created_time:
                        cohort_id = created_time[:7]  # YYYY-MM
                    else:
                        cohort_id = "unknown"
                elif cohort_type == "objective":
                    cohort_id = campaign.get("objective", "unknown")
                elif cohort_type == "strategy":
                    # This would require additional logic to determine strategy
                    cohort_id = "standard"
                else:
                    cohort_id = "default"
                
                cohort_groups[cohort_id].append(campaign)
            
            return dict(cohort_groups)
            
        except Exception as e:
            logger.error(f"Error getting cohort data: {e}")
            return {}
    
    async def _analyze_cohort_performance(
        self, 
        cohort_id: str, 
        campaigns: List[Dict[str, Any]], 
        metrics: List[str]
    ) -> CohortAnalysis:
        """Analyze performance for a specific cohort"""
        try:
            campaign_ids = [campaign["id"] for campaign in campaigns]
            
            # Get insights for this cohort
            insights_response = await self.client.graph_request(
                method="GET",
                path=f"{self.client.ad_account_id}/insights",
                params={
                    "level": "campaign",
                    "fields": ",".join(metrics),
                    "time_range": {"since": "30_days_ago", "until": "today"},
                    "filtering": [{"field": "campaign.id", "operator": "IN", "value": campaign_ids}],
                    "limit": 1000
                }
            )
            
            insights = insights_response.get("data", [])
            
            # Calculate cohort metrics
            total_spend = sum(float(data.get("spend", 0)) for data in insights)
            total_conversions = sum(int(data.get("conversions", 0)) for data in insights)
            total_impressions = sum(int(data.get("impressions", 0)) for data in insights)
            total_clicks = sum(int(data.get("clicks", 0)) for data in insights)
            
            # Calculate efficiency metrics
            roas = (total_conversions / total_spend) if total_spend > 0 else 0
            ctr = (total_clicks / total_impressions * 100) if total_impressions > 0 else 0
            cpc = (total_spend / total_clicks) if total_clicks > 0 else 0
            
            return CohortAnalysis(
                cohort_id=cohort_id,
                campaign_count=len(campaigns),
                total_spend=total_spend,
                total_conversions=total_conversions,
                total_impressions=total_impressions,
                total_clicks=total_clicks,
                roas=roas,
                ctr=ctr,
                cpc=cpc,
                performance_trend="stable"  # Would calculate actual trend
            )
            
        except Exception as e:
            logger.error(f"Error analyzing cohort performance: {e}")
            return CohortAnalysis(
                cohort_id=cohort_id,
                campaign_count=len(campaigns),
                total_spend=0,
                total_conversions=0,
                total_impressions=0,
                total_clicks=0,
                roas=0,
                ctr=0,
                cpc=0,
                performance_trend="unknown"
            )
    
    async def _calculate_efficiency_frontier(
        self, 
        performance_data: List[Dict[str, Any]], 
        efficiency_metrics: List[str]
    ) -> List[EfficiencyPoint]:
        """Calculate Pareto frontier for efficiency"""
        try:
            frontier_points = []
            
            for data in performance_data:
                # Calculate efficiency score based on metrics
                efficiency_score = self._calculate_efficiency_score(data, efficiency_metrics)
                
                # Calculate cost (spend)
                cost = float(data.get("spend", 0))
                
                # Calculate output (conversions or other metric)
                output = float(data.get("conversions", 0))
                
                if cost > 0 and output > 0:
                    frontier_points.append(EfficiencyPoint(
                        entity_id=data.get("campaign_id") or data.get("adset_id") or data.get("ad_id"),
                        entity_name=data.get("campaign_name") or data.get("adset_name") or data.get("ad_name"),
                        cost=cost,
                        output=output,
                        efficiency_score=efficiency_score,
                        is_on_frontier=False  # Will be determined later
                    ))
            
            # Sort by efficiency score and identify frontier
            frontier_points.sort(key=lambda x: x.efficiency_score, reverse=True)
            
            # Mark points on the frontier (simplified algorithm)
            for i, point in enumerate(frontier_points):
                if i < len(frontier_points) * 0.2:  # Top 20% are on frontier
                    point.is_on_frontier = True
            
            return frontier_points
            
        except Exception as e:
            logger.error(f"Error calculating efficiency frontier: {e}")
            return []
    
    async def _analyze_audience_overlap(
        self, 
        campaign_data: List[Dict[str, Any]]
    ) -> Dict[str, Any]:
        """Analyze audience overlap between campaigns"""
        try:
            # This is a simplified overlap analysis
            # In reality, this would require detailed audience data from Meta
            
            overlap_matrix = {}
            
            for i, campaign1 in enumerate(campaign_data):
                campaign1_id = campaign1.get("id")
                overlap_matrix[campaign1_id] = {}
                
                for j, campaign2 in enumerate(campaign_data):
                    if i != j:
                        campaign2_id = campaign2.get("id")
                        # Simplified overlap calculation
                        overlap_score = self._calculate_audience_overlap(campaign1, campaign2)
                        overlap_matrix[campaign1_id][campaign2_id] = overlap_score
            
            return {
                "overlap_matrix": overlap_matrix,
                "high_overlap_pairs": self._find_high_overlap_pairs(overlap_matrix, 0.7)
            }
            
        except Exception as e:
            logger.error(f"Error analyzing audience overlap: {e}")
            return {}
    
    async def _detect_cannibalization(
        self, 
        campaign_data: List[Dict[str, Any]], 
        overlap_analysis: Dict[str, Any],
        threshold: float
    ) -> List[CannibalizationAnalysis]:
        """Detect cannibalization between campaigns"""
        try:
            cannibalization_pairs = []
            
            overlap_matrix = overlap_analysis.get("overlap_matrix", {})
            
            for campaign1_id, overlaps in overlap_matrix.items():
                for campaign2_id, overlap_score in overlaps.items():
                    if overlap_score > threshold:
                        # Get performance data for both campaigns
                        campaign1_perf = await self._get_campaign_performance(campaign1_id)
                        campaign2_perf = await self._get_campaign_performance(campaign2_id)
                        
                        # Calculate cannibalization impact
                        impact = self._calculate_cannibalization_impact_score(
                            campaign1_perf, campaign2_perf, overlap_score
                        )
                        
                        cannibalization_pairs.append(CannibalizationAnalysis(
                            campaign1_id=campaign1_id,
                            campaign2_id=campaign2_id,
                            overlap_score=overlap_score,
                            impact_score=impact,
                            recommendation=self._get_cannibalization_recommendation(impact)
                        ))
            
            return cannibalization_pairs
            
        except Exception as e:
            logger.error(f"Error detecting cannibalization: {e}")
            return []
    
    async def _analyze_entity_decay(
        self, 
        entity_id: str, 
        time_series: List[Dict[str, Any]], 
        decay_metrics: List[str]
    ) -> DecayAnalysis:
        """Analyze performance decay for an entity"""
        try:
            if len(time_series) < 2:
                return DecayAnalysis(
                    entity_id=entity_id,
                    decay_rate=0,
                    decay_type="none",
                    confidence="low",
                    recommendations=[]
                )
            
            # Calculate decay rate for each metric
            decay_rates = {}
            for metric in decay_metrics:
                values = [float(data.get(metric, 0)) for data in time_series]
                if len(values) > 1:
                    decay_rate = self._calculate_decay_rate(values)
                    decay_rates[metric] = decay_rate
            
            # Determine overall decay type
            avg_decay_rate = statistics.mean(decay_rates.values()) if decay_rates else 0
            
            if avg_decay_rate > 0.1:
                decay_type = "high"
            elif avg_decay_rate > 0.05:
                decay_type = "moderate"
            elif avg_decay_rate > 0:
                decay_type = "low"
            else:
                decay_type = "none"
            
            # Generate recommendations
            recommendations = self._get_decay_recommendations(decay_type, decay_rates)
            
            return DecayAnalysis(
                entity_id=entity_id,
                decay_rate=avg_decay_rate,
                decay_type=decay_type,
                confidence="medium",  # Would calculate actual confidence
                recommendations=recommendations
            )
            
        except Exception as e:
            logger.error(f"Error analyzing entity decay: {e}")
            return DecayAnalysis(
                entity_id=entity_id,
                decay_rate=0,
                decay_type="unknown",
                confidence="low",
                recommendations=[]
            )
    
    async def _calculate_incrementality(
        self, 
        entity_id: str, 
        baseline_data: Dict[str, Any], 
        treatment_data: Dict[str, Any]
    ) -> IncrementalityAnalysis:
        """Calculate incrementality for an entity"""
        try:
            # Calculate baseline metrics
            baseline_conversions = float(baseline_data.get("conversions", 0))
            baseline_spend = float(baseline_data.get("spend", 0))
            
            # Calculate treatment metrics
            treatment_conversions = float(treatment_data.get("conversions", 0))
            treatment_spend = float(treatment_data.get("spend", 0))
            
            # Calculate incrementality
            incremental_conversions = treatment_conversions - baseline_conversions
            incremental_spend = treatment_spend - baseline_spend
            
            # Calculate incremental ROAS
            incremental_roas = (incremental_conversions / incremental_spend) if incremental_spend > 0 else 0
            
            # Calculate lift
            lift_percentage = ((treatment_conversions - baseline_conversions) / baseline_conversions * 100) if baseline_conversions > 0 else 0
            
            return IncrementalityAnalysis(
                entity_id=entity_id,
                baseline_conversions=baseline_conversions,
                treatment_conversions=treatment_conversions,
                incremental_conversions=incremental_conversions,
                incremental_spend=incremental_spend,
                incremental_roas=incremental_roas,
                lift_percentage=lift_percentage,
                confidence="medium"  # Would calculate actual confidence
            )
            
        except Exception as e:
            logger.error(f"Error calculating incrementality: {e}")
            return IncrementalityAnalysis(
                entity_id=entity_id,
                baseline_conversions=0,
                treatment_conversions=0,
                incremental_conversions=0,
                incremental_spend=0,
                incremental_roas=0,
                lift_percentage=0,
                confidence="low"
            )
    
    async def _calculate_contribution_margin(
        self, 
        entity_id: str, 
        performance_data: Dict[str, Any], 
        cost_data: Dict[str, Any]
    ) -> ContributionMargin:
        """Calculate contribution margin for an entity"""
        try:
            # Get revenue (conversion value)
            revenue = float(performance_data.get("conversion_value", 0))
            
            # Get variable costs
            ad_spend = float(performance_data.get("spend", 0))
            other_variable_costs = float(cost_data.get("other_variable_costs", 0))
            
            # Calculate contribution margin
            total_variable_costs = ad_spend + other_variable_costs
            contribution_margin = revenue - total_variable_costs
            contribution_margin_percentage = (contribution_margin / revenue * 100) if revenue > 0 else 0
            
            # Calculate profitability metrics
            roas = (revenue / ad_spend) if ad_spend > 0 else 0
            profit_margin = (contribution_margin / revenue * 100) if revenue > 0 else 0
            
            return ContributionMargin(
                entity_id=entity_id,
                revenue=revenue,
                variable_costs=total_variable_costs,
                contribution_margin=contribution_margin,
                contribution_margin_percentage=contribution_margin_percentage,
                roas=roas,
                profit_margin=profit_margin,
                is_profitable=contribution_margin > 0
            )
            
        except Exception as e:
            logger.error(f"Error calculating contribution margin: {e}")
            return ContributionMargin(
                entity_id=entity_id,
                revenue=0,
                variable_costs=0,
                contribution_margin=0,
                contribution_margin_percentage=0,
                roas=0,
                profit_margin=0,
                is_profitable=False
            )
    
    async def _analyze_saturation_point(
        self, 
        entity_id: str, 
        budget_data: Dict[str, Any], 
        performance_data: Dict[str, Any]
    ) -> SaturationAnalysis:
        """Analyze saturation point for an entity"""
        try:
            current_budget = float(budget_data.get("daily_budget", 0))
            current_spend = float(performance_data.get("spend", 0))
            current_conversions = float(performance_data.get("conversions", 0))
            
            # Calculate current efficiency
            current_efficiency = (current_conversions / current_spend) if current_spend > 0 else 0
            
            # Estimate saturation point (simplified)
            # In reality, this would use more sophisticated modeling
            saturation_budget = current_budget * 1.5  # Simplified estimate
            saturation_efficiency = current_efficiency * 0.8  # Efficiency drops at saturation
            
            # Calculate optimization potential
            optimization_potential = (saturation_budget - current_budget) * current_efficiency
            
            return SaturationAnalysis(
                entity_id=entity_id,
                current_budget=current_budget,
                saturation_budget=saturation_budget,
                current_efficiency=current_efficiency,
                saturation_efficiency=saturation_efficiency,
                optimization_potential=optimization_potential,
                saturation_level="low" if current_budget < saturation_budget * 0.7 else "high"
            )
            
        except Exception as e:
            logger.error(f"Error analyzing saturation point: {e}")
            return SaturationAnalysis(
                entity_id=entity_id,
                current_budget=0,
                saturation_budget=0,
                current_efficiency=0,
                saturation_efficiency=0,
                optimization_potential=0,
                saturation_level="unknown"
            )
    
    def _calculate_efficiency_score(
        self, 
        data: Dict[str, Any], 
        metrics: List[str]
    ) -> float:
        """Calculate efficiency score based on metrics"""
        try:
            # Simplified efficiency calculation
            conversions = float(data.get("conversions", 0))
            spend = float(data.get("spend", 0))
            
            if spend > 0:
                return conversions / spend
            return 0
            
        except Exception:
            return 0
    
    def _calculate_audience_overlap(
        self, 
        campaign1: Dict[str, Any], 
        campaign2: Dict[str, Any]
    ) -> float:
        """Calculate audience overlap between campaigns"""
        try:
            # Simplified overlap calculation
            # In reality, this would use detailed audience data
            objective1 = campaign1.get("objective", "")
            objective2 = campaign2.get("objective", "")
            
            if objective1 == objective2:
                return 0.5  # 50% overlap for same objective
            else:
                return 0.1  # 10% overlap for different objectives
                
        except Exception:
            return 0
    
    def _find_high_overlap_pairs(
        self, 
        overlap_matrix: Dict[str, Dict[str, float]], 
        threshold: float
    ) -> List[Tuple[str, str, float]]:
        """Find pairs with high audience overlap"""
        high_overlap_pairs = []
        
        for campaign1_id, overlaps in overlap_matrix.items():
            for campaign2_id, overlap_score in overlaps.items():
                if overlap_score > threshold:
                    high_overlap_pairs.append((campaign1_id, campaign2_id, overlap_score))
        
        return high_overlap_pairs
    
    async def _get_campaign_performance(self, campaign_id: str) -> Dict[str, Any]:
        """Get performance data for a campaign"""
        try:
            response = await self.client.graph_request(
                method="GET",
                path=f"{campaign_id}/insights",
                params={
                    "fields": "spend,conversions,impressions,clicks",
                    "time_range": {"since": "7_days_ago", "until": "today"}
                }
            )
            
            data = response.get("data", [])
            return data[0] if data else {}
            
        except Exception as e:
            logger.error(f"Error getting campaign performance: {e}")
            return {}
    
    def _calculate_cannibalization_impact_score(
        self, 
        campaign1_perf: Dict[str, Any], 
        campaign2_perf: Dict[str, Any], 
        overlap_score: float
    ) -> float:
        """Calculate cannibalization impact score"""
        try:
            # Simplified impact calculation
            campaign1_conversions = float(campaign1_perf.get("conversions", 0))
            campaign2_conversions = float(campaign2_perf.get("conversions", 0))
            
            # Impact is proportional to overlap and performance
            impact = overlap_score * (campaign1_conversions + campaign2_conversions) / 100
            return min(impact, 1.0)  # Cap at 1.0
            
        except Exception:
            return 0
    
    def _get_cannibalization_recommendation(self, impact_score: float) -> str:
        """Get recommendation based on cannibalization impact"""
        if impact_score > 0.7:
            return "Consider pausing one campaign or adjusting targeting"
        elif impact_score > 0.4:
            return "Monitor closely and consider audience refinement"
        else:
            return "Low impact, continue monitoring"
    
    def _calculate_decay_rate(self, values: List[float]) -> float:
        """Calculate decay rate from time series values"""
        try:
            if len(values) < 2:
                return 0
            
            # Simple linear decay calculation
            first_value = values[0]
            last_value = values[-1]
            
            if first_value > 0:
                return (first_value - last_value) / first_value
            return 0
            
        except Exception:
            return 0
    
    def _get_decay_recommendations(
        self, 
        decay_type: str, 
        decay_rates: Dict[str, float]
    ) -> List[str]:
        """Get recommendations based on decay analysis"""
        recommendations = []
        
        if decay_type == "high":
            recommendations.extend([
                "Consider creative refresh",
                "Review targeting strategy",
                "Analyze competitive landscape"
            ])
        elif decay_type == "moderate":
            recommendations.extend([
                "Monitor performance closely",
                "Consider minor optimizations"
            ])
        elif decay_type == "low":
            recommendations.append("Continue current strategy")
        
        return recommendations
    
    async def _get_performance_data(
        self, 
        context: AnalysisContext, 
        object_type: str
    ) -> List[Dict[str, Any]]:
        """Get performance data for analysis"""
        try:
            path = f"{context.ad_account_id}/insights"
            params = {
                "level": object_type,
                "fields": "spend,conversions,impressions,clicks,ctr,cpc,roas",
                "time_range": context.time_range,
                "limit": 1000
            }
            
            response = await self.client.graph_request(
                method="GET",
                path=path,
                params=params
            )
            
            return response.get("data", [])
            
        except Exception as e:
            logger.error(f"Error getting performance data: {e}")
            return []
    
    async def _get_campaign_data(self, context: AnalysisContext) -> List[Dict[str, Any]]:
        """Get campaign data for analysis"""
        try:
            response = await self.client.graph_request(
                method="GET",
                path=f"{context.ad_account_id}/campaigns",
                params={
                    "fields": "id,name,objective,status,created_time",
                    "limit": 1000
                }
            )
            
            return response.get("data", [])
            
        except Exception as e:
            logger.error(f"Error getting campaign data: {e}")
            return []
    
    async def _get_time_series_data(
        self, 
        context: AnalysisContext, 
        object_type: str
    ) -> Dict[str, List[Dict[str, Any]]]:
        """Get time series data for decay analysis"""
        try:
            # Get daily insights for the time range
            path = f"{context.ad_account_id}/insights"
            params = {
                "level": object_type,
                "fields": "spend,conversions,impressions,clicks",
                "time_range": context.time_range,
                "breakdowns": ["date"],
                "limit": 1000
            }
            
            response = await self.client.graph_request(
                method="GET",
                path=path,
                params=params
            )
            
            data = response.get("data", [])
            
            # Group by entity
            time_series_data = defaultdict(list)
            for item in data:
                entity_id = item.get("campaign_id") or item.get("adset_id") or item.get("ad_id")
                time_series_data[entity_id].append(item)
            
            return dict(time_series_data)
            
        except Exception as e:
            logger.error(f"Error getting time series data: {e}")
            return {}
    
    async def _get_baseline_data(
        self, 
        context: AnalysisContext, 
        baseline_period: Dict[str, str]
    ) -> Dict[str, Dict[str, Any]]:
        """Get baseline data for incrementality analysis"""
        try:
            path = f"{context.ad_account_id}/insights"
            params = {
                "level": "campaign",
                "fields": "spend,conversions,impressions,clicks",
                "time_range": baseline_period,
                "limit": 1000
            }
            
            response = await self.client.graph_request(
                method="GET",
                path=path,
                params=params
            )
            
            data = response.get("data", [])
            
            # Group by campaign ID
            baseline_data = {}
            for item in data:
                campaign_id = item.get("campaign_id")
                if campaign_id:
                    baseline_data[campaign_id] = item
            
            return baseline_data
            
        except Exception as e:
            logger.error(f"Error getting baseline data: {e}")
            return {}
    
    async def _get_treatment_data(
        self, 
        context: AnalysisContext, 
        treatment_period: Dict[str, str]
    ) -> Dict[str, Dict[str, Any]]:
        """Get treatment data for incrementality analysis"""
        try:
            path = f"{context.ad_account_id}/insights"
            params = {
                "level": "campaign",
                "fields": "spend,conversions,impressions,clicks",
                "time_range": treatment_period,
                "limit": 1000
            }
            
            response = await self.client.graph_request(
                method="GET",
                path=path,
                params=params
            )
            
            data = response.get("data", [])
            
            # Group by campaign ID
            treatment_data = {}
            for item in data:
                campaign_id = item.get("campaign_id")
                if campaign_id:
                    treatment_data[campaign_id] = item
            
            return treatment_data
            
        except Exception as e:
            logger.error(f"Error getting treatment data: {e}")
            return {}
    
    async def _get_cost_data(
        self, 
        context: AnalysisContext, 
        cost_components: List[str]
    ) -> Dict[str, Dict[str, Any]]:
        """Get cost data for contribution margin analysis"""
        try:
            # Simplified cost data - in reality would get from various sources
            cost_data = {}
            
            # Get campaign data
            campaigns_response = await self.client.graph_request(
                method="GET",
                path=f"{context.ad_account_id}/campaigns",
                params={"fields": "id", "limit": 1000}
            )
            
            campaigns = campaigns_response.get("data", [])
            
            for campaign in campaigns:
                campaign_id = campaign["id"]
                cost_data[campaign_id] = {
                    "other_variable_costs": 0,  # Would calculate from actual data
                    "fixed_costs": 0,
                    "total_costs": 0
                }
            
            return cost_data
            
        except Exception as e:
            logger.error(f"Error getting cost data: {e}")
            return {}
    
    async def _get_budget_data(
        self, 
        context: AnalysisContext, 
        object_type: str
    ) -> Dict[str, Dict[str, Any]]:
        """Get budget data for saturation analysis"""
        try:
            if object_type == "campaign":
                path = f"{context.ad_account_id}/campaigns"
                params = {"fields": "id,daily_budget,lifetime_budget", "limit": 1000}
            elif object_type == "adset":
                path = f"{context.ad_account_id}/adsets"
                params = {"fields": "id,daily_budget,lifetime_budget", "limit": 1000}
            else:
                return {}
            
            response = await self.client.graph_request(
                method="GET",
                path=path,
                params=params
            )
            
            data = response.get("data", [])
            
            # Group by ID
            budget_data = {}
            for item in data:
                entity_id = item["id"]
                budget_data[entity_id] = item
            
            return budget_data
            
        except Exception as e:
            logger.error(f"Error getting budget data: {e}")
            return {}
    
    async def _calculate_cohort_insights(
        self, 
        cohort_analyses: List[CohortAnalysis]
    ) -> Dict[str, Any]:
        """Calculate insights from cohort analyses"""
        try:
            if not cohort_analyses:
                return {}
            
            # Calculate aggregate metrics
            total_campaigns = sum(analysis.campaign_count for analysis in cohort_analyses)
            total_spend = sum(analysis.total_spend for analysis in cohort_analyses)
            total_conversions = sum(analysis.total_conversions for analysis in cohort_analyses)
            
            # Find best and worst performing cohorts
            best_cohort = max(cohort_analyses, key=lambda x: x.roas)
            worst_cohort = min(cohort_analyses, key=lambda x: x.roas)
            
            return {
                "total_campaigns": total_campaigns,
                "total_spend": total_spend,
                "total_conversions": total_conversions,
                "best_performing_cohort": best_cohort.cohort_id,
                "worst_performing_cohort": worst_cohort.cohort_id,
                "cohort_count": len(cohort_analyses)
            }
            
        except Exception as e:
            logger.error(f"Error calculating cohort insights: {e}")
            return {}
    
    async def _identify_frontier_opportunities(
        self, 
        frontier_points: List[EfficiencyPoint], 
        optimization_goals: List[str]
    ) -> List[Dict[str, Any]]:
        """Identify optimization opportunities from frontier analysis"""
        try:
            opportunities = []
            
            # Find entities not on frontier with potential
            non_frontier_points = [p for p in frontier_points if not p.is_on_frontier]
            
            for point in non_frontier_points:
                if point.efficiency_score > 0:
                    opportunities.append({
                        "entity_id": point.entity_id,
                        "entity_name": point.entity_name,
                        "current_efficiency": point.efficiency_score,
                        "optimization_potential": "medium",  # Would calculate actual potential
                        "recommended_actions": ["Optimize targeting", "Improve creative"]
                    })
            
            return opportunities
            
        except Exception as e:
            logger.error(f"Error identifying frontier opportunities: {e}")
            return []
    
    async def _calculate_cannibalization_impact(
        self, 
        cannibalization_pairs: List[CannibalizationAnalysis], 
        impact_metrics: List[str]
    ) -> Dict[str, Any]:
        """Calculate overall cannibalization impact"""
        try:
            if not cannibalization_pairs:
                return {"total_impact": 0, "affected_campaigns": 0}
            
            total_impact = sum(pair.impact_score for pair in cannibalization_pairs)
            affected_campaigns = len(set(
                pair.campaign1_id for pair in cannibalization_pairs
            ).union(set(pair.campaign2_id for pair in cannibalization_pairs)))
            
            return {
                "total_impact": total_impact,
                "affected_campaigns": affected_campaigns,
                "high_impact_pairs": len([p for p in cannibalization_pairs if p.impact_score > 0.7])
            }
            
        except Exception as e:
            logger.error(f"Error calculating cannibalization impact: {e}")
            return {}
    
    async def _calculate_decay_insights(
        self, 
        decay_analyses: List[DecayAnalysis]
    ) -> Dict[str, Any]:
        """Calculate insights from decay analyses"""
        try:
            if not decay_analyses:
                return {}
            
            # Count decay types
            decay_counts = defaultdict(int)
            for analysis in decay_analyses:
                decay_counts[analysis.decay_type] += 1
            
            # Calculate average decay rate
            avg_decay_rate = statistics.mean([a.decay_rate for a in decay_analyses])
            
            return {
                "total_entities_analyzed": len(decay_analyses),
                "decay_distribution": dict(decay_counts),
                "average_decay_rate": avg_decay_rate,
                "entities_needing_attention": decay_counts["high"] + decay_counts["moderate"]
            }
            
        except Exception as e:
            logger.error(f"Error calculating decay insights: {e}")
            return {}
    
    async def _calculate_overall_incrementality(
        self, 
        incrementality_analyses: List[IncrementalityAnalysis]
    ) -> Dict[str, Any]:
        """Calculate overall incrementality metrics"""
        try:
            if not incrementality_analyses:
                return {}
            
            total_incremental_conversions = sum(a.incremental_conversions for a in incrementality_analyses)
            total_incremental_spend = sum(a.incremental_spend for a in incrementality_analyses)
            total_lift = statistics.mean([a.lift_percentage for a in incrementality_analyses])
            
            return {
                "total_incremental_conversions": total_incremental_conversions,
                "total_incremental_spend": total_incremental_spend,
                "overall_incremental_roas": (total_incremental_conversions / total_incremental_spend) if total_incremental_spend > 0 else 0,
                "average_lift_percentage": total_lift,
                "entities_analyzed": len(incrementality_analyses)
            }
            
        except Exception as e:
            logger.error(f"Error calculating overall incrementality: {e}")
            return {}
    
    async def _calculate_profitability_insights(
        self, 
        margin_analyses: List[ContributionMargin]
    ) -> Dict[str, Any]:
        """Calculate profitability insights"""
        try:
            if not margin_analyses:
                return {}
            
            profitable_entities = [m for m in margin_analyses if m.is_profitable]
            total_revenue = sum(m.revenue for m in margin_analyses)
            total_contribution_margin = sum(m.contribution_margin for m in margin_analyses)
            
            return {
                "total_entities": len(margin_analyses),
                "profitable_entities": len(profitable_entities),
                "profitability_rate": len(profitable_entities) / len(margin_analyses) * 100,
                "total_revenue": total_revenue,
                "total_contribution_margin": total_contribution_margin,
                "overall_margin_percentage": (total_contribution_margin / total_revenue * 100) if total_revenue > 0 else 0
            }
            
        except Exception as e:
            logger.error(f"Error calculating profitability insights: {e}")
            return {}
    
    async def _calculate_saturation_recommendations(
        self, 
        saturation_analyses: List[SaturationAnalysis], 
        optimization_goals: List[str]
    ) -> List[Dict[str, Any]]:
        """Calculate saturation optimization recommendations"""
        try:
            recommendations = []
            
            # Find entities with optimization potential
            for analysis in saturation_analyses:
                if analysis.optimization_potential > 0:
                    recommendations.append({
                        "entity_id": analysis.entity_id,
                        "current_budget": analysis.current_budget,
                        "recommended_budget": analysis.saturation_budget,
                        "optimization_potential": analysis.optimization_potential,
                        "priority": "high" if analysis.saturation_level == "low" else "medium"
                    })
            
            # Sort by optimization potential
            recommendations.sort(key=lambda x: x["optimization_potential"], reverse=True)
            
            return recommendations
            
        except Exception as e:
            logger.error(f"Error calculating saturation recommendations: {e}")
            return []


# Global analysis engine instance
_analysis_engine: Optional[AdvancedAnalysisEngine] = None


def get_analysis_engine() -> AdvancedAnalysisEngine:
    """Get the global AdvancedAnalysisEngine instance"""
    global _analysis_engine
    if _analysis_engine is None:
        from .meta_client import get_client
        _analysis_engine = AdvancedAnalysisEngine(get_client())
    return _analysis_engine
