"""Campaigns Utility Functions

This module provides utility functions for the Adtlas Campaigns module:
- Campaign creation and management
- Campaign scheduling and automation
- Budget management and optimization
- Performance tracking and analytics
- Campaign validation and compliance
- Ad placement optimization
- Audience targeting utilities
- Campaign reporting and insights

Usage:
    from apps.campaigns.utils import (
        create_campaign,
        validate_campaign_budget,
        schedule_campaign,
        optimize_ad_placement,
        calculate_campaign_metrics,
        generate_campaign_report,
        check_campaign_compliance,
    )

Features:
    - Automated campaign optimization
    - Real-time budget monitoring
    - Performance analytics
    - Compliance checking
    - Audience targeting
    - A/B testing support
"""

import json
import uuid
from datetime import datetime, timedelta
from decimal import Decimal, ROUND_HALF_UP
from typing import Any, Dict, List, Optional, Tuple, Union

from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.cache import cache
from django.core.exceptions import ValidationError
from django.db import transaction
from django.db.models import Q, Sum, Avg, Count, F
from django.utils import timezone
from django.utils.text import slugify

from apps.core.logging import get_logger, log_user_action
from apps.core.utils import (
    generate_unique_id, validate_email, sanitize_input,
    calculate_percentage, format_currency
)

User = get_user_model()
logger = get_logger('campaigns.utils')


# =============================================================================
# Campaign Creation and Management
# =============================================================================

def create_campaign(campaign_data: Dict[str, Any], user: User) -> Dict[str, Any]:
    """
    Create a new advertising campaign.
    
    Args:
        campaign_data: Dictionary containing campaign information
        user: User creating the campaign
        
    Returns:
        Dictionary with campaign creation result
    """
    try:
        with transaction.atomic():
            # Validate required fields
            required_fields = ['name', 'budget', 'start_date', 'end_date', 'objective']
            for field in required_fields:
                if field not in campaign_data or not campaign_data[field]:
                    raise ValidationError(f"Field '{field}' is required")
            
            # Sanitize and validate data
            name = sanitize_input(campaign_data['name'], 100)
            description = sanitize_input(campaign_data.get('description', ''), 500)
            budget = Decimal(str(campaign_data['budget']))
            start_date = campaign_data['start_date']
            end_date = campaign_data['end_date']
            objective = campaign_data['objective']
            
            # Validate budget
            budget_validation = validate_campaign_budget(budget, start_date, end_date)
            if not budget_validation['valid']:
                raise ValidationError(budget_validation['message'])
            
            # Validate dates
            if start_date >= end_date:
                raise ValidationError("End date must be after start date")
            
            if start_date < timezone.now().date():
                raise ValidationError("Start date cannot be in the past")
            
            # Generate unique campaign ID
            campaign_id = generate_unique_id('CAMP')
            
            # Create campaign slug
            base_slug = slugify(name)
            campaign_slug = f"{base_slug}-{campaign_id.lower()}"
            
            # Prepare campaign data
            campaign_info = {
                'id': campaign_id,
                'name': name,
                'slug': campaign_slug,
                'description': description,
                'budget': budget,
                'daily_budget': calculate_daily_budget(budget, start_date, end_date),
                'start_date': start_date,
                'end_date': end_date,
                'objective': objective,
                'status': 'draft',
                'created_by': user.id,
                'created_at': timezone.now(),
                'targeting': campaign_data.get('targeting', {}),
                'settings': campaign_data.get('settings', {}),
            }
            
            # Log campaign creation
            log_user_action(
                user=user,
                action='campaign_created',
                object_type='Campaign',
                object_id=campaign_id,
                changes={
                    'name': name,
                    'budget': str(budget),
                    'objective': objective
                }
            )
            
            logger.info(f"Campaign created: {campaign_id} by user {user.username}")
            
            return {
                'success': True,
                'campaign': campaign_info,
                'message': 'Campaign created successfully'
            }
            
    except Exception as e:
        logger.error(f"Failed to create campaign: {str(e)}")
        return {
            'success': False,
            'error': str(e),
            'message': 'Failed to create campaign'
        }


