"""Creative Management Signal Handlers

Django signal handlers for creative asset management.
Handles automated tasks, logging, notifications, and data integrity.

Signal Categories:
- Model Signals: pre_save, post_save, pre_delete, post_delete
- File Signals: File upload, deletion, and processing
- Approval Signals: Approval workflow automation
- Performance Signals: Performance data updates
- Compliance Signals: Compliance checking automation
- Notification Signals: Email and system notifications
- Cache Signals: Cache invalidation and updates
- Audit Signals: Activity logging and tracking

Handlers:
- creative_pre_save: Pre-save processing
- creative_post_save: Post-save automation
- creative_post_delete: Cleanup after deletion
- file_upload_handler: Handle file uploads
- approval_status_changed: Handle approval changes
- performance_updated: Handle performance updates
- compliance_checked: Handle compliance results
- version_created: Handle version creation
- asset_uploaded: Handle asset uploads

Integrations:
- Celery for async tasks
- Email notifications
- Cache management
- File storage cleanup
- Audit logging
- Performance tracking
"""

from django.db.models.signals import pre_save, post_save, pre_delete, post_delete, m2m_changed
from django.dispatch import receiver
from django.core.cache import cache
from django.utils import timezone
from django.contrib.auth import get_user_model
from django.core.mail import send_mail
from django.template.loader import render_to_string
from django.conf import settings
from django.db import transaction

import logging
import os
from datetime import datetime, timedelta

from .models import (
    Creative, CreativeVersion, CreativeAsset, CreativeApproval,
    CreativePerformance, CreativeCompliance, CreativeTemplate,
    CreativeTag, CreativeVariant, CreativeFormat
)
from .tasks import (
    process_creative_file, check_compliance, generate_thumbnails,
    extract_metadata, send_approval_notification, update_performance_metrics
)
from .utils import get_file_metadata, validate_creative_file

User = get_user_model()
logger = logging.getLogger(__name__)


# Creative Model Signals
@receiver(pre_save, sender=Creative)
def creative_pre_save(sender, instance, **kwargs):
    """
    Handle creative pre-save operations.
    
    Args:
        sender: Model class
        instance: Creative instance
        **kwargs: Additional arguments
    """
    try:
        # Set updated timestamp and user
        instance.updated_at = timezone.now()
        
        # Auto-generate name if not provided
        if not instance.name and instance.primary_file:
            filename = os.path.basename(instance.primary_file.name)
            instance.name = os.path.splitext(filename)[0]
        
        # Validate creative type matches file
        if instance.primary_file and instance.creative_type:
            file_ext = os.path.splitext(instance.primary_file.name)[1].lower()
            
            type_extensions = {
                'image': ['.jpg', '.jpeg', '.png', '.gif', '.webp'],
                'video': ['.mp4', '.mov', '.avi', '.webm'],
                'audio': ['.mp3', '.wav', '.aac', '.ogg'],
                'html': ['.html', '.htm'],
                'banner': ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.html']
            }
            
            if instance.creative_type in type_extensions:
                if file_ext not in type_extensions[instance.creative_type]:
                    logger.warning(
                        f"Creative type '{instance.creative_type}' doesn't match file extension '{file_ext}' "
                        f"for creative: {instance.name}"
                    )
        
        # Set default priority if not specified
        if instance.priority is None:
            instance.priority = 3  # Medium priority
        
        # Auto-set status based on conditions
        if instance.pk is None:  # New creative
            if instance.primary_file:
                instance.creative_status = 'processing'
            else:
                instance.creative_status = 'draft'
        
        logger.info(f"Creative pre-save: {instance.name} (Status: {instance.creative_status})")
        
    except Exception as exc:
        logger.error(f"Error in creative pre-save: {str(exc)}")


