# -*- coding: utf-8 -*-
"""
Advertisers App Signals

This module contains signal handlers for the Advertisers application.
It handles model events, business logic, notifications, and integrations
with other parts of the system.
"""

import logging
from decimal import Decimal
from django.db.models.signals import (
    pre_save, post_save, pre_delete, post_delete,
    m2m_changed
)
from django.dispatch import receiver
from django.core.mail import send_mail
from django.conf import settings
from django.utils import timezone
from django.contrib.auth import get_user_model
from django.db import transaction

from .models import (
    BrandCategory,
    Agency,
    Advertiser,
    Brand,
    AdvertiserContact,
    AgencyUser,
    AdvertiserBilling
)

# Get logger
logger = logging.getLogger(__name__)

# Get User model
User = get_user_model()


# Brand Category Signals

@receiver(pre_save, sender=BrandCategory)
def brand_category_pre_save(sender, instance, **kwargs):
    """
    Handle brand category pre-save operations.
    
    - Validate hierarchy depth
    - Prevent circular references
    - Auto-generate order if not provided
    """
    try:
        # Validate hierarchy depth (max 3 levels)
        if instance.parent:
            depth = 1
            parent = instance.parent
            while parent.parent and depth < 3:
                parent = parent.parent
                depth += 1
            
            if depth >= 3:
                raise ValueError("Brand category hierarchy cannot exceed 3 levels")
        
        # Prevent circular references
        if instance.pk and instance.parent:
            current = instance.parent
            while current:
                if current.pk == instance.pk:
                    raise ValueError("Circular reference detected in brand category hierarchy")
                current = current.parent
        
        # Auto-generate order if not provided
        if instance.order is None:
            if instance.parent:
                max_order = BrandCategory.objects.filter(
                    parent=instance.parent
                ).aggregate(max_order=models.Max('order'))['max_order'] or 0
            else:
                max_order = BrandCategory.objects.filter(
                    parent__isnull=True
                ).aggregate(max_order=models.Max('order'))['max_order'] or 0
            
            instance.order = max_order + 1
        
        logger.info(f"Brand category pre-save validation completed for: {instance.name}")
        
    except Exception as e:
        logger.error(f"Error in brand_category_pre_save: {str(e)}")
        raise


@receiver(post_save, sender=BrandCategory)
def brand_category_post_save(sender, instance, created, **kwargs):
    """
    Handle brand category post-save operations.
    
    - Log creation/update
    - Update related brand counts
    - Send notifications if needed
    """
    try:
        if created:
            logger.info(f"New brand category created: {instance.name} (ID: {instance.pk})")
            
            # Send notification to admins for new categories
            if hasattr(settings, 'NOTIFY_ADMINS_ON_CATEGORY_CREATE') and settings.NOTIFY_ADMINS_ON_CATEGORY_CREATE:
                # Implementation would depend on your notification system
                pass
        else:
            logger.info(f"Brand category updated: {instance.name} (ID: {instance.pk})")
        
        # Update parent category brand counts if needed
        if instance.parent:
            # This could trigger a cache refresh or update related statistics
            pass
            
    except Exception as e:
        logger.error(f"Error in brand_category_post_save: {str(e)}")


# Agency Signals

@receiver(pre_save, sender=Agency)
def agency_pre_save(sender, instance, **kwargs):
    """
    Handle agency pre-save operations.
    
    - Generate unique code if not provided
    - Validate commission rate
    - Normalize data
    """
    try:
        # Generate unique code if not provided
        if not instance.code:
            base_code = instance.name[:3].upper().replace(' ', '')
            counter = 1
            while Agency.objects.filter(code=f"{base_code}{counter:03d}").exists():
                counter += 1
            instance.code = f"{base_code}{counter:03d}"
        
        # Validate commission rate
        if instance.commission_rate is not None:
            if not (0 <= instance.commission_rate <= 100):
                raise ValueError("Commission rate must be between 0 and 100")
        
        # Normalize email
        if instance.email:
            instance.email = instance.email.lower().strip()
        
        # Normalize phone
        if instance.phone:
            instance.phone = ''.join(filter(str.isdigit, instance.phone))
        
        logger.info(f"Agency pre-save validation completed for: {instance.name}")
        
    except Exception as e:
        logger.error(f"Error in agency_pre_save: {str(e)}")
        raise