def update_campaign(campaign_id: str, update_data: Dict[str, Any], user: User) -> Dict[str, Any]:
    """
    Update an existing campaign.
    
    Args:
        campaign_id: Campaign ID to update
        update_data: Dictionary containing update information
        user: User updating the campaign
        
    Returns:
        Dictionary with update result
    """
    try:
        with transaction.atomic():
            # Get campaign (this would normally be from database)
            # For now, we'll simulate campaign retrieval
            
            changes = {}
            allowed_fields = [
                'name', 'description', 'budget', 'daily_budget',
                'start_date', 'end_date', 'targeting', 'settings'
            ]
            
            for field in allowed_fields:
                if field in update_data:
                    if field in ['name', 'description']:
                        new_value = sanitize_input(str(update_data[field]), 500)
                    elif field == 'budget':
                        new_value = Decimal(str(update_data[field]))
                        # Validate budget
                        budget_validation = validate_campaign_budget(
                            new_value, 
                            update_data.get('start_date'),
                            update_data.get('end_date')
                        )
                        if not budget_validation['valid']:
                            raise ValidationError(budget_validation['message'])
                    else:
                        new_value = update_data[field]
                    
                    changes[field] = new_value
            
            # Log campaign update
            log_user_action(
                user=user,
                action='campaign_updated',
                object_type='Campaign',
                object_id=campaign_id,
                changes=changes
            )
            
            logger.info(f"Campaign updated: {campaign_id} by user {user.username}")
            
            return {
                'success': True,
                'changes': changes,
                'message': 'Campaign updated successfully'
            }
            
    except Exception as e:
        logger.error(f"Failed to update campaign {campaign_id}: {str(e)}")
        return {
            'success': False,
            'error': str(e),
            'message': 'Failed to update campaign'
        }


def delete_campaign(campaign_id: str, user: User) -> Dict[str, Any]:
    """
    Delete a campaign (soft delete).
    
    Args:
        campaign_id: Campaign ID to delete
        user: User deleting the campaign
        
    Returns:
        Dictionary with deletion result
    """
    try:
        with transaction.atomic():
            # Check if campaign can be deleted
            # (e.g., not currently running, no active ads)
            
            # Log campaign deletion
            log_user_action(
                user=user,
                action='campaign_deleted',
                object_type='Campaign',
                object_id=campaign_id
            )
            
            logger.info(f"Campaign deleted: {campaign_id} by user {user.username}")
            
            return {
                'success': True,
                'message': 'Campaign deleted successfully'
            }
            
    except Exception as e:
        logger.error(f"Failed to delete campaign {campaign_id}: {str(e)}")
        return {
            'success': False,
            'error': str(e),
            'message': 'Failed to delete campaign'
        }


# =============================================================================
# Budget Management
# =============================================================================

def validate_campaign_budget(budget: Decimal, start_date: datetime = None, 
                           end_date: datetime = None) -> Dict[str, Any]:
    """
    Validate campaign budget constraints.
    
    Args:
        budget: Campaign budget amount
        start_date: Campaign start date
        end_date: Campaign end date
        
    Returns:
        Dictionary with validation result
    """
    try:
        # Minimum budget check
        min_budget = Decimal(getattr(settings, 'MIN_CAMPAIGN_BUDGET', '10.00'))
        if budget < min_budget:
            return {
                'valid': False,
                'message': f'Budget must be at least {format_currency(min_budget)}'
            }
        
        # Maximum budget check
        max_budget = Decimal(getattr(settings, 'MAX_CAMPAIGN_BUDGET', '100000.00'))
        if budget > max_budget:
            return {
                'valid': False,
                'message': f'Budget cannot exceed {format_currency(max_budget)}'
            }
        
        # Daily budget validation
        if start_date and end_date:
            duration = (end_date - start_date).days + 1
            daily_budget = budget / duration
            min_daily_budget = Decimal(getattr(settings, 'MIN_DAILY_BUDGET', '1.00'))
            
            if daily_budget < min_daily_budget:
                return {
                    'valid': False,
                    'message': f'Daily budget ({format_currency(daily_budget)}) is below minimum ({format_currency(min_daily_budget)})'
                }
        
        return {
            'valid': True,
            'message': 'Budget is valid'
        }
        
    except Exception as e:
        logger.error(f"Budget validation error: {str(e)}")
        return {
            'valid': False,
            'message': 'Budget validation failed'
        }