@receiver(post_save, sender=Creative)
def creative_post_save(sender, instance, created, **kwargs):
    """
    Handle creative post-save operations.
    
    Args:
        sender: Model class
        instance: Creative instance
        created: Whether instance was created
        **kwargs: Additional arguments
    """
    try:
        if created:
            logger.info(f"New creative created: {instance.name} (ID: {instance.pk})")
            
            # Create initial version if file is present
            if instance.primary_file:
                CreativeVersion.objects.create(
                    creative=instance,
                    version_number=1,
                    file=instance.primary_file,
                    file_size=instance.file_size or 0,
                    changes='Initial version',
                    created_by=instance.created_by
                )
                
                # Start file processing
                transaction.on_commit(
                    lambda: process_creative_file.delay(instance.pk)
                )
            
            # Send creation notification
            if instance.created_by and instance.created_by.email:
                transaction.on_commit(
                    lambda: send_creation_notification.delay(instance.pk)
                )
        
        else:
            logger.info(f"Creative updated: {instance.name} (ID: {instance.pk})")
            
            # Check if file was changed
            if instance.tracker.has_changed('primary_file') and instance.primary_file:
                # Create new version
                version_number = instance.versions.count() + 1
                CreativeVersion.objects.create(
                    creative=instance,
                    version_number=version_number,
                    file=instance.primary_file,
                    file_size=instance.file_size or 0,
                    changes='File updated',
                    created_by=instance.updated_by
                )
                
                # Reprocess file
                transaction.on_commit(
                    lambda: process_creative_file.delay(instance.pk)
                )
            
            # Check if status changed to approved
            if (instance.tracker.has_changed('creative_status') and 
                instance.creative_status == 'approved'):
                
                # Send approval notification
                transaction.on_commit(
                    lambda: send_status_notification.delay(instance.pk, 'approved')
                )
        
        # Invalidate related caches
        cache_keys = [
            f'creative_{instance.pk}',
            f'creative_list_{instance.campaign_id}' if instance.campaign_id else None,
            'creative_stats',
            'dashboard_stats'
        ]
        
        for key in filter(None, cache_keys):
            cache.delete(key)
        
    except Exception as exc:
        logger.error(f"Error in creative post-save: {str(exc)}")


@receiver(pre_delete, sender=Creative)
def creative_pre_delete(sender, instance, **kwargs):
    """
    Handle creative pre-delete operations.
    
    Args:
        sender: Model class
        instance: Creative instance
        **kwargs: Additional arguments
    """
    try:
        logger.info(f"Creative being deleted: {instance.name} (ID: {instance.pk})")
        
        # Store file paths for cleanup
        files_to_delete = []
        
        if instance.primary_file:
            files_to_delete.append(instance.primary_file.path)
        
        if instance.thumbnail:
            files_to_delete.append(instance.thumbnail.path)
        
        # Store version files
        for version in instance.versions.all():
            if version.file:
                files_to_delete.append(version.file.path)
        
        # Store asset files
        for asset in instance.assets.all():
            if asset.file:
                files_to_delete.append(asset.file.path)
        
        # Store file paths in instance for post_delete cleanup
        instance._files_to_delete = files_to_delete
        
    except Exception as exc:
        logger.error(f"Error in creative pre-delete: {str(exc)}")


@receiver(post_delete, sender=Creative)
def creative_post_delete(sender, instance, **kwargs):
    """
    Handle creative post-delete cleanup.
    
    Args:
        sender: Model class
        instance: Creative instance
        **kwargs: Additional arguments
    """
    try:
        logger.info(f"Creative deleted: {instance.name}")
        
        # Clean up files
        files_to_delete = getattr(instance, '_files_to_delete', [])
        
        for file_path in files_to_delete:
            try:
                if os.path.exists(file_path):
                    os.remove(file_path)
                    logger.info(f"Deleted file: {file_path}")
            except Exception as exc:
                logger.warning(f"Could not delete file {file_path}: {str(exc)}")
        
        # Invalidate caches
        cache_keys = [
            f'creative_{instance.pk}',
            f'creative_list_{instance.campaign_id}' if instance.campaign_id else None,
            'creative_stats',
            'dashboard_stats'
        ]
        
        for key in filter(None, cache_keys):
            cache.delete(key)
        
    except Exception as exc:
        logger.error(f"Error in creative post-delete: {str(exc)}")


# Creative Version Signals
@receiver(post_save, sender=CreativeVersion)
def version_created(sender, instance, created, **kwargs):
    """
    Handle creative version creation.
    
    Args:
        sender: Model class
        instance: CreativeVersion instance
        created: Whether instance was created
        **kwargs: Additional arguments
    """
    try:
        if created:
            logger.info(
                f"New version created: {instance.creative.name} v{instance.version_number}"
            )
            
            # Update creative file size if this is the latest version
            if instance.version_number == instance.creative.versions.count():
                if instance.file_size:
                    instance.creative.file_size = instance.file_size
                    instance.creative.save(update_fields=['file_size'])
            
            # Extract metadata for new version
            if instance.file:
                transaction.on_commit(
                    lambda: extract_metadata.delay(instance.creative.pk)
                )
        
    except Exception as exc:
        logger.error(f"Error in version creation: {str(exc)}")