@receiver(post_save, sender=Agency)
def agency_post_save(sender, instance, created, **kwargs):
    """
    Handle agency post-save operations.
    
    - Log creation/update
    - Create default billing record
    - Send welcome email for new agencies
    """
    try:
        if created:
            logger.info(f"New agency created: {instance.name} (ID: {instance.pk})")
            
            # Send welcome email
            if instance.email and hasattr(settings, 'SEND_AGENCY_WELCOME_EMAIL') and settings.SEND_AGENCY_WELCOME_EMAIL:
                try:
                    send_mail(
                        subject=f'Welcome to {settings.SITE_NAME}',
                        message=f'Welcome {instance.name}! Your agency account has been created.',
                        from_email=settings.DEFAULT_FROM_EMAIL,
                        recipient_list=[instance.email],
                        fail_silently=True
                    )
                    logger.info(f"Welcome email sent to agency: {instance.email}")
                except Exception as email_error:
                    logger.error(f"Failed to send welcome email to agency {instance.email}: {str(email_error)}")
        else:
            logger.info(f"Agency updated: {instance.name} (ID: {instance.pk})")
            
    except Exception as e:
        logger.error(f"Error in agency_post_save: {str(e)}")


# Advertiser Signals

@receiver(pre_save, sender=Advertiser)
def advertiser_pre_save(sender, instance, **kwargs):
    """
    Handle advertiser pre-save operations.
    
    - Generate unique code if not provided
    - Validate agency relationship
    - Normalize data
    """
    try:
        # Generate unique code if not provided
        if not instance.code:
            base_code = instance.name[:3].upper().replace(' ', '')
            counter = 1
            while Advertiser.objects.filter(code=f"{base_code}{counter:04d}").exists():
                counter += 1
            instance.code = f"{base_code}{counter:04d}"
        
        # Validate agency relationship for agency-managed advertisers
        if instance.advertiser_type == 'agency_managed' and not instance.agency:
            raise ValueError("Agency-managed advertisers must have an associated agency")
        
        if instance.advertiser_type != 'agency_managed' and instance.agency:
            instance.agency = None  # Clear agency for non-agency-managed advertisers
        
        # Normalize email
        if instance.email:
            instance.email = instance.email.lower().strip()
        
        # Normalize phone
        if instance.phone:
            instance.phone = ''.join(filter(str.isdigit, instance.phone))
        
        # Validate credit limit
        if instance.credit_limit is not None and instance.credit_limit < 0:
            raise ValueError("Credit limit cannot be negative")
        
        logger.info(f"Advertiser pre-save validation completed for: {instance.name}")
        
    except Exception as e:
        logger.error(f"Error in advertiser_pre_save: {str(e)}")
        raise


@receiver(post_save, sender=Advertiser)
def advertiser_post_save(sender, instance, created, **kwargs):
    """
    Handle advertiser post-save operations.
    
    - Log creation/update
    - Create default billing record
    - Update agency statistics
    - Send notifications
    """
    try:
        if created:
            logger.info(f"New advertiser created: {instance.name} (ID: {instance.pk})")
            
            # Create default billing record
            with transaction.atomic():
                if not hasattr(instance, 'billing') or not instance.billing:
                    AdvertiserBilling.objects.create(
                        advertiser=instance,
                        billing_name=instance.name,
                        credit_limit=instance.credit_limit or Decimal('10000.00'),
                        current_balance=Decimal('0.00'),
                        payment_method='credit_card',
                        billing_cycle='monthly'
                    )
                    logger.info(f"Default billing record created for advertiser: {instance.name}")
            
            # Send welcome email
            if instance.email and hasattr(settings, 'SEND_ADVERTISER_WELCOME_EMAIL') and settings.SEND_ADVERTISER_WELCOME_EMAIL:
                try:
                    send_mail(
                        subject=f'Welcome to {settings.SITE_NAME}',
                        message=f'Welcome {instance.name}! Your advertiser account has been created.',
                        from_email=settings.DEFAULT_FROM_EMAIL,
                        recipient_list=[instance.email],
                        fail_silently=True
                    )
                    logger.info(f"Welcome email sent to advertiser: {instance.email}")
                except Exception as email_error:
                    logger.error(f"Failed to send welcome email to advertiser {instance.email}: {str(email_error)}")
        else:
            logger.info(f"Advertiser updated: {instance.name} (ID: {instance.pk})")
        
        # Update agency statistics if agency-managed
        if instance.agency:
            # This could trigger a cache refresh or update related statistics
            logger.info(f"Agency statistics updated for agency: {instance.agency.name}")
            
    except Exception as e:
        logger.error(f"Error in advertiser_post_save: {str(e)}")


# Brand Signals