def calculate_daily_budget(total_budget: Decimal, start_date: datetime, 
                          end_date: datetime) -> Decimal:
    """
    Calculate daily budget based on total budget and campaign duration.
    
    Args:
        total_budget: Total campaign budget
        start_date: Campaign start date
        end_date: Campaign end date
        
    Returns:
        Daily budget amount
    """
    try:
        duration = (end_date - start_date).days + 1
        daily_budget = total_budget / duration
        
        # Round to 2 decimal places
        return daily_budget.quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
        
    except Exception as e:
        logger.error(f"Daily budget calculation error: {str(e)}")
        return Decimal('0.00')


def track_budget_spending(campaign_id: str, amount: Decimal, 
                         transaction_type: str = 'spend') -> Dict[str, Any]:
    """
    Track budget spending for a campaign.
    
    Args:
        campaign_id: Campaign ID
        amount: Amount spent or refunded
        transaction_type: Type of transaction (spend, refund, adjustment)
        
    Returns:
        Dictionary with tracking result
    """
    try:
        cache_key = f"campaign_spending_{campaign_id}"
        current_spending = cache.get(cache_key, Decimal('0.00'))
        
        if transaction_type == 'spend':
            new_spending = current_spending + amount
        elif transaction_type == 'refund':
            new_spending = max(Decimal('0.00'), current_spending - amount)
        else:  # adjustment
            new_spending = current_spending + amount
        
        # Update cache
        cache.set(cache_key, new_spending, 86400)  # 24 hours
        
        # Log spending transaction
        logger.info(f"Budget {transaction_type}: {campaign_id} - {format_currency(amount)}")
        
        return {
            'success': True,
            'previous_spending': current_spending,
            'new_spending': new_spending,
            'transaction_amount': amount,
            'transaction_type': transaction_type
        }
        
    except Exception as e:
        logger.error(f"Budget tracking error for {campaign_id}: {str(e)}")
        return {
            'success': False,
            'error': str(e)
        }


def check_budget_alerts(campaign_id: str, total_budget: Decimal) -> List[Dict[str, Any]]:
    """
    Check for budget alerts and warnings.
    
    Args:
        campaign_id: Campaign ID
        total_budget: Total campaign budget
        
    Returns:
        List of alert dictionaries
    """
    alerts = []
    
    try:
        cache_key = f"campaign_spending_{campaign_id}"
        current_spending = cache.get(cache_key, Decimal('0.00'))
        
        spending_percentage = (current_spending / total_budget) * 100
        
        # Define alert thresholds
        thresholds = [
            {'level': 50, 'type': 'info', 'message': 'Campaign has spent 50% of budget'},
            {'level': 75, 'type': 'warning', 'message': 'Campaign has spent 75% of budget'},
            {'level': 90, 'type': 'danger', 'message': 'Campaign has spent 90% of budget'},
            {'level': 100, 'type': 'critical', 'message': 'Campaign budget exhausted'}
        ]
        
        for threshold in thresholds:
            if spending_percentage >= threshold['level']:
                # Check if alert was already sent
                alert_key = f"budget_alert_{campaign_id}_{threshold['level']}"
                if not cache.get(alert_key):
                    alerts.append({
                        'campaign_id': campaign_id,
                        'type': threshold['type'],
                        'level': threshold['level'],
                        'message': threshold['message'],
                        'spending_percentage': float(spending_percentage),
                        'current_spending': current_spending,
                        'total_budget': total_budget,
                        'timestamp': timezone.now()
                    })
                    
                    # Mark alert as sent
                    cache.set(alert_key, True, 86400)  # 24 hours
        
        return alerts
        
    except Exception as e:
        logger.error(f"Budget alert check error for {campaign_id}: {str(e)}")
        return []


