"""
Comparison and benchmarking tools for Meta Ads campaigns.

This module provides capabilities for comparing campaigns, ad sets, creatives,
and time periods to help LLMs understand relative performance.
"""

import statistics
from typing import Any, Dict, List

from .meta_client import MetaClient
from .errors import MetaApiError
from .logging import get_logger

logger = get_logger(__name__)


class ComparisonManager:
    """Manages comparison and benchmarking operations for campaigns."""

    def __init__(self, client: MetaClient):
        self._client = client

    async def compare_campaigns(
        self,
        campaign_ids: List[str],
        start_date: str,
        end_date: str,
        metrics: List[str] = None,
        include_breakdowns: bool = False,
    ) -> Dict[str, Any]:
        """
        Compare multiple campaigns side-by-side.
        """
        if metrics is None:
            metrics = ["spend", "impressions", "clicks", "ctr", "cpc", "conversions"]

        try:
            # Get performance data for all campaigns
            campaign_data = []
            for campaign_id in campaign_ids:
                response = await self._client.get(
                    f"{campaign_id}/insights",
                    params={
                        "fields": ",".join(metrics),
                        "time_range": {"since": start_date, "until": end_date},
                    }
                )
                insights_data = response.get("data", [])
                
                # Aggregate data for the campaign
                campaign_metrics = self._aggregate_insights(insights_data, metrics)
                campaign_metrics["campaign_id"] = campaign_id
                
                # Get campaign details
                campaign_details = await self._client.get(
                    campaign_id,
                    params={"fields": "id,name,status,objective,daily_budget"}
                )
                campaign_info = campaign_details.get("data", {})
                campaign_metrics.update({
                    "name": campaign_info.get("name", "Unknown"),
                    "status": campaign_info.get("status", "unknown"),
                    "objective": campaign_info.get("objective", "unknown"),
                    "daily_budget": float(campaign_info.get("daily_budget", 0)),
                })
                
                campaign_data.append(campaign_metrics)

            # Calculate comparative metrics
            comparison_results = self._calculate_comparison_metrics(campaign_data, metrics)

            # Add breakdowns if requested
            breakdown_data = {}
            if include_breakdowns:
                breakdown_data = await self._get_breakdown_comparison(campaign_ids, start_date, end_date)

            return {
                "comparison_type": "campaigns",
                "date_range": {"start": start_date, "end": end_date},
                "campaigns_compared": len(campaign_ids),
                "campaign_data": campaign_data,
                "comparison_analysis": comparison_results,
                "breakdown_analysis": breakdown_data,
                "summary": {
                    "best_performing_campaign": max(campaign_data, key=lambda x: x.get("conversions", 0))["campaign_id"],
                    "highest_spend_campaign": max(campaign_data, key=lambda x: x.get("spend", 0))["campaign_id"],
                    "most_efficient_campaign": min(campaign_data, key=lambda x: x.get("cpc", float('inf')))["campaign_id"],
                },
                "next_recommended_actions": [
                    "Analyze top-performing campaign strategies",
                    "Identify successful elements to replicate",
                    "Consider budget reallocation based on performance",
                ],
            }
        except MetaApiError as exc:
            logger.error("Failed to compare campaigns", campaign_ids=campaign_ids, error=exc.to_dict())
            raise

    async def compare_adsets(
        self,
        adset_ids: List[str],
        start_date: str,
        end_date: str,
        metrics: List[str] = None,
        include_audience_breakdown: bool = False,
    ) -> Dict[str, Any]:
        """
        Compare multiple ad sets.
        """
        if metrics is None:
            metrics = ["spend", "impressions", "clicks", "ctr", "cpc", "conversions"]

        try:
            # Get performance data for all ad sets
            adset_data = []
            for adset_id in adset_ids:
                response = await self._client.get(
                    f"{adset_id}/insights",
                    params={
                        "fields": ",".join(metrics),
                        "time_range": {"since": start_date, "until": end_date},
                    }
                )
                insights_data = response.get("data", [])
                
                # Aggregate data for the ad set
                adset_metrics = self._aggregate_insights(insights_data, metrics)
                adset_metrics["adset_id"] = adset_id
                
                # Get ad set details
                adset_details = await self._client.get(
                    adset_id,
                    params={"fields": "id,name,status,optimization_goal,daily_budget,targeting"}
                )
                adset_info = adset_details.get("data", {})
                adset_metrics.update({
                    "name": adset_info.get("name", "Unknown"),
                    "status": adset_info.get("status", "unknown"),
                    "optimization_goal": adset_info.get("optimization_goal", "unknown"),
                    "daily_budget": float(adset_info.get("daily_budget", 0)),
                })
                
                adset_data.append(adset_metrics)

            # Calculate comparative metrics
            comparison_results = self._calculate_comparison_metrics(adset_data, metrics)

            # Add audience breakdown if requested
            audience_breakdown = {}
            if include_audience_breakdown:
                audience_breakdown = await self._get_audience_breakdown_comparison(adset_ids, start_date, end_date)

            return {
                "comparison_type": "adsets",
                "date_range": {"start": start_date, "end": end_date},
                "adsets_compared": len(adset_ids),
                "adset_data": adset_data,
                "comparison_analysis": comparison_results,
                "audience_breakdown": audience_breakdown,
                "summary": {
                    "best_performing_adset": max(adset_data, key=lambda x: x.get("conversions", 0))["adset_id"],
                    "highest_spend_adset": max(adset_data, key=lambda x: x.get("spend", 0))["adset_id"],
                    "most_efficient_adset": min(adset_data, key=lambda x: x.get("cpc", float('inf')))["adset_id"],
                },
                "next_recommended_actions": [
                    "Analyze top-performing ad set targeting",
                    "Identify successful optimization strategies",
                    "Consider budget reallocation between ad sets",
                ],
            }
        except MetaApiError as exc:
            logger.error("Failed to compare adsets", adset_ids=adset_ids, error=exc.to_dict())
            raise

    async def compare_creatives(
        self,
        creative_ids: List[str],
        start_date: str,
        end_date: str,
        metrics: List[str] = None,
        include_fatigue_analysis: bool = True,
    ) -> Dict[str, Any]:
        """
        Compare creative performance.
        """
        if metrics is None:
            metrics = ["spend", "impressions", "clicks", "ctr", "cpc", "conversions"]

        try:
            # Get performance data for all creatives
            creative_data = []
            for creative_id in creative_ids:
                response = await self._client.get(
                    f"{creative_id}/insights",
                    params={
                        "fields": ",".join(metrics + ["reach", "frequency"]),
                        "time_range": {"since": start_date, "until": end_date},
                    }
                )
                insights_data = response.get("data", [])
                
                # Aggregate data for the creative
                creative_metrics = self._aggregate_insights(insights_data, metrics)
                creative_metrics["creative_id"] = creative_id
                
                # Get creative details
                creative_details = await self._client.get(
                    creative_id,
                    params={"fields": "id,name,object_story_spec,thumbnail_url"}
                )
                creative_info = creative_details.get("data", {})
                creative_metrics.update({
                    "name": creative_info.get("name", "Unknown"),
                    "thumbnail_url": creative_info.get("thumbnail_url", ""),
                })
                
                creative_data.append(creative_metrics)

            # Calculate comparative metrics
            comparison_results = self._calculate_comparison_metrics(creative_data, metrics)

            # Add fatigue analysis if requested
            fatigue_analysis = {}
            if include_fatigue_analysis:
                fatigue_analysis = await self._get_creative_fatigue_analysis(creative_ids, start_date, end_date)

            return {
                "comparison_type": "creatives",
                "date_range": {"start": start_date, "end": end_date},
                "creatives_compared": len(creative_ids),
                "creative_data": creative_data,
                "comparison_analysis": comparison_results,
                "fatigue_analysis": fatigue_analysis,
                "summary": {
                    "best_performing_creative": max(creative_data, key=lambda x: x.get("conversions", 0))["creative_id"],
                    "highest_engagement_creative": max(creative_data, key=lambda x: x.get("ctr", 0))["creative_id"],
                    "most_efficient_creative": min(creative_data, key=lambda x: x.get("cpc", float('inf')))["creative_id"],
                },
                "next_recommended_actions": [
                    "Analyze top-performing creative elements",
                    "Identify successful creative patterns",
                    "Plan creative refresh schedule based on fatigue",
                ],
            }
        except MetaApiError as exc:
            logger.error("Failed to compare creatives", creative_ids=creative_ids, error=exc.to_dict())
            raise

    async def compare_time_periods(
        self,
        entity_id: str,
        entity_type: str,
        period_a_start: str,
        period_a_end: str,
        period_b_start: str,
        period_b_end: str,
        metrics: List[str] = None,
        include_trend_analysis: bool = True,
    ) -> Dict[str, Any]:
        """
        Compare same campaign across time periods.
        """
        if metrics is None:
            metrics = ["spend", "impressions", "clicks", "ctr", "cpc", "conversions"]

        try:
            # Get performance data for both periods
            period_a_data = await self._get_period_data(entity_id, period_a_start, period_a_end, metrics)
            period_b_data = await self._get_period_data(entity_id, period_b_start, period_b_end, metrics)

            # Calculate period comparisons
            period_comparison = self._calculate_period_comparison(period_a_data, period_b_data, metrics)

            # Add trend analysis if requested
            trend_analysis = {}
            if include_trend_analysis:
                trend_analysis = await self._get_trend_analysis(entity_id, period_a_start, period_b_end, metrics)

            return {
                "comparison_type": "time_periods",
                "entity_id": entity_id,
                "entity_type": entity_type,
                "period_a": {"start": period_a_start, "end": period_a_end, "data": period_a_data},
                "period_b": {"start": period_b_start, "end": period_b_end, "data": period_b_data},
                "period_comparison": period_comparison,
                "trend_analysis": trend_analysis,
                "summary": {
                    "overall_trend": "improving" if period_comparison["overall_change_percentage"] > 0 else "declining",
                    "biggest_improvement": max(period_comparison["metric_changes"].items(), key=lambda x: x[1]["change_percentage"]),
                    "biggest_decline": min(period_comparison["metric_changes"].items(), key=lambda x: x[1]["change_percentage"]),
                },
                "next_recommended_actions": [
                    "Analyze factors contributing to performance changes",
                    "Identify successful strategies from better period",
                    "Adjust strategy based on comparison insights",
                ],
            }
        except MetaApiError as exc:
            logger.error("Failed to compare time periods", entity_id=entity_id, error=exc.to_dict())
            raise

    async def compare_to_account_average(
        self,
        entity_id: str,
        entity_type: str,
        ad_account_id: str,
        start_date: str,
        end_date: str,
        metrics: List[str] = None,
        include_percentiles: bool = True,
    ) -> Dict[str, Any]:
        """
        Compare entity to account-level averages.
        """
        if metrics is None:
            metrics = ["spend", "impressions", "clicks", "ctr", "cpc", "conversions"]

        try:
            # Get entity performance
            entity_data = await self._get_period_data(entity_id, start_date, end_date, metrics)

            # Get account-level performance
            account_response = await self._client.get(
                f"{ad_account_id}/insights",
                params={
                    "fields": ",".join(metrics),
                    "time_range": {"since": start_date, "until": end_date},
                }
            )
            account_insights = account_response.get("data", [])
            account_data = self._aggregate_insights(account_insights, metrics)

            # Get all entities of the same type for percentile calculation
            all_entities_data = []
            if include_percentiles:
                all_entities_data = await self._get_all_entities_data(ad_account_id, entity_type, start_date, end_date, metrics)

            # Calculate comparison to account average
            comparison_to_average = self._calculate_average_comparison(entity_data, account_data, metrics)

            # Calculate percentiles if requested
            percentile_rankings = {}
            if include_percentiles and all_entities_data:
                percentile_rankings = self._calculate_percentile_rankings(entity_data, all_entities_data, metrics)

            return {
                "comparison_type": "account_average",
                "entity_id": entity_id,
                "entity_type": entity_type,
                "ad_account_id": ad_account_id,
                "date_range": {"start": start_date, "end": end_date},
                "entity_performance": entity_data,
                "account_average": account_data,
                "comparison_to_average": comparison_to_average,
                "percentile_rankings": percentile_rankings,
                "summary": {
                    "performance_vs_average": "above_average" if comparison_to_average["overall_performance_score"] > 1.0 else "below_average",
                    "best_metric_vs_average": max(comparison_to_average["metric_ratios"].items(), key=lambda x: x[1]),
                    "worst_metric_vs_average": min(comparison_to_average["metric_ratios"].items(), key=lambda x: x[1]),
                },
                "next_recommended_actions": [
                    "Focus on metrics below account average",
                    "Replicate strategies from above-average performance",
                    "Set targets based on account benchmarks",
                ],
            }
        except MetaApiError as exc:
            logger.error("Failed to compare to account average", entity_id=entity_id, error=exc.to_dict())
            raise

    async def calculate_percentiles(
        self,
        ad_account_id: str,
        entity_type: str,
        start_date: str,
        end_date: str,
        metrics: List[str] = None,
        percentiles: List[int] = None,
    ) -> Dict[str, Any]:
        """
        Calculate performance percentiles for entities in an account.
        """
        if metrics is None:
            metrics = ["spend", "impressions", "clicks", "ctr", "cpc", "conversions"]
        if percentiles is None:
            percentiles = [25, 50, 75, 90, 95]

        try:
            # Get all entities data
            all_entities_data = await self._get_all_entities_data(ad_account_id, entity_type, start_date, end_date, metrics)

            # Calculate percentiles for each metric
            percentile_data = {}
            for metric in metrics:
                values = [entity.get(metric, 0) for entity in all_entities_data if entity.get(metric) is not None]
                if values:
                    percentile_data[metric] = {
                        "count": len(values),
                        "min": min(values),
                        "max": max(values),
                        "mean": statistics.mean(values),
                        "median": statistics.median(values),
                        "percentiles": {p: self._calculate_percentile(values, p) for p in percentiles},
                    }

            return {
                "analysis_type": "percentiles",
                "ad_account_id": ad_account_id,
                "entity_type": entity_type,
                "date_range": {"start": start_date, "end": end_date},
                "total_entities": len(all_entities_data),
                "percentile_data": percentile_data,
                "percentiles_calculated": percentiles,
                "summary": {
                    "most_variable_metric": max(percentile_data.items(), key=lambda x: x[1]["max"] - x[1]["min"]),
                    "least_variable_metric": min(percentile_data.items(), key=lambda x: x[1]["max"] - x[1]["min"]),
                },
                "next_recommended_actions": [
                    "Use percentiles to set performance targets",
                    "Identify entities in top percentiles for analysis",
                    "Focus optimization on entities below median",
                ],
            }
        except MetaApiError as exc:
            logger.error("Failed to calculate percentiles", ad_account_id=ad_account_id, error=exc.to_dict())
            raise

    def _aggregate_insights(self, insights_data: List[Dict[str, Any]], metrics: List[str]) -> Dict[str, Any]:
        """Aggregate insights data across multiple time periods."""
        aggregated = {}
        
        for metric in metrics:
            if metric == "conversions":
                # Sum up conversion values
                total_conversions = 0
                for insight in insights_data:
                    actions = insight.get("actions", [])
                    for action in actions:
                        if action.get("action_type") == "offsite_conversion.fb_pixel_purchase":
                            total_conversions += action.get("value", 0)
                aggregated[metric] = total_conversions
            else:
                # Sum up other metrics
                aggregated[metric] = sum(float(insight.get(metric, 0)) for insight in insights_data)
        
        # Calculate derived metrics
        if aggregated.get("clicks", 0) > 0 and aggregated.get("impressions", 0) > 0:
            aggregated["ctr"] = aggregated["clicks"] / aggregated["impressions"]
        else:
            aggregated["ctr"] = 0
            
        if aggregated.get("spend", 0) > 0 and aggregated.get("clicks", 0) > 0:
            aggregated["cpc"] = aggregated["spend"] / aggregated["clicks"]
        else:
            aggregated["cpc"] = 0
            
        if aggregated.get("spend", 0) > 0 and aggregated.get("impressions", 0) > 0:
            aggregated["cpm"] = (aggregated["spend"] / aggregated["impressions"]) * 1000
        else:
            aggregated["cpm"] = 0
        
        return aggregated

    def _calculate_comparison_metrics(self, entities_data: List[Dict[str, Any]], metrics: List[str]) -> Dict[str, Any]:
        """Calculate comparative metrics across entities."""
        comparison = {
            "metric_ranges": {},
            "performance_rankings": {},
            "efficiency_scores": {},
        }
        
        for metric in metrics:
            values = [entity.get(metric, 0) for entity in entities_data]
            if values:
                comparison["metric_ranges"][metric] = {
                    "min": min(values),
                    "max": max(values),
                    "mean": statistics.mean(values),
                    "median": statistics.median(values),
                }
                
                # Rank entities by this metric
                sorted_entities = sorted(entities_data, key=lambda x: x.get(metric, 0), reverse=True)
                comparison["performance_rankings"][metric] = [
                    {"entity_id": entity.get("campaign_id") or entity.get("adset_id") or entity.get("creative_id"), 
                     "value": entity.get(metric, 0)} 
                    for entity in sorted_entities
                ]
        
        # Calculate efficiency scores (conversions per dollar spent)
        for entity in entities_data:
            entity_id = entity.get("campaign_id") or entity.get("adset_id") or entity.get("creative_id")
            spend = entity.get("spend", 0)
            conversions = entity.get("conversions", 0)
            efficiency_score = conversions / spend if spend > 0 else 0
            comparison["efficiency_scores"][entity_id] = efficiency_score
        
        return comparison

    def _calculate_period_comparison(self, period_a_data: Dict[str, Any], period_b_data: Dict[str, Any], metrics: List[str]) -> Dict[str, Any]:
        """Calculate comparison between two time periods."""
        comparison = {
            "metric_changes": {},
            "overall_change_percentage": 0,
        }
        
        total_change = 0
        for metric in metrics:
            period_a_value = period_a_data.get(metric, 0)
            period_b_value = period_b_data.get(metric, 0)
            
            if period_a_value > 0:
                change_percentage = ((period_b_value - period_a_value) / period_a_value) * 100
            else:
                change_percentage = 0 if period_b_value == 0 else 100
            
            comparison["metric_changes"][metric] = {
                "period_a": period_a_value,
                "period_b": period_b_value,
                "change_absolute": period_b_value - period_a_value,
                "change_percentage": change_percentage,
                "trend": "up" if change_percentage > 0 else "down" if change_percentage < 0 else "stable",
            }
            
            total_change += change_percentage
        
        comparison["overall_change_percentage"] = total_change / len(metrics) if metrics else 0
        
        return comparison

    def _calculate_average_comparison(self, entity_data: Dict[str, Any], account_data: Dict[str, Any], metrics: List[str]) -> Dict[str, Any]:
        """Calculate comparison to account average."""
        comparison = {
            "metric_ratios": {},
            "overall_performance_score": 0,
        }
        
        total_ratio = 0
        for metric in metrics:
            entity_value = entity_data.get(metric, 0)
            account_value = account_data.get(metric, 0)
            
            if account_value > 0:
                ratio = entity_value / account_value
            else:
                ratio = 1.0 if entity_value == 0 else float('inf')
            
            comparison["metric_ratios"][metric] = {
                "entity_value": entity_value,
                "account_average": account_value,
                "ratio": ratio,
                "performance_vs_average": "above" if ratio > 1.0 else "below" if ratio < 1.0 else "equal",
            }
            
            total_ratio += ratio
        
        comparison["overall_performance_score"] = total_ratio / len(metrics) if metrics else 1.0
        
        return comparison

    def _calculate_percentile_rankings(self, entity_data: Dict[str, Any], all_entities_data: List[Dict[str, Any]], metrics: List[str]) -> Dict[str, Any]:
        """Calculate percentile rankings for an entity."""
        rankings = {}
        
        for metric in metrics:
            entity_value = entity_data.get(metric, 0)
            all_values = [entity.get(metric, 0) for entity in all_entities_data if entity.get(metric) is not None]
            
            if all_values:
                # Calculate percentile
                percentile = (sum(1 for v in all_values if v <= entity_value) / len(all_values)) * 100
                rankings[metric] = {
                    "value": entity_value,
                    "percentile": percentile,
                    "ranking": f"Top {100 - percentile:.1f}%" if percentile > 50 else f"Bottom {percentile:.1f}%",
                }
        
        return rankings

    def _calculate_percentile(self, values: List[float], percentile: int) -> float:
        """Calculate a specific percentile from a list of values."""
        if not values:
            return 0.0
        
        sorted_values = sorted(values)
        index = (percentile / 100) * (len(sorted_values) - 1)
        
        if index.is_integer():
            return sorted_values[int(index)]
        else:
            lower = sorted_values[int(index)]
            upper = sorted_values[int(index) + 1]
            return lower + (upper - lower) * (index - int(index))

    async def _get_period_data(self, entity_id: str, start_date: str, end_date: str, metrics: List[str]) -> Dict[str, Any]:
        """Get performance data for a specific period."""
        response = await self._client.get(
            f"{entity_id}/insights",
            params={
                "fields": ",".join(metrics),
                "time_range": {"since": start_date, "until": end_date},
            }
        )
        insights_data = response.get("data", [])
        return self._aggregate_insights(insights_data, metrics)

    async def _get_all_entities_data(self, ad_account_id: str, entity_type: str, start_date: str, end_date: str, metrics: List[str]) -> List[Dict[str, Any]]:
        """Get performance data for all entities of a type in an account."""
        # Get all entities
        entities_response = await self._client.get(
            f"{ad_account_id}/{entity_type}s",
            params={"fields": "id,name,status"}
        )
        entities = entities_response.get("data", [])
        
        # Get performance data for each entity
        all_entities_data = []
        for entity in entities:
            entity_id = entity["id"]
            try:
                entity_data = await self._get_period_data(entity_id, start_date, end_date, metrics)
                entity_data["entity_id"] = entity_id
                entity_data["name"] = entity.get("name", "Unknown")
                entity_data["status"] = entity.get("status", "unknown")
                all_entities_data.append(entity_data)
            except MetaApiError:
                # Skip entities that can't be accessed
                continue
        
        return all_entities_data

    async def _get_breakdown_comparison(self, campaign_ids: List[str], start_date: str, end_date: str) -> Dict[str, Any]:
        """Get demographic/device breakdown comparison."""
        # This would implement detailed breakdown analysis
        # For now, return a placeholder
        return {"breakdown_analysis": "Detailed breakdown analysis would be implemented here"}

    async def _get_audience_breakdown_comparison(self, adset_ids: List[str], start_date: str, end_date: str) -> Dict[str, Any]:
        """Get audience performance breakdown comparison."""
        # This would implement audience breakdown analysis
        # For now, return a placeholder
        return {"audience_breakdown": "Detailed audience breakdown analysis would be implemented here"}

    async def _get_creative_fatigue_analysis(self, creative_ids: List[str], start_date: str, end_date: str) -> Dict[str, Any]:
        """Get creative fatigue analysis."""
        # This would implement creative fatigue analysis
        # For now, return a placeholder
        return {"fatigue_analysis": "Detailed creative fatigue analysis would be implemented here"}

    async def _get_trend_analysis(self, entity_id: str, start_date: str, end_date: str, metrics: List[str]) -> Dict[str, Any]:
        """Get trend analysis between periods."""
        # This would implement trend analysis
        # For now, return a placeholder
        return {"trend_analysis": "Detailed trend analysis would be implemented here"}