@receiver(pre_save, sender=Brand)
def brand_pre_save(sender, instance, **kwargs):
    """
    Handle brand pre-save operations.
    
    - Generate unique code if not provided
    - Validate color codes
    - Normalize data
    """
    try:
        # Generate unique code if not provided
        if not instance.code:
            advertiser_code = instance.advertiser.code[:3] if instance.advertiser.code else 'BRD'
            base_code = instance.name[:3].upper().replace(' ', '')
            counter = 1
            while Brand.objects.filter(code=f"{advertiser_code}{base_code}{counter:03d}").exists():
                counter += 1
            instance.code = f"{advertiser_code}{base_code}{counter:03d}"
        
        # Validate color codes (hex format)
        def validate_color(color):
            if color and not color.startswith('#'):
                color = f"#{color}"
            if color and len(color) not in [4, 7]:  # #RGB or #RRGGBB
                raise ValueError(f"Invalid color format: {color}")
            return color
        
        if instance.primary_color:
            instance.primary_color = validate_color(instance.primary_color)
        
        if instance.secondary_color:
            instance.secondary_color = validate_color(instance.secondary_color)
        
        logger.info(f"Brand pre-save validation completed for: {instance.name}")
        
    except Exception as e:
        logger.error(f"Error in brand_pre_save: {str(e)}")
        raise


@receiver(post_save, sender=Brand)
def brand_post_save(sender, instance, created, **kwargs):
    """
    Handle brand post-save operations.
    
    - Log creation/update
    - Update category statistics
    - Generate brand assets if needed
    """
    try:
        if created:
            logger.info(f"New brand created: {instance.name} (ID: {instance.pk})")
            
            # Update category statistics
            if instance.category:
                logger.info(f"Category statistics updated for category: {instance.category.name}")
        else:
            logger.info(f"Brand updated: {instance.name} (ID: {instance.pk})")
            
    except Exception as e:
        logger.error(f"Error in brand_post_save: {str(e)}")


# Advertiser Contact Signals

@receiver(post_save, sender=AdvertiserContact)
def advertiser_contact_post_save(sender, instance, created, **kwargs):
    """
    Handle advertiser contact post-save operations.
    
    - Log creation/update
    - Send welcome email to new contacts
    """
    try:
        if created:
            logger.info(f"New contact created: {instance.get_full_name()} for {instance.advertiser.name}")
            
            # Send welcome email to new contacts
            if instance.email and hasattr(settings, 'SEND_CONTACT_WELCOME_EMAIL') and settings.SEND_CONTACT_WELCOME_EMAIL:
                try:
                    send_mail(
                        subject=f'Welcome to {settings.SITE_NAME}',
                        message=f'Hello {instance.get_full_name()}, you have been added as a contact for {instance.advertiser.name}.',
                        from_email=settings.DEFAULT_FROM_EMAIL,
                        recipient_list=[instance.email],
                        fail_silently=True
                    )
                    logger.info(f"Welcome email sent to contact: {instance.email}")
                except Exception as email_error:
                    logger.error(f"Failed to send welcome email to contact {instance.email}: {str(email_error)}")
        else:
            logger.info(f"Contact updated: {instance.get_full_name()} for {instance.advertiser.name}")
            
    except Exception as e:
        logger.error(f"Error in advertiser_contact_post_save: {str(e)}")


# Agency User Signals

@receiver(post_save, sender=AgencyUser)
def agency_user_post_save(sender, instance, created, **kwargs):
    """
    Handle agency user post-save operations.
    
    - Log creation/update
    - Send invitation email to new users
    - Update user permissions
    """
    try:
        if created:
            logger.info(f"New agency user created: {instance.user.get_full_name()} for {instance.agency.name}")
            
            # Send invitation email
            if hasattr(settings, 'SEND_AGENCY_USER_INVITATION') and settings.SEND_AGENCY_USER_INVITATION:
                try:
                    send_mail(
                        subject=f'Invitation to join {instance.agency.name}',
                        message=f'You have been invited to join {instance.agency.name} as a {instance.get_role_display()}.',
                        from_email=settings.DEFAULT_FROM_EMAIL,
                        recipient_list=[instance.user.email],
                        fail_silently=True
                    )
                    logger.info(f"Invitation email sent to: {instance.user.email}")
                except Exception as email_error:
                    logger.error(f"Failed to send invitation email to {instance.user.email}: {str(email_error)}")
        else:
            logger.info(f"Agency user updated: {instance.user.get_full_name()} for {instance.agency.name}")
        
        # Update user permissions based on role
        # This would integrate with your permission system
        
    except Exception as e:
        logger.error(f"Error in agency_user_post_save: {str(e)}")


# Advertiser Billing Signals