# =============================================================================
# Campaign Scheduling
# =============================================================================

def schedule_campaign(campaign_id: str, schedule_data: Dict[str, Any]) -> Dict[str, Any]:
    """
    Schedule campaign activation and deactivation.
    
    Args:
        campaign_id: Campaign ID
        schedule_data: Scheduling information
        
    Returns:
        Dictionary with scheduling result
    """
    try:
        start_time = schedule_data.get('start_time')
        end_time = schedule_data.get('end_time')
        timezone_name = schedule_data.get('timezone', 'UTC')
        
        # Validate scheduling data
        if start_time and start_time <= timezone.now():
            return {
                'success': False,
                'message': 'Start time must be in the future'
            }
        
        if end_time and start_time and end_time <= start_time:
            return {
                'success': False,
                'message': 'End time must be after start time'
            }
        
        # Store scheduling information
        schedule_info = {
            'campaign_id': campaign_id,
            'start_time': start_time.isoformat() if start_time else None,
            'end_time': end_time.isoformat() if end_time else None,
            'timezone': timezone_name,
            'created_at': timezone.now().isoformat()
        }
        
        cache_key = f"campaign_schedule_{campaign_id}"
        cache.set(cache_key, schedule_info, 86400 * 30)  # 30 days
        
        logger.info(f"Campaign scheduled: {campaign_id}")
        
        return {
            'success': True,
            'schedule': schedule_info,
            'message': 'Campaign scheduled successfully'
        }
        
    except Exception as e:
        logger.error(f"Campaign scheduling error for {campaign_id}: {str(e)}")
        return {
            'success': False,
            'error': str(e),
            'message': 'Failed to schedule campaign'
        }


def check_scheduled_campaigns() -> List[Dict[str, Any]]:
    """
    Check for campaigns that need to be activated or deactivated.
    
    Returns:
        List of campaigns requiring status changes
    """
    actions_needed = []
    
    try:
        # This would normally query the database for scheduled campaigns
        # For now, we'll simulate checking cache entries
        
        current_time = timezone.now()
        
        # In a real implementation, you would:
        # 1. Query campaigns with scheduled start/end times
        # 2. Check if current time matches scheduled times
        # 3. Return list of actions needed
        
        logger.info("Checked scheduled campaigns")
        
        return actions_needed
        
    except Exception as e:
        logger.error(f"Error checking scheduled campaigns: {str(e)}")
        return []


# =============================================================================
# Performance Analytics
# =============================================================================

