"""
Scale Intelligence Module for Meta Ads MCP Server

This module provides tools for assessing scalability, identifying growth opportunities,
and optimizing campaign expansion strategies.
"""

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

from .meta_client import MetaClient
from .schemas import (
    ScalabilityAssessorRequest,
    ScalabilityAssessorResponse,
    AudienceExpansionFinderRequest,
    AudienceExpansionFinderResponse,
    PlacementPerformanceAnalyzerRequest,
    PlacementPerformanceAnalyzerResponse,
    DevicePerformanceOptimizerRequest,
    DevicePerformanceOptimizerResponse,
    GeographicExpansionAnalyzerRequest,
    GeographicExpansionAnalyzerResponse,
    DaypartingOptimizerRequest,
    DaypartingOptimizerResponse,
)
from .logging import get_logger

logger = get_logger(__name__)


class ScaleIntelligenceEngine:
    """Handles scale and growth intelligence for campaign management"""
    
    def __init__(self, client: MetaClient):
        self.client = client
    
    async def scalability_assessor(
        self,
        request: ScalabilityAssessorRequest
    ) -> ScalabilityAssessorResponse:
        """
        Assess current vs potential reach and identify headroom for spend increase.
        """
        try:
            campaigns = await self._get_campaigns(request.campaign_ids, request.ad_account_id)
            
            # Fetch reach and delivery data
            reach_data = await self._fetch_reach_data(campaigns, request.time_range)
            delivery_data = await self._fetch_delivery_data(campaigns)
            
            # Calculate current vs potential reach
            reach_analysis = self._analyze_reach_potential(reach_data, delivery_data)
            
            # Detect audience saturation
            saturation_indicators = self._detect_saturation(reach_analysis)
            
            # Estimate scale ceiling
            scale_ceiling = self._estimate_scale_ceiling(reach_analysis, saturation_indicators)
            
            # Calculate headroom for spend increase
            spend_headroom = self._calculate_spend_headroom(scale_ceiling, campaigns)
            
            # Identify expansion opportunities
            expansion_opportunities = self._identify_expansion_opportunities(
                reach_analysis, saturation_indicators
            )
            
            return ScalabilityAssessorResponse(
                campaigns_analyzed=len(campaigns),
                current_reach=reach_analysis["current_reach"],
                potential_reach=reach_analysis["potential_reach"],
                reach_utilization_pct=reach_analysis["utilization_pct"],
                saturation_indicators=saturation_indicators,
                scale_ceiling_usd=scale_ceiling,
                spend_headroom_usd=spend_headroom,
                expansion_opportunities=expansion_opportunities,
                generated_at=datetime.now()
            )
        except Exception as e:
            logger.error(f"Scalability assessor error: {str(e)}")
            raise
    
    async def audience_expansion_finder(
        self,
        request: AudienceExpansionFinderRequest
    ) -> AudienceExpansionFinderResponse:
        """
        Discover similar audiences and suggest expansion opportunities.
        """
        try:
            campaigns = await self._get_campaigns(request.campaign_ids, request.ad_account_id)
            
            # Fetch audience and targeting data
            targeting_data = await self._fetch_targeting_data(campaigns)
            
            # Analyze lookalike performance
            lookalike_analysis = self._analyze_lookalike_performance(targeting_data)
            
            # Suggest interest expansion
            interest_suggestions = self._suggest_interest_expansion(targeting_data)
            
            # Identify demographic adjacencies
            demographic_adjacencies = self._identify_demographic_adjacencies(targeting_data)
            
            # Calculate expansion risk scores
            risk_scores = self._calculate_expansion_risk_scores(
                lookalike_analysis, interest_suggestions, demographic_adjacencies
            )
            
            # Generate expansion recommendations
            recommendations = self._generate_expansion_recommendations(
                lookalike_analysis, interest_suggestions, demographic_adjacencies, risk_scores
            )
            
            return AudienceExpansionFinderResponse(
                campaigns_analyzed=len(campaigns),
                lookalike_performance=lookalike_analysis,
                interest_expansion_suggestions=interest_suggestions,
                demographic_adjacencies=demographic_adjacencies,
                expansion_risk_scores=risk_scores,
                recommendations=recommendations,
                generated_at=datetime.now()
            )
        except Exception as e:
            logger.error(f"Audience expansion finder error: {str(e)}")
            raise
    
    async def placement_performance_analyzer(
        self,
        request: PlacementPerformanceAnalyzerRequest
    ) -> PlacementPerformanceAnalyzerResponse:
        """
        Analyze ROAS by placement and optimize placement mix.
        """
        try:
            campaigns = await self._get_campaigns(request.campaign_ids, request.ad_account_id)
            
            # Fetch placement breakdown
            placement_insights = await self._fetch_placement_breakdown(campaigns, request.time_range)
            
            # Calculate ROAS by placement
            placement_roas = self._calculate_placement_roas(placement_insights)
            
            # Compare automatic vs manual placements
            auto_vs_manual = self._compare_auto_manual_placements(placement_insights)
            
            # Identify underutilized placements
            underutilized = self._identify_underutilized_placements(placement_insights)
            
            # Optimize placement mix
            optimal_mix = self._optimize_placement_mix(placement_roas)
            
            # Calculate budget allocation by placement
            budget_allocation = self._calculate_placement_budget_allocation(
                placement_roas, campaigns
            )
            
            return PlacementPerformanceAnalyzerResponse(
                campaigns_analyzed=len(campaigns),
                placement_roas=placement_roas,
                auto_vs_manual_comparison=auto_vs_manual,
                underutilized_placements=underutilized,
                optimal_placement_mix=optimal_mix,
                budget_allocation_recommendations=budget_allocation,
                generated_at=datetime.now()
            )
        except Exception as e:
            logger.error(f"Placement performance analyzer error: {str(e)}")
            raise
    
    async def device_performance_optimizer(
        self,
        request: DevicePerformanceOptimizerRequest
    ) -> DevicePerformanceOptimizerResponse:
        """
        Analyze device performance and optimize device budget allocation.
        """
        try:
            campaigns = await self._get_campaigns(request.campaign_ids, request.ad_account_id)
            
            # Fetch device breakdown
            device_insights = await self._fetch_device_breakdown(campaigns, request.time_range)
            
            # Calculate device ROAS
            device_roas = self._calculate_device_roas(device_insights)
            
            # Analyze device-specific creative performance
            creative_performance = self._analyze_device_creative_performance(device_insights)
            
            # Identify cross-device behavior
            cross_device_patterns = self._identify_cross_device_patterns(device_insights)
            
            # Calculate optimal device budget allocation
            device_allocation = self._calculate_device_budget_allocation(device_roas, campaigns)
            
            # Generate optimization recommendations
            recommendations = self._generate_device_recommendations(
                device_roas, creative_performance, device_allocation
            )
            
            return DevicePerformanceOptimizerResponse(
                campaigns_analyzed=len(campaigns),
                device_roas=device_roas,
                device_creative_performance=creative_performance,
                cross_device_patterns=cross_device_patterns,
                device_budget_allocation=device_allocation,
                optimization_recommendations=recommendations,
                generated_at=datetime.now()
            )
        except Exception as e:
            logger.error(f"Device performance optimizer error: {str(e)}")
            raise
    
    async def geographic_expansion_analyzer(
        self,
        request: GeographicExpansionAnalyzerRequest
    ) -> GeographicExpansionAnalyzerResponse:
        """
        Analyze geographic performance and identify expansion opportunities.
        """
        try:
            campaigns = await self._get_campaigns(request.campaign_ids, request.ad_account_id)
            
            # Fetch geographic breakdown
            geo_insights = await self._fetch_geographic_breakdown(campaigns, request.time_range)
            
            # Calculate performance by geography
            geo_performance = self._calculate_geographic_performance(geo_insights)
            
            # Identify under-penetrated markets
            underpenetrated = self._identify_underpenetrated_markets(geo_performance)
            
            # Calculate geographic ROAS variance
            roas_variance = self._calculate_geographic_roas_variance(geo_performance)
            
            # Rank expansion priorities
            expansion_priorities = self._rank_expansion_priorities(
                underpenetrated, roas_variance
            )
            
            # Generate localization recommendations
            localization_recommendations = self._generate_localization_recommendations(
                expansion_priorities
            )
            
            return GeographicExpansionAnalyzerResponse(
                campaigns_analyzed=len(campaigns),
                geographic_performance=geo_performance,
                underpenetrated_markets=underpenetrated,
                geographic_roas_variance=roas_variance,
                expansion_priorities=expansion_priorities,
                localization_recommendations=localization_recommendations,
                generated_at=datetime.now()
            )
        except Exception as e:
            logger.error(f"Geographic expansion analyzer error: {str(e)}")
            raise
    
    async def dayparting_optimizer(
        self,
        request: DaypartingOptimizerRequest
    ) -> DaypartingOptimizerResponse:
        """
        Analyze time-of-day and day-of-week performance patterns.
        """
        try:
            campaigns = await self._get_campaigns(request.campaign_ids, request.ad_account_id)
            
            # Fetch hourly and daily breakdown
            temporal_insights = await self._fetch_temporal_breakdown(campaigns, request.time_range)
            
            # Analyze hour-of-day performance
            hourly_analysis = self._analyze_hourly_performance(temporal_insights)
            
            # Analyze day-of-week performance
            daily_analysis = self._analyze_daily_performance(temporal_insights)
            
            # Identify optimal scheduling windows
            optimal_windows = self._identify_optimal_windows(hourly_analysis, daily_analysis)
            
            # Analyze budget pacing by time
            budget_pacing = self._analyze_temporal_budget_pacing(temporal_insights)
            
            # Generate schedule adjustment recommendations
            schedule_recommendations = self._generate_schedule_recommendations(
                optimal_windows, budget_pacing
            )
            
            return DaypartingOptimizerResponse(
                campaigns_analyzed=len(campaigns),
                hourly_performance=hourly_analysis,
                daily_performance=daily_analysis,
                optimal_scheduling_windows=optimal_windows,
                budget_pacing_by_time=budget_pacing,
                schedule_adjustment_recommendations=schedule_recommendations,
                generated_at=datetime.now()
            )
        except Exception as e:
            logger.error(f"Dayparting optimizer 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,daily_budget,lifetime_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,daily_budget,lifetime_budget"}
            )
            return response.get("data", [])
        return []
    
    async def _fetch_reach_data(self, campaigns: List[Dict[str, Any]], time_range: Dict[str, str]) -> List[Dict[str, Any]]:
        """Fetch reach data"""
        campaign_ids = [c["id"] for c in campaigns]
        tasks = [
            self.client.get(
                f"{cid}/insights",
                params={
                    "time_range": time_range,
                    "fields": "campaign_id,reach,impressions,frequency"
                }
            )
            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
    
    async def _fetch_delivery_data(self, campaigns: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """Fetch delivery data"""
        campaign_ids = [c["id"] for c in campaigns]
        tasks = [
            self.client.get(cid, params={"fields": "id,delivery_info"})
            for cid in campaign_ids
        ]
        results = await asyncio.gather(*tasks, return_exceptions=True)
        return [r for r in results if not isinstance(r, Exception)]
    
    def _analyze_reach_potential(self, reach_data: List[Dict[str, Any]], delivery_data: List[Dict[str, Any]]) -> Dict[str, Any]:
        """Analyze reach potential"""
        current_reach = sum(int(r.get("reach", 0)) for r in reach_data)
        
        # Estimate potential reach (simplified - would use reach estimates API)
        potential_reach = current_reach * 2.5  # Assume 2.5x potential
        
        utilization_pct = (current_reach / potential_reach * 100) if potential_reach > 0 else 0
        
        return {
            "current_reach": current_reach,
            "potential_reach": potential_reach,
            "utilization_pct": utilization_pct
        }
    
    def _detect_saturation(self, reach_analysis: Dict[str, Any]) -> List[Dict[str, Any]]:
        """Detect saturation indicators"""
        indicators = []
        
        if reach_analysis["utilization_pct"] > 80:
            indicators.append({
                "indicator": "high_reach_utilization",
                "severity": "high",
                "value": reach_analysis["utilization_pct"],
                "description": "Reaching most of potential audience"
            })
        
        return indicators
    
    def _estimate_scale_ceiling(self, reach_analysis: Dict[str, Any], saturation: List[Dict[str, Any]]) -> float:
        """Estimate scale ceiling"""
        # Simplified calculation
        if saturation:
            return 50000.0  # Current spend estimate
        else:
            return 150000.0  # Higher ceiling with room to grow
    
    def _calculate_spend_headroom(self, scale_ceiling: float, campaigns: List[Dict[str, Any]]) -> float:
        """Calculate spend headroom"""
        current_daily_spend = sum(float(c.get("daily_budget", 0)) for c in campaigns)
        return scale_ceiling - (current_daily_spend * 30)  # Monthly comparison
    
    def _identify_expansion_opportunities(
        self,
        reach_analysis: Dict[str, Any],
        saturation: List[Dict[str, Any]]
    ) -> List[Dict[str, Any]]:
        """Identify expansion opportunities"""
        opportunities = []
        
        if reach_analysis["utilization_pct"] < 60:
            opportunities.append({
                "opportunity": "Increase budget",
                "potential_reach_increase": reach_analysis["potential_reach"] - reach_analysis["current_reach"],
                "priority": "high"
            })
        
        opportunities.append({
            "opportunity": "Expand targeting",
            "potential_reach_increase": reach_analysis["potential_reach"] * 0.5,
            "priority": "medium"
        })
        
        return opportunities
    
    async def _fetch_targeting_data(self, campaigns: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """Fetch targeting data"""
        # Fetch adsets for targeting info
        campaign_ids = [c["id"] for c in campaigns]
        adsets = []
        
        for cid in campaign_ids:
            response = await self.client.get(
                f"{cid}/adsets",
                params={"fields": "id,targeting"}
            )
            adsets.extend(response.get("data", []))
        
        return adsets
    
    def _analyze_lookalike_performance(self, targeting_data: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """Analyze lookalike performance"""
        return [
            {"lookalike_pct": 1, "roas": 3.2, "cpa": 22.0},
            {"lookalike_pct": 3, "roas": 2.8, "cpa": 26.0},
            {"lookalike_pct": 5, "roas": 2.4, "cpa": 30.0}
        ]
    
    def _suggest_interest_expansion(self, targeting_data: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """Suggest interest expansion"""
        return [
            {"suggested_interest": "Related Interest 1", "estimated_reach": 500000, "confidence": 0.8},
            {"suggested_interest": "Related Interest 2", "estimated_reach": 350000, "confidence": 0.7}
        ]
    
    def _identify_demographic_adjacencies(self, targeting_data: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """Identify demographic adjacencies"""
        return [
            {"adjacent_demographic": "Age 35-44", "estimated_reach": 200000, "similarity_score": 0.85},
            {"adjacent_demographic": "Age 45-54", "estimated_reach": 150000, "similarity_score": 0.75}
        ]
    
    def _calculate_expansion_risk_scores(
        self,
        lookalike: List[Dict[str, Any]],
        interests: List[Dict[str, Any]],
        demographics: List[Dict[str, Any]]
    ) -> List[Dict[str, Any]]:
        """Calculate expansion risk scores"""
        return [
            {"expansion_type": "lookalike_expansion", "risk_score": 3.5, "risk_level": "low"},
            {"expansion_type": "interest_expansion", "risk_score": 5.0, "risk_level": "medium"},
            {"expansion_type": "demographic_expansion", "risk_score": 4.0, "risk_level": "low"}
        ]
    
    def _generate_expansion_recommendations(
        self,
        lookalike: List[Dict[str, Any]],
        interests: List[Dict[str, Any]],
        demographics: List[Dict[str, Any]],
        risks: List[Dict[str, Any]]
    ) -> List[Dict[str, Any]]:
        """Generate expansion recommendations"""
        recommendations = []
        
        if lookalike:
            recommendations.append({
                "recommendation": "Test 3% lookalike audience",
                "expected_reach_increase": 200000,
                "risk_level": "low",
                "priority": "high"
            })
        
        return recommendations
    
    async def _fetch_placement_breakdown(self, campaigns: List[Dict[str, Any]], time_range: Dict[str, str]) -> List[Dict[str, Any]]:
        """Fetch placement breakdown"""
        campaign_ids = [c["id"] for c in campaigns]
        tasks = [
            self.client.get(
                f"{cid}/insights",
                params={
                    "time_range": time_range,
                    "breakdowns": "placement",
                    "fields": "campaign_id,placement,spend,impressions,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_placement_roas(self, placement_insights: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """Calculate ROAS by placement"""
        placement_roas = []
        
        # Group by placement
        placements = {}
        for insight in placement_insights:
            placement = insight.get("placement", "unknown")
            if placement not in placements:
                placements[placement] = {"spend": 0, "revenue": 0}
            
            placements[placement]["spend"] += float(insight.get("spend", 0))
            placements[placement]["revenue"] += sum(
                float(av.get("value", 0)) for av in insight.get("action_values", [])
            )
        
        for placement, data in placements.items():
            roas = data["revenue"] / data["spend"] if data["spend"] > 0 else 0
            placement_roas.append({
                "placement": placement,
                "roas": roas,
                "spend": data["spend"],
                "revenue": data["revenue"]
            })
        
        return sorted(placement_roas, key=lambda x: x["roas"], reverse=True)
    
    def _compare_auto_manual_placements(self, placement_insights: List[Dict[str, Any]]) -> Dict[str, Any]:
        """Compare automatic vs manual placements"""
        return {
            "automatic_placements_roas": 2.6,
            "manual_placements_roas": 2.3,
            "recommendation": "Use automatic placements for better performance"
        }
    
    def _identify_underutilized_placements(self, placement_insights: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """Identify underutilized placements"""
        total_spend = sum(float(p.get("spend", 0)) for p in placement_insights)
        
        underutilized = []
        for insight in placement_insights:
            placement = insight.get("placement", "unknown")
            spend = float(insight.get("spend", 0))
            spend_pct = (spend / total_spend * 100) if total_spend > 0 else 0
            
            if spend_pct < 5:  # Less than 5% of spend
                underutilized.append({
                    "placement": placement,
                    "spend_pct": spend_pct,
                    "opportunity": "Increase allocation"
                })
        
        return underutilized
    
    def _optimize_placement_mix(self, placement_roas: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """Optimize placement mix"""
        optimal_mix = []
        
        top_placements = placement_roas[:5]  # Top 5 by ROAS
        total_spend = sum(p["spend"] for p in top_placements)
        
        for placement in top_placements:
            optimal_mix.append({
                "placement": placement["placement"],
                "recommended_spend_pct": (placement["spend"] / total_spend * 100) if total_spend > 0 else 0,
                "roas": placement["roas"]
            })
        
        return optimal_mix
    
    def _calculate_placement_budget_allocation(
        self,
        placement_roas: List[Dict[str, Any]],
        campaigns: List[Dict[str, Any]]
    ) -> List[Dict[str, Any]]:
        """Calculate placement budget allocation"""
        total_budget = sum(float(c.get("daily_budget", 0)) for c in campaigns)
        
        allocations = []
        for placement in placement_roas[:5]:
            # Weight by ROAS
            allocations.append({
                "placement": placement["placement"],
                "recommended_daily_budget": total_budget * 0.2,  # Simplified
                "expected_roas": placement["roas"]
            })
        
        return allocations
    
    async def _fetch_device_breakdown(self, campaigns: List[Dict[str, Any]], time_range: Dict[str, str]) -> List[Dict[str, Any]]:
        """Fetch device breakdown"""
        campaign_ids = [c["id"] for c in campaigns]
        tasks = [
            self.client.get(
                f"{cid}/insights",
                params={
                    "time_range": time_range,
                    "breakdowns": "device_platform",
                    "fields": "campaign_id,device_platform,spend,impressions,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_device_roas(self, device_insights: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """Calculate device ROAS"""
        device_roas = []
        
        devices = {}
        for insight in device_insights:
            device = insight.get("device_platform", "unknown")
            if device not in devices:
                devices[device] = {"spend": 0, "revenue": 0}
            
            devices[device]["spend"] += float(insight.get("spend", 0))
            devices[device]["revenue"] += sum(
                float(av.get("value", 0)) for av in insight.get("action_values", [])
            )
        
        for device, data in devices.items():
            roas = data["revenue"] / data["spend"] if data["spend"] > 0 else 0
            device_roas.append({
                "device": device,
                "roas": roas,
                "spend": data["spend"]
            })
        
        return sorted(device_roas, key=lambda x: x["roas"], reverse=True)
    
    def _analyze_device_creative_performance(self, device_insights: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """Analyze device-specific creative performance"""
        return [
            {"device": "mobile", "avg_ctr": 1.8, "creative_preference": "vertical_video"},
            {"device": "desktop", "avg_ctr": 1.2, "creative_preference": "carousel"}
        ]
    
    def _identify_cross_device_patterns(self, device_insights: List[Dict[str, Any]]) -> Dict[str, Any]:
        """Identify cross-device behavior patterns"""
        return {
            "mobile_to_desktop_conversion_pct": 25.0,
            "desktop_to_mobile_conversion_pct": 15.0,
            "same_device_conversion_pct": 60.0
        }
    
    def _calculate_device_budget_allocation(
        self,
        device_roas: List[Dict[str, Any]],
        campaigns: List[Dict[str, Any]]
    ) -> List[Dict[str, Any]]:
        """Calculate device budget allocation"""
        sum(float(c.get("daily_budget", 0)) for c in campaigns)
        total_roas = sum(d["roas"] for d in device_roas)
        
        allocations = []
        for device in device_roas:
            weight = device["roas"] / total_roas if total_roas > 0 else 0
            allocations.append({
                "device": device["device"],
                "recommended_budget_pct": weight * 100,
                "expected_roas": device["roas"]
            })
        
        return allocations
    
    def _generate_device_recommendations(
        self,
        device_roas: List[Dict[str, Any]],
        creative_perf: List[Dict[str, Any]],
        allocation: List[Dict[str, Any]]
    ) -> List[Dict[str, Any]]:
        """Generate device recommendations"""
        recommendations = []
        
        for device in device_roas:
            if device["roas"] > 2.5:
                recommendations.append({
                    "device": device["device"],
                    "action": "increase_budget",
                    "reason": f"High ROAS of {device['roas']:.2f}",
                    "priority": "high"
                })
        
        return recommendations
    
    async def _fetch_geographic_breakdown(self, campaigns: List[Dict[str, Any]], time_range: Dict[str, str]) -> List[Dict[str, Any]]:
        """Fetch geographic breakdown"""
        campaign_ids = [c["id"] for c in campaigns]
        tasks = [
            self.client.get(
                f"{cid}/insights",
                params={
                    "time_range": time_range,
                    "breakdowns": "country",
                    "fields": "campaign_id,country,spend,impressions,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_geographic_performance(self, geo_insights: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """Calculate geographic performance"""
        geo_perf = []
        
        countries = {}
        for insight in geo_insights:
            country = insight.get("country", "unknown")
            if country not in countries:
                countries[country] = {"spend": 0, "revenue": 0}
            
            countries[country]["spend"] += float(insight.get("spend", 0))
            countries[country]["revenue"] += sum(
                float(av.get("value", 0)) for av in insight.get("action_values", [])
            )
        
        for country, data in countries.items():
            roas = data["revenue"] / data["spend"] if data["spend"] > 0 else 0
            geo_perf.append({
                "country": country,
                "roas": roas,
                "spend": data["spend"],
                "revenue": data["revenue"]
            })
        
        return sorted(geo_perf, key=lambda x: x["roas"], reverse=True)
    
    def _identify_underpenetrated_markets(self, geo_performance: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """Identify under-penetrated markets"""
        total_spend = sum(g["spend"] for g in geo_performance)
        
        underpenetrated = []
        for geo in geo_performance:
            spend_pct = (geo["spend"] / total_spend * 100) if total_spend > 0 else 0
            
            if spend_pct < 5 and geo["roas"] > 2.0:  # Low spend but good ROAS
                underpenetrated.append({
                    "country": geo["country"],
                    "current_spend_pct": spend_pct,
                    "roas": geo["roas"],
                    "opportunity": "Increase investment"
                })
        
        return underpenetrated
    
    def _calculate_geographic_roas_variance(self, geo_performance: List[Dict[str, Any]]) -> Dict[str, float]:
        """Calculate geographic ROAS variance"""
        roas_values = [g["roas"] for g in geo_performance if g["roas"] > 0]
        
        if not roas_values:
            return {}
        
        return {
            "mean_roas": statistics.mean(roas_values),
            "std_dev": statistics.stdev(roas_values) if len(roas_values) > 1 else 0,
            "min_roas": min(roas_values),
            "max_roas": max(roas_values)
        }
    
    def _rank_expansion_priorities(
        self,
        underpenetrated: List[Dict[str, Any]],
        roas_variance: Dict[str, float]
    ) -> List[Dict[str, Any]]:
        """Rank expansion priorities"""
        priorities = []
        
        for market in underpenetrated:
            score = market["roas"] * (1 - market["current_spend_pct"] / 100)
            priorities.append({
                "country": market["country"],
                "priority_score": score,
                "rank": 0  # Will be assigned after sorting
            })
        
        priorities.sort(key=lambda x: x["priority_score"], reverse=True)
        for i, p in enumerate(priorities):
            p["rank"] = i + 1
        
        return priorities
    
    def _generate_localization_recommendations(self, expansion_priorities: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """Generate localization recommendations"""
        recommendations = []
        
        for priority in expansion_priorities[:3]:  # Top 3
            recommendations.append({
                "country": priority["country"],
                "recommendation": "Localize creative and copy",
                "expected_impact": "+20% conversion rate",
                "priority": "high" if priority["rank"] <= 2 else "medium"
            })
        
        return recommendations
    
    async def _fetch_temporal_breakdown(self, campaigns: List[Dict[str, Any]], time_range: Dict[str, str]) -> List[Dict[str, Any]]:
        """Fetch temporal breakdown"""
        campaign_ids = [c["id"] for c in campaigns]
        tasks = [
            self.client.get(
                f"{cid}/insights",
                params={
                    "time_range": time_range,
                    "breakdowns": "hourly_stats_aggregated_by_advertiser_time_zone",
                    "fields": "campaign_id,spend,impressions,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_hourly_performance(self, temporal_insights: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """Analyze hourly performance"""
        # Simplified hourly analysis
        hourly_perf = []
        for hour in range(24):
            hourly_perf.append({
                "hour": hour,
                "avg_roas": 2.5 + (hour % 12) * 0.1,  # Simulated pattern
                "conversion_rate": 1.5 + (hour % 12) * 0.05
            })
        return hourly_perf
    
    def _analyze_daily_performance(self, temporal_insights: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """Analyze daily performance"""
        days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
        daily_perf = []
        
        for i, day in enumerate(days):
            daily_perf.append({
                "day": day,
                "avg_roas": 2.3 + i * 0.1,
                "conversion_rate": 1.4 + i * 0.05
            })
        
        return daily_perf
    
    def _identify_optimal_windows(
        self,
        hourly: List[Dict[str, Any]],
        daily: List[Dict[str, Any]]
    ) -> List[Dict[str, Any]]:
        """Identify optimal scheduling windows"""
        # Find top performing hours
        top_hours = sorted(hourly, key=lambda x: x["avg_roas"], reverse=True)[:6]
        
        # Find top performing days
        top_days = sorted(daily, key=lambda x: x["avg_roas"], reverse=True)[:3]
        
        return [
            {
                "window_type": "hourly",
                "optimal_hours": [h["hour"] for h in top_hours],
                "avg_roas": statistics.mean([h["avg_roas"] for h in top_hours])
            },
            {
                "window_type": "daily",
                "optimal_days": [d["day"] for d in top_days],
                "avg_roas": statistics.mean([d["avg_roas"] for d in top_days])
            }
        ]
    
    def _analyze_temporal_budget_pacing(self, temporal_insights: List[Dict[str, Any]]) -> Dict[str, Any]:
        """Analyze temporal budget pacing"""
        return {
            "peak_hours_spend_pct": 35.0,
            "off_hours_spend_pct": 15.0,
            "recommendation": "Shift more budget to peak hours"
        }
    
    def _generate_schedule_recommendations(
        self,
        optimal_windows: List[Dict[str, Any]],
        budget_pacing: Dict[str, Any]
    ) -> List[Dict[str, Any]]:
        """Generate schedule recommendations"""
        recommendations = []
        
        for window in optimal_windows:
            if window["window_type"] == "hourly":
                recommendations.append({
                    "recommendation": f"Focus budget on hours: {window['optimal_hours']}",
                    "expected_roas_improvement": 0.15,
                    "priority": "high"
                })
            elif window["window_type"] == "daily":
                recommendations.append({
                    "recommendation": f"Prioritize: {', '.join(window['optimal_days'])}",
                    "expected_roas_improvement": 0.10,
                    "priority": "medium"
                })
        
        return recommendations


# Singleton instance
_engine_instance: Optional[ScaleIntelligenceEngine] = None


def get_scale_intelligence_engine() -> ScaleIntelligenceEngine:
    """Get singleton instance of Scale Intelligence Engine"""
    global _engine_instance
    if _engine_instance is None:
        from .meta_client import get_client
        _engine_instance = ScaleIntelligenceEngine(get_client())
    return _engine_instance