# Creative Asset Signals
@receiver(post_save, sender=CreativeAsset)
def asset_uploaded(sender, instance, created, **kwargs):
    """
    Handle creative asset upload.
    
    Args:
        sender: Model class
        instance: CreativeAsset instance
        created: Whether instance was created
        **kwargs: Additional arguments
    """
    try:
        if created and instance.file:
            logger.info(
                f"New asset uploaded: {instance.name} for creative {instance.creative.name}"
            )
            
            # Update asset file size
            if instance.file:
                instance.file_size = instance.file.size
                instance.save(update_fields=['file_size'])
            
            # Generate thumbnail for image assets
            if instance.asset_type == 'image':
                transaction.on_commit(
                    lambda: generate_thumbnails.delay(instance.creative.pk)
                )
        
    except Exception as exc:
        logger.error(f"Error in asset upload: {str(exc)}")


# Creative Approval Signals
@receiver(post_save, sender=CreativeApproval)
def approval_status_changed(sender, instance, created, **kwargs):
    """
    Handle approval status changes.
    
    Args:
        sender: Model class
        instance: CreativeApproval instance
        created: Whether instance was created
        **kwargs: Additional arguments
    """
    try:
        if created:
            logger.info(
                f"Approval created: {instance.creative.name} - {instance.approval_status}"
            )
            
            # Update creative status based on approval
            if instance.approval_status == 'approved':
                instance.creative.creative_status = 'approved'
                instance.creative.save(update_fields=['creative_status'])
                
                # Send approval notification
                transaction.on_commit(
                    lambda: send_approval_notification.delay(
                        instance.creative.pk,
                        instance.approval_status,
                        instance.reviewer.pk,
                        instance.comments
                    )
                )
            
            elif instance.approval_status == 'rejected':
                instance.creative.creative_status = 'rejected'
                instance.creative.save(update_fields=['creative_status'])
                
                # Send rejection notification
                transaction.on_commit(
                    lambda: send_approval_notification.delay(
                        instance.creative.pk,
                        instance.approval_status,
                        instance.reviewer.pk,
                        instance.comments
                    )
                )
        
    except Exception as exc:
        logger.error(f"Error in approval status change: {str(exc)}")


# Creative Performance Signals
@receiver(post_save, sender=CreativePerformance)
def performance_updated(sender, instance, created, **kwargs):
    """
    Handle performance data updates.
    
    Args:
        sender: Model class
        instance: CreativePerformance instance
        created: Whether instance was created
        **kwargs: Additional arguments
    """
    try:
        logger.info(
            f"Performance updated: {instance.creative.name} - {instance.date}"
        )
        
        # Update creative aggregate performance
        creative = instance.creative
        performance_data = creative.performance_data.aggregate(
            total_impressions=models.Sum('impressions'),
            total_clicks=models.Sum('clicks'),
            total_conversions=models.Sum('conversions')
        )
        
        creative.impressions = performance_data['total_impressions'] or 0
        creative.clicks = performance_data['total_clicks'] or 0
        creative.conversions = performance_data['total_conversions'] or 0
        creative.save(update_fields=['impressions', 'clicks', 'conversions'])
        
        # Invalidate performance caches
        cache.delete(f'creative_performance_{creative.pk}')
        cache.delete('performance_stats')
        
    except Exception as exc:
        logger.error(f"Error in performance update: {str(exc)}")


# Creative Compliance Signals
@receiver(post_save, sender=CreativeCompliance)
def compliance_checked(sender, instance, created, **kwargs):
    """
    Handle compliance check results.
    
    Args:
        sender: Model class
        instance: CreativeCompliance instance
        created: Whether instance was created
        **kwargs: Additional arguments
    """
    try:
        logger.info(
            f"Compliance checked: {instance.creative.name} - {instance.compliance_status}"
        )
        
        # Update creative compliance status
        creative = instance.creative
        creative.is_compliant = instance.compliance_status == 'compliant'
        creative.compliance_score = instance.compliance_score
        creative.save(update_fields=['is_compliant', 'compliance_score'])
        
        # Send notification if non-compliant
        if instance.compliance_status == 'non_compliant':
            transaction.on_commit(
                lambda: send_compliance_notification.delay(instance.pk)
            )
        
    except Exception as exc:
        logger.error(f"Error in compliance check: {str(exc)}")


# Creative Template Signals
@receiver(post_save, sender=CreativeTemplate)
def template_updated(sender, instance, created, **kwargs):
    """
    Handle template updates.
    
    Args:
        sender: Model class
        instance: CreativeTemplate instance
        created: Whether instance was created
        **kwargs: Additional arguments
    """
    try:
        if created:
            logger.info(f"New template created: {instance.name}")
        else:
            logger.info(f"Template updated: {instance.name}")
        
        # Invalidate template caches
        cache.delete('template_list')
        cache.delete(f'template_{instance.pk}')
        
    except Exception as exc:
        logger.error(f"Error in template update: {str(exc)}")