def calculate_campaign_metrics(campaign_id: str, date_range: Dict[str, datetime] = None) -> Dict[str, Any]:
    """
    Calculate campaign performance metrics.
    
    Args:
        campaign_id: Campaign ID
        date_range: Optional date range for metrics
        
    Returns:
        Dictionary with campaign metrics
    """
    try:
        # Default to last 30 days if no date range provided
        if not date_range:
            end_date = timezone.now()
            start_date = end_date - timedelta(days=30)
            date_range = {'start': start_date, 'end': end_date}
        
        # Simulate metrics calculation
        # In a real implementation, this would query analytics data
        
        metrics = {
            'campaign_id': campaign_id,
            'date_range': {
                'start': date_range['start'].isoformat(),
                'end': date_range['end'].isoformat()
            },
            'impressions': 0,
            'clicks': 0,
            'conversions': 0,
            'spend': Decimal('0.00'),
            'ctr': Decimal('0.00'),  # Click-through rate
            'cpc': Decimal('0.00'),  # Cost per click
            'cpm': Decimal('0.00'),  # Cost per mille
            'conversion_rate': Decimal('0.00'),
            'cost_per_conversion': Decimal('0.00'),
            'roas': Decimal('0.00'),  # Return on ad spend
            'calculated_at': timezone.now().isoformat()
        }
        
        # Calculate derived metrics
        if metrics['impressions'] > 0:
            metrics['ctr'] = (Decimal(metrics['clicks']) / Decimal(metrics['impressions'])) * 100
            metrics['cpm'] = (metrics['spend'] / Decimal(metrics['impressions'])) * 1000
        
        if metrics['clicks'] > 0:
            metrics['cpc'] = metrics['spend'] / Decimal(metrics['clicks'])
            metrics['conversion_rate'] = (Decimal(metrics['conversions']) / Decimal(metrics['clicks'])) * 100
        
        if metrics['conversions'] > 0:
            metrics['cost_per_conversion'] = metrics['spend'] / Decimal(metrics['conversions'])
        
        logger.info(f"Calculated metrics for campaign: {campaign_id}")
        
        return {
            'success': True,
            'metrics': metrics
        }
        
    except Exception as e:
        logger.error(f"Metrics calculation error for {campaign_id}: {str(e)}")
        return {
            'success': False,
            'error': str(e)
        }


def generate_campaign_report(campaign_id: str, report_type: str = 'summary', 
                           date_range: Dict[str, datetime] = None) -> Dict[str, Any]:
    """
    Generate campaign performance report.
    
    Args:
        campaign_id: Campaign ID
        report_type: Type of report (summary, detailed, comparison)
        date_range: Date range for the report
        
    Returns:
        Dictionary with report data
    """
    try:
        # Get campaign metrics
        metrics_result = calculate_campaign_metrics(campaign_id, date_range)
        
        if not metrics_result['success']:
            return metrics_result
        
        metrics = metrics_result['metrics']
        
        # Generate report based on type
        if report_type == 'summary':
            report = {
                'type': 'summary',
                'campaign_id': campaign_id,
                'summary': {
                    'total_impressions': metrics['impressions'],
                    'total_clicks': metrics['clicks'],
                    'total_conversions': metrics['conversions'],
                    'total_spend': metrics['spend'],
                    'average_ctr': metrics['ctr'],
                    'average_cpc': metrics['cpc'],
                    'conversion_rate': metrics['conversion_rate']
                },
                'insights': generate_campaign_insights(metrics),
                'recommendations': generate_campaign_recommendations(metrics)
            }
        
        elif report_type == 'detailed':
            report = {
                'type': 'detailed',
                'campaign_id': campaign_id,
                'metrics': metrics,
                'daily_breakdown': [],  # Would contain daily metrics
                'audience_breakdown': {},  # Would contain audience segments
                'placement_breakdown': {},  # Would contain placement performance
                'insights': generate_campaign_insights(metrics),
                'recommendations': generate_campaign_recommendations(metrics)
            }
        
        else:  # comparison
            report = {
                'type': 'comparison',
                'campaign_id': campaign_id,
                'current_period': metrics,
                'previous_period': {},  # Would contain previous period metrics
                'comparison': {},  # Would contain period-over-period changes
                'insights': generate_campaign_insights(metrics)
            }
        
        report['generated_at'] = timezone.now().isoformat()
        
        logger.info(f"Generated {report_type} report for campaign: {campaign_id}")
        
        return {
            'success': True,
            'report': report
        }
        
    except Exception as e:
        logger.error(f"Report generation error for {campaign_id}: {str(e)}")
        return {
            'success': False,
            'error': str(e)
        }