@receiver(pre_save, sender=AdvertiserBilling)
def advertiser_billing_pre_save(sender, instance, **kwargs):
    """
    Handle advertiser billing pre-save operations.
    
    - Validate credit limit and balance
    - Check for over-limit conditions
    """
    try:
        # Validate credit limit
        if instance.credit_limit is not None and instance.credit_limit < 0:
            raise ValueError("Credit limit cannot be negative")
        
        # Validate current balance
        if instance.current_balance is not None and instance.current_balance < 0:
            logger.warning(f"Negative balance detected for advertiser {instance.advertiser.name}: {instance.current_balance}")
        
        # Check for over-limit condition
        if (instance.credit_limit is not None and 
            instance.current_balance is not None and 
            instance.current_balance > instance.credit_limit):
            logger.warning(f"Over-limit condition for advertiser {instance.advertiser.name}: Balance {instance.current_balance} exceeds limit {instance.credit_limit}")
        
        logger.info(f"Billing pre-save validation completed for: {instance.advertiser.name}")
        
    except Exception as e:
        logger.error(f"Error in advertiser_billing_pre_save: {str(e)}")
        raise


@receiver(post_save, sender=AdvertiserBilling)
def advertiser_billing_post_save(sender, instance, created, **kwargs):
    """
    Handle advertiser billing post-save operations.
    
    - Log creation/update
    - Send notifications for billing events
    - Update financial statistics
    """
    try:
        if created:
            logger.info(f"New billing record created for: {instance.advertiser.name}")
        else:
            logger.info(f"Billing record updated for: {instance.advertiser.name}")
        
        # Check for low credit warning
        if (instance.credit_limit and instance.current_balance and 
            instance.current_balance > instance.credit_limit * Decimal('0.9')):
            
            logger.warning(f"Low credit warning for {instance.advertiser.name}: {instance.available_credit} remaining")
            
            # Send low credit notification
            if hasattr(settings, 'SEND_LOW_CREDIT_NOTIFICATIONS') and settings.SEND_LOW_CREDIT_NOTIFICATIONS:
                # Implementation would depend on your notification system
                pass
        
        # Check for over-limit condition
        if instance.is_over_limit:
            logger.error(f"Over-limit condition for {instance.advertiser.name}")
            
            # Send over-limit notification
            if hasattr(settings, 'SEND_OVER_LIMIT_NOTIFICATIONS') and settings.SEND_OVER_LIMIT_NOTIFICATIONS:
                # Implementation would depend on your notification system
                pass
            
    except Exception as e:
        logger.error(f"Error in advertiser_billing_post_save: {str(e)}")


# Delete Signals

@receiver(pre_delete, sender=Agency)
def agency_pre_delete(sender, instance, **kwargs):
    """
    Handle agency pre-delete operations.
    
    - Check for dependent advertisers
    - Log deletion attempt
    """
    try:
        advertiser_count = instance.advertisers.count()
        if advertiser_count > 0:
            logger.warning(f"Attempting to delete agency {instance.name} with {advertiser_count} advertisers")
        
        logger.info(f"Agency deletion initiated: {instance.name} (ID: {instance.pk})")
        
    except Exception as e:
        logger.error(f"Error in agency_pre_delete: {str(e)}")


@receiver(post_delete, sender=Agency)
def agency_post_delete(sender, instance, **kwargs):
    """
    Handle agency post-delete operations.
    
    - Log successful deletion
    - Clean up related data
    """
    try:
        logger.info(f"Agency deleted: {instance.name} (ID: {instance.pk})")
        
    except Exception as e:
        logger.error(f"Error in agency_post_delete: {str(e)}")


@receiver(pre_delete, sender=Advertiser)
def advertiser_pre_delete(sender, instance, **kwargs):
    """
    Handle advertiser pre-delete operations.
    
    - Check for dependent brands and campaigns
    - Log deletion attempt
    """
    try:
        brand_count = instance.brands.count()
        if brand_count > 0:
            logger.warning(f"Attempting to delete advertiser {instance.name} with {brand_count} brands")
        
        logger.info(f"Advertiser deletion initiated: {instance.name} (ID: {instance.pk})")
        
    except Exception as e:
        logger.error(f"Error in advertiser_pre_delete: {str(e)}")


@receiver(post_delete, sender=Advertiser)
def advertiser_post_delete(sender, instance, **kwargs):
    """
    Handle advertiser post-delete operations.
    
    - Log successful deletion
    - Update agency statistics
    """
    try:
        logger.info(f"Advertiser deleted: {instance.name} (ID: {instance.pk})")
        
        # Update agency statistics if applicable
        if instance.agency:
            logger.info(f"Agency statistics updated after advertiser deletion: {instance.agency.name}")
        
    except Exception as e:
        logger.error(f"Error in advertiser_post_delete: {str(e)}")