# Creative Tag Signals
@receiver(m2m_changed, sender=Creative.tags.through)
def creative_tags_changed(sender, instance, action, pk_set, **kwargs):
    """
    Handle creative tag changes.
    
    Args:
        sender: Through model class
        instance: Creative instance
        action: M2M action (post_add, post_remove, etc.)
        pk_set: Set of tag PKs
        **kwargs: Additional arguments
    """
    try:
        if action in ['post_add', 'post_remove']:
            logger.info(
                f"Tags {action} for creative: {instance.name} - Tags: {pk_set}"
            )
            
            # Invalidate tag-related caches
            cache.delete(f'creative_tags_{instance.pk}')
            cache.delete('tag_stats')
        
    except Exception as exc:
        logger.error(f"Error in tag change: {str(exc)}")


# Creative Variant Signals
@receiver(post_save, sender=CreativeVariant)
def variant_created(sender, instance, created, **kwargs):
    """
    Handle creative variant creation.
    
    Args:
        sender: Model class
        instance: CreativeVariant instance
        created: Whether instance was created
        **kwargs: Additional arguments
    """
    try:
        if created:
            logger.info(
                f"New variant created: {instance.variant_name} for creative {instance.creative.name}"
            )
            
            # Ensure traffic allocation doesn't exceed 100%
            total_allocation = instance.creative.variants.aggregate(
                total=models.Sum('traffic_allocation')
            )['total'] or 0
            
            if total_allocation > 100:
                logger.warning(
                    f"Traffic allocation exceeds 100% for creative {instance.creative.name}: {total_allocation}%"
                )
        
    except Exception as exc:
        logger.error(f"Error in variant creation: {str(exc)}")


# Notification Tasks - moved to tasks.py








# Cache Management
def invalidate_creative_caches(creative_id):
    """
    Invalidate all caches related to a creative.
    
    Args:
        creative_id (int): Creative ID
    """
    cache_keys = [
        f'creative_{creative_id}',
        f'creative_performance_{creative_id}',
        f'creative_tags_{creative_id}',
        'creative_list',
        'creative_stats',
        'dashboard_stats',
        'performance_stats',
        'tag_stats'
    ]
    
    for key in cache_keys:
        cache.delete(key)
    
    logger.info(f"Caches invalidated for creative: {creative_id}")


# Audit Logging
def log_creative_activity(creative, action, user=None, details=None):
    """
    Log creative activity for audit purposes.
    
    Args:
        creative (Creative): Creative instance
        action (str): Action performed
        user (User): User who performed action
        details (dict): Additional details
    """
    try:
        # This would integrate with an audit logging system
        log_entry = {
            'timestamp': timezone.now().isoformat(),
            'creative_id': creative.pk,
            'creative_name': creative.name,
            'action': action,
            'user_id': user.pk if user else None,
            'user_email': user.email if user else None,
            'details': details or {}
        }
        
        logger.info(f"Creative activity: {action} - {creative.name} by {user}")
        
        # Store in audit log (implementation depends on audit system)
        # audit_logger.info(json.dumps(log_entry))
        
    except Exception as exc:
        logger.error(f"Error logging creative activity: {str(exc)}")


# Performance Monitoring
@receiver(post_save, sender=Creative)
def monitor_creative_performance(sender, instance, created, **kwargs):
    """
    Monitor creative performance and trigger alerts.
    
    Args:
        sender: Model class
        instance: Creative instance
        created: Whether instance was created
        **kwargs: Additional arguments
    """
    try:
        if not created and instance.impressions > 0:
            # Calculate performance metrics
            ctr = (instance.clicks / instance.impressions) * 100 if instance.impressions else 0
            conversion_rate = (instance.conversions / instance.clicks) * 100 if instance.clicks else 0
            
            # Check for performance alerts
            if ctr < 0.5:  # Low CTR threshold
                logger.warning(
                    f"Low CTR alert: {instance.name} - CTR: {ctr:.2f}%"
                )
                
                # Send alert notification
                transaction.on_commit(
                    lambda: send_performance_alert.delay(instance.pk, 'low_ctr', ctr)
                )
            
            if conversion_rate < 1.0 and instance.clicks > 100:  # Low conversion rate
                logger.warning(
                    f"Low conversion rate alert: {instance.name} - Rate: {conversion_rate:.2f}%"
                )
                
                # Send alert notification
                transaction.on_commit(
                    lambda: send_performance_alert.delay(instance.pk, 'low_conversion', conversion_rate)
                )
        
    except Exception as exc:
        logger.error(f"Error monitoring creative performance: {str(exc)}")