def generate_campaign_insights(metrics: Dict[str, Any]) -> List[str]:
    """
    Generate insights based on campaign metrics.
    
    Args:
        metrics: Campaign metrics dictionary
        
    Returns:
        List of insight strings
    """
    insights = []
    
    try:
        # CTR insights
        ctr = float(metrics.get('ctr', 0))
        if ctr > 2.0:
            insights.append("Excellent click-through rate indicates strong ad relevance")
        elif ctr < 0.5:
            insights.append("Low click-through rate suggests need for ad optimization")
        
        # Conversion rate insights
        conversion_rate = float(metrics.get('conversion_rate', 0))
        if conversion_rate > 5.0:
            insights.append("High conversion rate shows effective targeting")
        elif conversion_rate < 1.0:
            insights.append("Low conversion rate may indicate landing page issues")
        
        # Spend insights
        spend = float(metrics.get('spend', 0))
        if spend > 0:
            if metrics.get('conversions', 0) == 0:
                insights.append("No conversions recorded - review targeting and creative")
        
        return insights
        
    except Exception as e:
        logger.error(f"Error generating insights: {str(e)}")
        return []


def generate_campaign_recommendations(metrics: Dict[str, Any]) -> List[str]:
    """
    Generate recommendations based on campaign metrics.
    
    Args:
        metrics: Campaign metrics dictionary
        
    Returns:
        List of recommendation strings
    """
    recommendations = []
    
    try:
        # CTR recommendations
        ctr = float(metrics.get('ctr', 0))
        if ctr < 1.0:
            recommendations.append("Consider testing new ad creatives to improve CTR")
            recommendations.append("Review and refine audience targeting")
        
        # CPC recommendations
        cpc = float(metrics.get('cpc', 0))
        if cpc > 2.0:
            recommendations.append("High CPC detected - consider bid optimization")
            recommendations.append("Test different ad placements to reduce costs")
        
        # Conversion recommendations
        conversion_rate = float(metrics.get('conversion_rate', 0))
        if conversion_rate < 2.0:
            recommendations.append("Optimize landing page for better conversions")
            recommendations.append("Review conversion tracking setup")
        
        return recommendations
        
    except Exception as e:
        logger.error(f"Error generating recommendations: {str(e)}")
        return []


# =============================================================================
# Campaign Optimization
# =============================================================================

def optimize_campaign_bidding(campaign_id: str, optimization_goal: str = 'conversions') -> Dict[str, Any]:
    """
    Optimize campaign bidding strategy.
    
    Args:
        campaign_id: Campaign ID
        optimization_goal: Goal for optimization (conversions, clicks, impressions)
        
    Returns:
        Dictionary with optimization results
    """
    try:
        # Get current campaign metrics
        metrics_result = calculate_campaign_metrics(campaign_id)
        
        if not metrics_result['success']:
            return metrics_result
        
        metrics = metrics_result['metrics']
        
        # Analyze performance and suggest bid adjustments
        recommendations = []
        
        if optimization_goal == 'conversions':
            conversion_rate = float(metrics.get('conversion_rate', 0))
            if conversion_rate > 3.0:
                recommendations.append("Increase bids to capture more volume")
            elif conversion_rate < 1.0:
                recommendations.append("Decrease bids to improve efficiency")
        
        elif optimization_goal == 'clicks':
            ctr = float(metrics.get('ctr', 0))
            if ctr > 2.0:
                recommendations.append("Increase bids for high-performing placements")
            elif ctr < 0.5:
                recommendations.append("Reduce bids or pause underperforming placements")
        
        optimization_result = {
            'campaign_id': campaign_id,
            'optimization_goal': optimization_goal,
            'current_metrics': metrics,
            'recommendations': recommendations,
            'optimized_at': timezone.now().isoformat()
        }
        
        logger.info(f"Optimized bidding for campaign: {campaign_id}")
        
        return {
            'success': True,
            'optimization': optimization_result
        }
        
    except Exception as e:
        logger.error(f"Bidding optimization error for {campaign_id}: {str(e)}")
        return {
            'success': False,
            'error': str(e)
        }


