"""
Competitive Intelligence Tools for Meta Ads MCP Server

This module provides tools to analyze the competitive landscape.
"""

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

from .meta_client import MetaClient
from .schemas import (
    AuctionInsightsRequest,
    MarketSaturationRequest,
    ShareOfVoiceRequest,
    AuctionInsightsResponse,
    MarketSaturationResponse,
    ShareOfVoiceResponse,
)
from .logging import get_logger

logger = get_logger(__name__)


class CompetitiveIntelligenceEngine:
    """Handles competitive intelligence analysis for Meta Ads"""
    
    def __init__(self, client: MetaClient):
        self.client = client
    
    async def get_auction_insights(
        self, 
        request: AuctionInsightsRequest
    ) -> AuctionInsightsResponse:
        """Deep auction analysis"""
        try:
            logger.info(f"Getting auction insights for {request.entity_id}")
            
            # Get delivery insights from Meta API
            insights_response = await self.client.graph_request(
                method="GET",
                path=f"{request.entity_id}/insights",
                params={
                    "fields": "auction_competitiveness,auction_overlap,reach,frequency,cpm,cpp",
                    "time_range": request.time_range,
                    "level": "adset"
                }
            )
            
            data = insights_response.get("data", [])
            
            # Analyze auction competitiveness
            if data:
                first_entry = data[0]
                auction_competitiveness = first_entry.get("auction_competitiveness", "unknown")
                auction_overlap = first_entry.get("auction_overlap", {})
                cpm = float(first_entry.get("cpm", 0))
                cpp = float(first_entry.get("cpp", 0))
                int(first_entry.get("reach", 0))
                float(first_entry.get("frequency", 0))
                
                # Calculate competitive pressure score (0-100)
                competitive_pressure = self._calculate_competitive_pressure(
                    auction_competitiveness, cpm, cpp
                )
                
                # Estimate number of competitors
                competitor_count = self._estimate_competitor_count(
                    auction_overlap, competitive_pressure
                )
                
                # Provide recommendations
                recommendations = self._generate_auction_recommendations(
                    competitive_pressure, cpm, cpp, request.metrics_of_interest
                )
                
                return AuctionInsightsResponse(
                    auction_competitiveness=auction_competitiveness,
                    competitive_pressure_score=competitive_pressure,
                    estimated_competitor_count=competitor_count,
                    avg_cpm=cpm,
                    avg_cpp=cpp,
                    auction_overlap_data=auction_overlap,
                    recommendations=recommendations,
                    market_conditions="high_competition" if competitive_pressure > 70 else "moderate_competition" if competitive_pressure > 40 else "low_competition",
                    time_range=request.time_range,
                    generated_at=datetime.utcnow()
                )
            else:
                return AuctionInsightsResponse(
                    auction_competitiveness="unknown",
                    competitive_pressure_score=0,
                    estimated_competitor_count=0,
                    avg_cpm=0,
                    avg_cpp=0,
                    auction_overlap_data={},
                    recommendations=["Unable to retrieve auction data - ensure ad set is active"],
                    market_conditions="unknown",
                    time_range=request.time_range,
                    generated_at=datetime.utcnow()
                )
            
        except Exception as e:
            logger.error(f"Error getting auction insights: {e}")
            raise
    
    async def estimate_market_saturation(
        self, 
        request: MarketSaturationRequest
    ) -> MarketSaturationResponse:
        """Estimate market saturation"""
        try:
            logger.info(f"Estimating market saturation for {request.ad_account_id}")
            
            # Get reach data over time
            insights_response = await self.client.graph_request(
                method="GET",
                path=f"act_{request.ad_account_id}/insights",
                params={
                    "fields": "reach,frequency,impressions,spend",
                    "time_range": request.time_range,
                    "time_increment": "1",
                    "level": "account"
                }
            )
            
            data = insights_response.get("data", [])
            
            # Analyze reach and frequency trends
            reach_trend = []
            frequency_trend = []
            
            for entry in data:
                reach_trend.append(int(entry.get("reach", 0)))
                frequency_trend.append(float(entry.get("frequency", 0)))
            
            # Calculate saturation metrics
            if len(reach_trend) > 7:
                reach_growth = self._calculate_growth_rate(reach_trend)
                frequency_growth = self._calculate_growth_rate(frequency_trend)
                
                # Estimate saturation level (0-100%)
                saturation_level = self._estimate_saturation(
                    reach_growth, frequency_growth, request.target_audience_size
                )
                
                # Calculate days until saturation
                days_until_saturation = self._estimate_days_until_saturation(
                    saturation_level, reach_growth
                )
                
                # Generate expansion recommendations
                expansion_recommendations = self._generate_expansion_recommendations(
                    saturation_level, request.expansion_options
                )
                
                return MarketSaturationResponse(
                    saturation_level=saturation_level,
                    current_reach=reach_trend[-1],
                    reach_growth_rate=reach_growth,
                    frequency_trend=frequency_growth,
                    days_until_saturation=days_until_saturation,
                    expansion_recommendations=expansion_recommendations,
                    market_size_estimate=request.target_audience_size,
                    penetration_percentage=(reach_trend[-1] / request.target_audience_size * 100) if request.target_audience_size > 0 else 0,
                    time_range=request.time_range,
                    generated_at=datetime.utcnow()
                )
            else:
                return MarketSaturationResponse(
                    saturation_level=0,
                    current_reach=0,
                    reach_growth_rate=0,
                    frequency_trend=0,
                    days_until_saturation=None,
                    expansion_recommendations=["Need at least 7 days of data for accurate saturation analysis"],
                    market_size_estimate=request.target_audience_size,
                    penetration_percentage=0,
                    time_range=request.time_range,
                    generated_at=datetime.utcnow()
                )
            
        except Exception as e:
            logger.error(f"Error estimating market saturation: {e}")
            raise
    
    async def estimate_share_of_voice(
        self, 
        request: ShareOfVoiceRequest
    ) -> ShareOfVoiceResponse:
        """Estimate share of voice in target market"""
        try:
            logger.info(f"Estimating share of voice for {request.ad_account_id}")
            
            # Get impression and reach data
            insights_response = await self.client.graph_request(
                method="GET",
                path=f"act_{request.ad_account_id}/insights",
                params={
                    "fields": "impressions,reach,spend,frequency",
                    "time_range": request.time_range,
                    "level": "account"
                }
            )
            
            data = insights_response.get("data", [])
            
            if data:
                first_entry = data[0]
                impressions = int(first_entry.get("impressions", 0))
                reach = int(first_entry.get("reach", 0))
                spend = float(first_entry.get("spend", 0))
                float(first_entry.get("frequency", 0))
                
                # Estimate share of voice based on reach and market size
                estimated_sov = (reach / request.market_size * 100) if request.market_size > 0 else 0
                
                # Estimate competitive spend (based on industry benchmarks)
                estimated_competitive_spend = self._estimate_competitive_spend(
                    spend, estimated_sov, request.industry_vertical
                )
                
                # Calculate relative position
                relative_position = self._calculate_relative_position(
                    estimated_sov, spend, estimated_competitive_spend
                )
                
                # Generate growth recommendations
                growth_recommendations = self._generate_sov_recommendations(
                    estimated_sov, relative_position, request.target_sov
                )
                
                return ShareOfVoiceResponse(
                    estimated_share_of_voice=estimated_sov,
                    impressions=impressions,
                    reach=reach,
                    spend=spend,
                    estimated_competitive_spend=estimated_competitive_spend,
                    relative_position=relative_position,
                    target_sov=request.target_sov,
                    gap_to_target=request.target_sov - estimated_sov if request.target_sov else None,
                    growth_recommendations=growth_recommendations,
                    time_range=request.time_range,
                    generated_at=datetime.utcnow()
                )
            else:
                return ShareOfVoiceResponse(
                    estimated_share_of_voice=0,
                    impressions=0,
                    reach=0,
                    spend=0,
                    estimated_competitive_spend=0,
                    relative_position="unknown",
                    target_sov=request.target_sov,
                    gap_to_target=request.target_sov if request.target_sov else None,
                    growth_recommendations=["Unable to retrieve account data"],
                    time_range=request.time_range,
                    generated_at=datetime.utcnow()
                )
            
        except Exception as e:
            logger.error(f"Error estimating share of voice: {e}")
            raise
    
    def _calculate_competitive_pressure(
        self, 
        auction_competitiveness: str, 
        cpm: float, 
        cpp: float
    ) -> float:
        """Calculate competitive pressure score"""
        # Map auction competitiveness to base score
        base_scores = {
            "low": 20,
            "medium": 50,
            "high": 80,
            "very_high": 95,
            "unknown": 50
        }
        
        base_score = base_scores.get(auction_competitiveness, 50)
        
        # Adjust based on CPM (higher CPM = more competition)
        # Industry average CPM is around $10
        cpm_factor = min((cpm / 10) * 10, 20) if cpm > 0 else 0
        
        return min(base_score + cpm_factor, 100)
    
    def _estimate_competitor_count(
        self, 
        auction_overlap: Dict[str, Any], 
        competitive_pressure: float
    ) -> int:
        """Estimate number of competitors"""
        # Rough estimation based on competitive pressure
        if competitive_pressure > 80:
            return 100  # High competition
        elif competitive_pressure > 60:
            return 50
        elif competitive_pressure > 40:
            return 20
        else:
            return 10
    
    def _generate_auction_recommendations(
        self, 
        competitive_pressure: float, 
        cpm: float, 
        cpp: float,
        metrics_of_interest: List[str]
    ) -> List[str]:
        """Generate recommendations based on auction analysis"""
        recommendations = []
        
        if competitive_pressure > 70:
            recommendations.append("High competitive pressure detected - consider expanding targeting or testing new audiences")
            recommendations.append("Consider bid cap strategies to control costs in competitive auction")
        
        if cpm > 15:
            recommendations.append(f"CPM is high (${cpm:.2f}) - review targeting breadth and creative quality")
        
        if "cost_efficiency" in metrics_of_interest and competitive_pressure > 60:
            recommendations.append("Focus on improving ad relevance to reduce costs in competitive auction")
        
        if "reach" in metrics_of_interest:
            recommendations.append("Consider dayparting to avoid peak competition hours")
        
        return recommendations if recommendations else ["Auction conditions are favorable - maintain current strategy"]
    
    def _calculate_growth_rate(self, trend: List[float]) -> float:
        """Calculate growth rate from trend"""
        if len(trend) < 2:
            return 0
        
        # Simple linear growth calculation
        first_half = sum(trend[:len(trend)//2]) / (len(trend)//2)
        second_half = sum(trend[len(trend)//2:]) / (len(trend) - len(trend)//2)
        
        if first_half == 0:
            return 0
        
        return ((second_half - first_half) / first_half) * 100
    
    def _estimate_saturation(
        self, 
        reach_growth: float, 
        frequency_growth: float, 
        target_audience_size: int
    ) -> float:
        """Estimate saturation level"""
        # Declining reach growth and increasing frequency = saturation
        if reach_growth < 5 and frequency_growth > 10:
            return 80  # High saturation
        elif reach_growth < 10 and frequency_growth > 5:
            return 60  # Medium saturation
        elif reach_growth < 20:
            return 40  # Low saturation
        else:
            return 20  # Minimal saturation
    
    def _estimate_days_until_saturation(
        self, 
        saturation_level: float, 
        reach_growth: float
    ) -> Optional[int]:
        """Estimate days until market saturation"""
        if saturation_level > 70:
            return 0  # Already saturated
        
        if reach_growth <= 0:
            return None  # Can't estimate
        
        # Simple projection
        days_remaining = int((100 - saturation_level) / (reach_growth / 7))
        return max(days_remaining, 1)
    
    def _generate_expansion_recommendations(
        self, 
        saturation_level: float, 
        expansion_options: List[str]
    ) -> List[str]:
        """Generate expansion recommendations"""
        recommendations = []
        
        if saturation_level > 60:
            recommendations.append("Market saturation approaching - expansion recommended")
            
            if "lookalike" in expansion_options:
                recommendations.append("Create lookalike audiences to expand reach")
            if "interests" in expansion_options:
                recommendations.append("Test new interest-based targeting")
            if "geographic" in expansion_options:
                recommendations.append("Consider geographic expansion to new regions")
            if "demographics" in expansion_options:
                recommendations.append("Expand age ranges or demographic targeting")
        elif saturation_level > 40:
            recommendations.append("Moderate saturation - begin planning expansion")
        else:
            recommendations.append("Low saturation - focus on current audience optimization")
        
        return recommendations
    
    def _estimate_competitive_spend(
        self, 
        own_spend: float, 
        estimated_sov: float, 
        industry_vertical: str
    ) -> float:
        """Estimate total competitive spend"""
        # Rough estimation: if we have X% SOV with $Y spend, total market is ~$Y / (X/100)
        if estimated_sov > 0:
            total_market_spend = own_spend / (estimated_sov / 100)
            competitive_spend = total_market_spend - own_spend
            return max(competitive_spend, 0)
        return 0
    
    def _calculate_relative_position(
        self, 
        estimated_sov: float, 
        own_spend: float, 
        competitive_spend: float
    ) -> str:
        """Calculate relative competitive position"""
        if estimated_sov > 40:
            return "market_leader"
        elif estimated_sov > 20:
            return "strong_player"
        elif estimated_sov > 10:
            return "moderate_player"
        else:
            return "challenger"
    
    def _generate_sov_recommendations(
        self, 
        estimated_sov: float, 
        relative_position: str, 
        target_sov: Optional[float]
    ) -> List[str]:
        """Generate share of voice recommendations"""
        recommendations = []
        
        if target_sov and estimated_sov < target_sov:
            gap = target_sov - estimated_sov
            recommendations.append(f"To reach target SOV of {target_sov}%, increase reach by {gap:.1f} percentage points")
            
            # Estimate budget increase needed
            budget_increase = gap * 2  # Rough 2:1 ratio
            recommendations.append(f"Estimated budget increase needed: ~{budget_increase:.0f}%")
        
        if relative_position == "challenger":
            recommendations.append("Focus on niche targeting and efficient acquisition to grow SOV")
        elif relative_position == "moderate_player":
            recommendations.append("Invest in brand building and audience expansion to increase SOV")
        elif relative_position == "strong_player":
            recommendations.append("Maintain current strategy and defend market position")
        else:  # market_leader
            recommendations.append("Leverage market leadership position - focus on customer retention and lifetime value")
        
        return recommendations


# Global competitive intelligence engine instance
_competitive_engine: Optional[CompetitiveIntelligenceEngine] = None


def get_competitive_engine() -> CompetitiveIntelligenceEngine:
    """Get the global CompetitiveIntelligenceEngine instance"""
    global _competitive_engine
    if _competitive_engine is None:
        from .meta_client import get_client
        _competitive_engine = CompetitiveIntelligenceEngine(get_client())
    return _competitive_engine