def optimize_ad_placement(campaign_id: str, placement_data: List[Dict[str, Any]]) -> Dict[str, Any]:
    """
    Optimize ad placement based on performance data.
    
    Args:
        campaign_id: Campaign ID
        placement_data: List of placement performance data
        
    Returns:
        Dictionary with placement optimization results
    """
    try:
        optimized_placements = []
        
        for placement in placement_data:
            placement_id = placement.get('id')
            performance = placement.get('performance', {})
            
            # Calculate placement score
            score = calculate_placement_score(performance)
            
            # Determine action based on score
            if score >= 80:
                action = 'increase_bid'
                recommendation = 'High-performing placement - increase bid'
            elif score >= 60:
                action = 'maintain'
                recommendation = 'Good performance - maintain current bid'
            elif score >= 40:
                action = 'decrease_bid'
                recommendation = 'Underperforming - decrease bid'
            else:
                action = 'pause'
                recommendation = 'Poor performance - consider pausing'
            
            optimized_placements.append({
                'placement_id': placement_id,
                'score': score,
                'action': action,
                'recommendation': recommendation,
                'performance': performance
            })
        
        optimization_result = {
            'campaign_id': campaign_id,
            'placements': optimized_placements,
            'optimized_at': timezone.now().isoformat()
        }
        
        logger.info(f"Optimized ad placement for campaign: {campaign_id}")
        
        return {
            'success': True,
            'optimization': optimization_result
        }
        
    except Exception as e:
        logger.error(f"Placement optimization error for {campaign_id}: {str(e)}")
        return {
            'success': False,
            'error': str(e)
        }


def calculate_placement_score(performance: Dict[str, Any]) -> float:
    """
    Calculate a performance score for an ad placement.
    
    Args:
        performance: Performance metrics dictionary
        
    Returns:
        Performance score (0-100)
    """
    try:
        # Weight different metrics
        weights = {
            'ctr': 0.3,
            'conversion_rate': 0.4,
            'cost_efficiency': 0.3
        }
        
        # Normalize metrics to 0-100 scale
        ctr_score = min(100, float(performance.get('ctr', 0)) * 50)  # 2% CTR = 100 points
        conversion_score = min(100, float(performance.get('conversion_rate', 0)) * 20)  # 5% conversion = 100 points
        
        # Cost efficiency (lower cost per conversion = higher score)
        cost_per_conversion = float(performance.get('cost_per_conversion', 999))
        cost_efficiency_score = max(0, 100 - (cost_per_conversion * 2))  # $50 CPC = 0 points
        
        # Calculate weighted score
        total_score = (
            ctr_score * weights['ctr'] +
            conversion_score * weights['conversion_rate'] +
            cost_efficiency_score * weights['cost_efficiency']
        )
        
        return round(total_score, 2)
        
    except Exception as e:
        logger.error(f"Error calculating placement score: {str(e)}")
        return 0.0


# =============================================================================
# Campaign Compliance
# =============================================================================

def check_campaign_compliance(campaign_data: Dict[str, Any]) -> Dict[str, Any]:
    """
    Check campaign compliance with advertising policies.
    
    Args:
        campaign_data: Campaign data to check
        
    Returns:
        Dictionary with compliance results
    """
    try:
        compliance_issues = []
        warnings = []
        
        # Check content compliance
        content_check = check_content_compliance(campaign_data)
        if content_check['issues']:
            compliance_issues.extend(content_check['issues'])
        if content_check['warnings']:
            warnings.extend(content_check['warnings'])
        
        # Check targeting compliance
        targeting_check = check_targeting_compliance(campaign_data.get('targeting', {}))
        if targeting_check['issues']:
            compliance_issues.extend(targeting_check['issues'])
        
        # Check budget compliance
        budget_check = validate_campaign_budget(
            Decimal(str(campaign_data.get('budget', 0))),
            campaign_data.get('start_date'),
            campaign_data.get('end_date')
        )
        if not budget_check['valid']:
            compliance_issues.append(f"Budget compliance: {budget_check['message']}")
        
        compliance_result = {
            'compliant': len(compliance_issues) == 0,
            'issues': compliance_issues,
            'warnings': warnings,
            'checked_at': timezone.now().isoformat()
        }
        
        logger.info(f"Compliance check completed - Issues: {len(compliance_issues)}, Warnings: {len(warnings)}")
        
        return {
            'success': True,
            'compliance': compliance_result
        }
        
    except Exception as e:
        logger.error(f"Compliance check error: {str(e)}")
        return {
            'success': False,
            'error': str(e)
        }


def check_content_compliance(campaign_data: Dict[str, Any]) -> Dict[str, List[str]]:
    """
    Check content compliance for campaign.
    
    Args:
        campaign_data: Campaign data to check
        
    Returns:
        Dictionary with issues and warnings
    """
    issues = []
    warnings = []
    
    try:
        # Check campaign name and description
        name = campaign_data.get('name', '')
        description = campaign_data.get('description', '')
        
        # Prohibited words check
        prohibited_words = [
            'guaranteed', 'free money', 'get rich quick', 'miracle',
            'instant', 'risk-free', 'no questions asked'
        ]
        
        content_to_check = f"{name} {description}".lower()
        
        for word in prohibited_words:
            if word in content_to_check:
                issues.append(f"Prohibited word detected: '{word}'")
        
        # Length checks
        if len(name) > 100:
            warnings.append("Campaign name is very long - consider shortening")
        
        if len(description) > 500:
            warnings.append("Campaign description is very long")
        
        # Special character checks
        if any(char in name for char in ['!', '@', '#', '$', '%']):
            warnings.append("Campaign name contains special characters")
        
        return {
            'issues': issues,
            'warnings': warnings
        }
        
    except Exception as e:
        logger.error(f"Content compliance check error: {str(e)}")
        return {
            'issues': ["Content compliance check failed"],
            'warnings': []
        }


def check_targeting_compliance(targeting_data: Dict[str, Any]) -> Dict[str, List[str]]:
    """
    Check targeting compliance.
    
    Args:
        targeting_data: Targeting configuration
        
    Returns:
        Dictionary with issues
    """
    issues = []
    
    try:
        # Check age targeting
        min_age = targeting_data.get('min_age')
        if min_age and min_age < 18:
            issues.append("Minimum age targeting must be 18 or older")
        
        # Check location targeting
        excluded_locations = targeting_data.get('excluded_locations', [])
        if 'restricted_region' in excluded_locations:
            issues.append("Cannot target restricted regions")
        
        # Check interest targeting
        interests = targeting_data.get('interests', [])
        prohibited_interests = ['gambling', 'adult_content', 'illegal_substances']
        
        for interest in interests:
            if interest in prohibited_interests:
                issues.append(f"Prohibited interest targeting: {interest}")
        
        return {
            'issues': issues
        }
        
    except Exception as e:
        logger.error(f"Targeting compliance check error: {str(e)}")
        return {
            'issues': ["Targeting compliance check failed"]
        }


# =============================================================================
# Export all utility functions
# =============================================================================

__all__ = [
    # Campaign Management
    'create_campaign',
    'update_campaign',
    'delete_campaign',
    
    # Budget Management
    'validate_campaign_budget',
    'calculate_daily_budget',
    'track_budget_spending',
    'check_budget_alerts',
    
    # Scheduling
    'schedule_campaign',
    'check_scheduled_campaigns',
    
    # Analytics
    'calculate_campaign_metrics',
    'generate_campaign_report',
    'generate_campaign_insights',
    'generate_campaign_recommendations',
    
    # Optimization
    'optimize_campaign_bidding',
    'optimize_ad_placement',
    'calculate_placement_score',
    
    # Compliance
    'check_campaign_compliance',
    'check_content_compliance',
    'check_targeting_compliance',
]