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

This module contains all database models for the Advertisers application.
It defines the core entities for managing advertisers, agencies, brands,
and their relationships in the Ad-tlas platform.

Models:
    BrandCategory: Hierarchical categorization system for organizing brands by industry or type.
    Agency: Advertising agencies that manage multiple advertisers and campaigns.
    Advertiser: Core entity representing both individual and agency-managed advertisers.
    Brand: Individual brands associated with advertisers for campaign targeting.
    AdvertiserContact: Contact information management for multiple contacts per advertiser.
    AgencyUser: User-agency relationship management with role-based permissions.
    AdvertiserBilling: Comprehensive billing and financial information tracking.

Key Features:
    - Hierarchical brand categorization with unlimited nesting
    - Agency-advertiser relationship management
    - Multi-contact support per advertiser
    - Role-based access control for agency users
    - Comprehensive billing and credit management
    - Audit trails and activity tracking
    - Flexible advertiser types (individual, corporate, government, etc.)

Business Rules:
    - Agency-managed advertisers must have an associated agency
    - Brand codes must be unique within each advertiser
    - Credit limits and balances are tracked for financial control
    - All entities support soft deletion and audit trails

Author: Ad-tlas Development Team
Version: 1.0.0
Last Updated: 2024
License: Proprietary
"""

import uuid
from decimal import Decimal
from typing import Optional, Dict, Any, List, Union

from django.db import models
from django.contrib.auth import get_user_model
from django.core.validators import (
    MinValueValidator, 
    MaxValueValidator, 
    RegexValidator,
    EmailValidator
)
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
from django.urls import reverse
from django.utils import timezone
from django.db.models import QuerySet

from apps.core.models import BaseModel, TimestampedModel

# Get the user model for type hints and relationships
User = get_user_model()


class BrandCategory(BaseModel):
    """
    Hierarchical brand categorization system for organizing brands by industry or type.
    
    This model provides a flexible, tree-like structure for categorizing brands,
    supporting unlimited nesting levels for complex organizational needs.
    Categories can represent industries, sub-industries, product types, or any
    other logical grouping that helps in brand management and campaign targeting.
    
    Attributes:
        name (str): Unique name of the category (max 100 chars)
        parent (BrandCategory, optional): Parent category for hierarchical organization
        description (str, optional): Detailed description of the category
        order (int): Display order for sorting (default: 0, lower numbers first)
        is_active (bool): Whether category is active and available (default: True)
    
    Properties:
        full_path (str): Complete hierarchical path (e.g., "Automotive > Luxury > SUV")
    
    Methods:
        get_descendants(): Returns all descendant categories recursively
        get_absolute_url(): Returns the URL for category detail view
    
    Business Rules:
        - Category names must be unique across the entire system
        - Circular references are prevented by model validation
        - Inactive categories are hidden from selection but preserved for data integrity
        - Deletion cascades to all subcategories (use with caution)
    
    Examples:
        >>> automotive = BrandCategory.objects.create(name="Automotive")
        >>> luxury = BrandCategory.objects.create(name="Luxury Cars", parent=automotive)
        >>> print(luxury.full_path)  # "Automotive > Luxury Cars"
        >>> descendants = automotive.get_descendants()  # All subcategories
    
    Note:
        When deleting categories with subcategories, consider the impact on
        existing brands that may be assigned to those categories.
    """
    
    # Category identification
    name = models.CharField(
        max_length=100,
        unique=True,
        verbose_name=_('Category Name'),
        help_text=_('Name of the brand category (e.g., Automotive, Food & Beverage)')
    )
    
    # Category hierarchy support
    parent = models.ForeignKey(
        'self',
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name='subcategories',
        verbose_name=_('Parent Category'),
        help_text=_('Parent category for hierarchical organization')
    )
    
    # Category description
    description = models.TextField(
        blank=True,
        verbose_name=_('Description'),
        help_text=_('Detailed description of the category')
    )
    
    # Display order for sorting
    order = models.PositiveIntegerField(
        default=0,
        verbose_name=_('Display Order'),
        help_text=_('Order for displaying categories (lower numbers first)')
    )
    
    # Status tracking
    is_active = models.BooleanField(
        default=True,
        verbose_name=_('Active'),
        help_text=_('Whether this category is active and available for use')
    )
    
    class Meta:
        verbose_name = _('Brand Category')
        verbose_name_plural = _('Brand Categories')
        ordering = ['order', 'name']
        indexes = [
            models.Index(fields=['name']),
            models.Index(fields=['parent']),
            models.Index(fields=['is_active']),
        ]
    
    def __str__(self) -> str:
        """
        String representation of the brand category.
        
        Returns:
            str: Category name with parent context if applicable
                 (e.g., "Automotive > Luxury Cars" or just "Automotive")
        """
        if self.parent:
            return f"{self.parent.name} > {self.name}"
        return self.name
    
    def get_absolute_url(self) -> str:
        """
        Get the absolute URL for this category's detail view.
        
        Returns:
            str: URL path for the category detail page
        
        Note:
            Requires 'advertisers:category_detail' URL pattern to be defined
        """
        return reverse('advertisers:category_detail', kwargs={'pk': self.pk})
    
    @property
    def full_path(self) -> str:
        """
        Get the complete hierarchical path of the category.
        
        Traverses up the parent chain to build a full path representation
        useful for display and breadcrumb navigation.
        
        Returns:
            str: Full path with ' > ' separator (e.g., "Automotive > Luxury > SUV")
        
        Performance Note:
            This property makes database queries for each parent level.
            Consider using select_related('parent') when accessing multiple categories.
        """
        path = [self.name]
        parent = self.parent
        # Traverse up the parent chain to build full path
        while parent:
            path.insert(0, parent.name)
            parent = parent.parent
        return ' > '.join(path)
    
    def get_descendants(self) -> List['BrandCategory']:
        """
        Get all descendant categories recursively.
        
        Performs a recursive traversal to collect all subcategories
        at any depth level below this category.
        
        Returns:
            List[BrandCategory]: All descendant categories in depth-first order
        
        Performance Warning:
            This method can be expensive for deep hierarchies with many subcategories.
            Consider using a tree traversal library like django-mptt for better performance
            in production environments with large category trees.
        
        Example:
            >>> automotive = BrandCategory.objects.get(name="Automotive")
            >>> all_subcategories = automotive.get_descendants()
            >>> print([cat.name for cat in all_subcategories])
            ['Luxury Cars', 'Economy Cars', 'SUV', 'Sedan', ...]
        """
        descendants = []
        # Recursively collect all subcategories
        for child in self.subcategories.all():
            descendants.append(child)
            descendants.extend(child.get_descendants())
        return descendants
    
    def clean(self) -> None:
        """
        Validate the model instance before saving.
        
        Performs custom validation to prevent circular references
        in the category hierarchy.
        
        Raises:
            ValidationError: If a circular reference is detected
        
        Business Logic:
            - Prevents setting a descendant as parent (circular reference)
            - Ensures category cannot be its own parent
        """
        super().clean()
        
        # Prevent circular references
        if self.parent:
            # Check if trying to set self as parent
            if self.parent == self:
                raise ValidationError({
                    'parent': _('A category cannot be its own parent.')
                })
            
            # Check if trying to set a descendant as parent
            if self.pk and self.parent in self.get_descendants():
                raise ValidationError({
                    'parent': _('Cannot set a descendant category as parent. This would create a circular reference.')
                })


class Agency(BaseModel):
    """
    Comprehensive advertising agency management model.
    
    This model represents advertising agencies that manage multiple advertisers
    and campaigns on behalf of their clients. It provides complete agency
    profile management including contact information, financial settings,
    and relationship tracking.
    
    Attributes:
        name (str): Official agency name (max 200 chars, unique)
        code (str): Unique agency identifier code (max 20 chars, uppercase/numbers/hyphens)
        email (str): Primary agency email address
        phone (str, optional): Primary phone number with international format validation
        address (str, optional): Physical address of the agency
        city (str, optional): City location (max 100 chars)
        country (str, optional): Country location (max 100 chars)
        website (str, optional): Agency website URL
        description (str, optional): Detailed agency description and services
        commission_rate (Decimal): Default commission rate percentage (0-100%, default 15%)
        is_active (bool): Whether agency is active and operational (default True)
        created_by (User, optional): User who created the agency record
    
    Properties:
        total_advertisers (int): Count of active advertisers managed by agency
        total_brands (int): Count of active brands across all managed advertisers
        active_campaigns_count (int): Count of currently active campaigns
    
    Methods:
        get_absolute_url(): Returns URL for agency detail view
        __str__(): Returns formatted agency name and code
    
    Business Rules:
        - Agency codes must be unique and follow uppercase alphanumeric format
        - Commission rates are validated between 0-100%
        - Email addresses must be valid and unique
        - Phone numbers follow international E.164 format
        - Inactive agencies retain data but cannot manage new campaigns
    
    Relationships:
        - One-to-many with Advertiser (agency can manage multiple advertisers)
        - One-to-many with AgencyUser (multiple users can belong to agency)
        - Many-to-one with User (created_by relationship)
    
    Examples:
        >>> agency = Agency.objects.create(
        ...     name="Ogilvy & Mather",
        ...     code="OGILVY-MAR",
        ...     email="contact@ogilvy.com",
        ...     commission_rate=Decimal('12.50')
        ... )
        >>> print(f"Agency manages {agency.total_advertisers} advertisers")
    
    Note:
        When deactivating agencies, consider the impact on active campaigns
        and ensure proper handover procedures are followed.
    """
    
    # Agency identification
    name = models.CharField(
        max_length=200,
        unique=True,
        verbose_name=_('Agency Name'),
        help_text=_('Official name of the advertising agency')
    )
    
    # Unique agency code for internal reference
    code = models.CharField(
        max_length=20,
        unique=True,
        validators=[
            RegexValidator(
                regex=r'^[A-Z0-9-_]+$',
                message=_('Code must contain only uppercase letters, numbers, hyphens, and underscores')
            )
        ],
        verbose_name=_('Agency Code'),
        help_text=_('Unique identifier code for the agency (e.g., OGILVY-MAR)')
    )
    
    # Contact information
    email = models.EmailField(
        validators=[EmailValidator()],
        verbose_name=_('Email Address'),
        help_text=_('Primary email address for the agency')
    )
    
    phone = models.CharField(
        max_length=20,
        blank=True,
        validators=[
            RegexValidator(
                regex=r'^\+?[1-9]\d{1,14}$',
                message=_('Enter a valid phone number')
            )
        ],
        verbose_name=_('Phone Number'),
        help_text=_('Primary phone number for the agency')
    )
    
    # Address information
    address = models.TextField(
        blank=True,
        verbose_name=_('Address'),
        help_text=_('Physical address of the agency')
    )
    
    city = models.CharField(
        max_length=100,
        blank=True,
        verbose_name=_('City'),
        help_text=_('City where the agency is located')
    )
    
    country = models.CharField(
        max_length=100,
        blank=True,
        verbose_name=_('Country'),
        help_text=_('Country where the agency is located')
    )
    
    # Business information
    website = models.URLField(
        blank=True,
        verbose_name=_('Website'),
        help_text=_('Agency website URL')
    )
    
    description = models.TextField(
        blank=True,
        verbose_name=_('Description'),
        help_text=_('Detailed description of the agency and its services')
    )
    
    # Financial information
    commission_rate = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        default=Decimal('15.00'),
        validators=[
            MinValueValidator(Decimal('0.00')),
            MaxValueValidator(Decimal('100.00'))
        ],
        verbose_name=_('Commission Rate (%)'),
        help_text=_('Default commission rate for this agency')
    )
    
    # Status and settings
    is_active = models.BooleanField(
        default=True,
        verbose_name=_('Active'),
        help_text=_('Whether this agency is active and can manage campaigns')
    )
    
    # Relationship tracking
    created_by = models.ForeignKey(
        User,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='created_agencies',
        verbose_name=_('Created By'),
        help_text=_('User who created this agency record')
    )
    
    class Meta:
        verbose_name = _('Agency')
        verbose_name_plural = _('Agencies')
        ordering = ['name']
        indexes = [
            models.Index(fields=['name']),
            models.Index(fields=['code']),
            models.Index(fields=['is_active']),
            models.Index(fields=['created_at']),
        ]
    
    def __str__(self) -> str:
        """
        String representation of the agency.
        
        Returns:
            str: Formatted string with agency name and code (e.g., "Ogilvy & Mather (OGILVY-MAR)")
        """
        return f"{self.name} ({self.code})"
    
    def get_absolute_url(self) -> str:
        """
        Get the absolute URL for this agency's detail view.
        
        Returns:
            str: URL path for the agency detail page
        
        Note:
            Requires 'advertisers:agency_detail' URL pattern to be defined
        """
        return reverse('advertisers:agency_detail', kwargs={'pk': self.pk})
    
    @property
    def total_advertisers(self) -> int:
        """
        Get the total number of active advertisers managed by this agency.
        
        Returns:
            int: Count of active advertisers under this agency's management
        
        Performance Note:
            This property executes a database query. Consider prefetching
            when accessing this property for multiple agencies.
        """
        return self.advertisers.filter(is_active=True).count()
    
    @property
    def total_brands(self) -> int:
        """
        Get the total number of active brands across all managed advertisers.
        
        Aggregates brand counts from all active advertisers managed by this agency.
        
        Returns:
            int: Total count of active brands across all managed advertisers
        
        Performance Warning:
            This property can be expensive for agencies with many advertisers.
            Consider using database aggregation for better performance:
            
            >>> from django.db.models import Count
            >>> agency.advertisers.filter(is_active=True).aggregate(
            ...     total_brands=Count('brands', filter=Q(brands__is_active=True))
            ... )['total_brands']
        """
        return sum(advertiser.brands.filter(is_active=True).count() 
                  for advertiser in self.advertisers.filter(is_active=True))
    
    @property
    def active_campaigns_count(self) -> int:
        """
        Get the number of currently active campaigns managed by this agency.
        
        Counts campaigns across all advertisers managed by this agency
        that have an 'active' status.
        
        Returns:
            int: Count of active campaigns across all managed advertisers
        
        Note:
            This property imports Campaign model dynamically to avoid
            circular import issues. The import is cached after first use.
        """
        from apps.campaigns.models import Campaign
        return Campaign.objects.filter(
            advertiser__agency=self,
            status='active'
        ).count()
    
    def get_commission_for_amount(self, amount: Decimal) -> Decimal:
        """
        Calculate commission amount for a given campaign spend.
        
        Args:
            amount (Decimal): The campaign spend amount
        
        Returns:
            Decimal: Commission amount based on agency's commission rate
        
        Example:
            >>> agency = Agency.objects.get(code="OGILVY-MAR")
            >>> commission = agency.get_commission_for_amount(Decimal('1000.00'))
            >>> print(f"Commission: ${commission}")  # Commission: $150.00 (if rate is 15%)
        """
        return (amount * self.commission_rate) / Decimal('100')
    
    def can_manage_advertiser(self, advertiser: 'Advertiser') -> bool:
        """
        Check if this agency can manage the specified advertiser.
        
        Args:
            advertiser (Advertiser): The advertiser to check
        
        Returns:
            bool: True if agency can manage the advertiser, False otherwise
        
        Business Logic:
            - Agency must be active
            - Advertiser must be agency-managed type
            - Advertiser must not already be managed by another agency
        """
        return (
            self.is_active and
            advertiser.advertiser_type == 'agency_managed' and
            (advertiser.agency is None or advertiser.agency == self)
        )


class Advertiser(BaseModel):
    """
    Core advertiser model representing both individual and agency-managed advertisers.
    
    This model serves as the central entity for all advertising activities,
    supporting both direct advertisers and those managed by agencies.
    """
    
    # Advertiser types
    ADVERTISER_TYPES = [
        ('individual', _('Individual Advertiser')),
        ('agency_managed', _('Agency Managed')),
        ('corporate', _('Corporate')),
        ('government', _('Government')),
        ('non_profit', _('Non-Profit')),
    ]
    
    # Advertiser identification
    name = models.CharField(
        max_length=200,
        verbose_name=_('Advertiser Name'),
        help_text=_('Official name of the advertiser or company')
    )
    
    # Unique advertiser code
    code = models.CharField(
        max_length=20,
        unique=True,
        validators=[
            RegexValidator(
                regex=r'^[A-Z0-9-_]+$',
                message=_('Code must contain only uppercase letters, numbers, hyphens, and underscores')
            )
        ],
        verbose_name=_('Advertiser Code'),
        help_text=_('Unique identifier code for the advertiser')
    )
    
    # Advertiser type and agency relationship
    advertiser_type = models.CharField(
        max_length=20,
        choices=ADVERTISER_TYPES,
        default='individual',
        verbose_name=_('Advertiser Type'),
        help_text=_('Type of advertiser (individual, agency-managed, etc.)')
    )
    
    # Optional agency relationship
    agency = models.ForeignKey(
        Agency,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='advertisers',
        verbose_name=_('Managing Agency'),
        help_text=_('Agency managing this advertiser (if applicable)')
    )
    
    # Contact information
    email = models.EmailField(
        validators=[EmailValidator()],
        verbose_name=_('Email Address'),
        help_text=_('Primary email address for the advertiser')
    )
    
    phone = models.CharField(
        max_length=20,
        blank=True,
        validators=[
            RegexValidator(
                regex=r'^\+?[1-9]\d{1,14}$',
                message=_('Enter a valid phone number')
            )
        ],
        verbose_name=_('Phone Number'),
        help_text=_('Primary phone number for the advertiser')
    )
    
    # Address information
    address = models.TextField(
        blank=True,
        verbose_name=_('Address'),
        help_text=_('Physical address of the advertiser')
    )
    
    city = models.CharField(
        max_length=100,
        blank=True,
        verbose_name=_('City'),
        help_text=_('City where the advertiser is located')
    )
    
    country = models.CharField(
        max_length=100,
        blank=True,
        verbose_name=_('Country'),
        help_text=_('Country where the advertiser is located')
    )
    
    # Business information
    website = models.URLField(
        blank=True,
        verbose_name=_('Website'),
        help_text=_('Advertiser website URL')
    )
    
    industry = models.CharField(
        max_length=100,
        blank=True,
        verbose_name=_('Industry'),
        help_text=_('Industry or business sector of the advertiser')
    )
    
    description = models.TextField(
        blank=True,
        verbose_name=_('Description'),
        help_text=_('Detailed description of the advertiser and their business')
    )
    
    # Financial information
    credit_limit = models.DecimalField(
        max_digits=12,
        decimal_places=2,
        default=Decimal('0.00'),
        validators=[MinValueValidator(Decimal('0.00'))],
        verbose_name=_('Credit Limit'),
        help_text=_('Maximum credit limit for this advertiser')
    )
    
    # Status and settings
    is_active = models.BooleanField(
        default=True,
        verbose_name=_('Active'),
        help_text=_('Whether this advertiser is active and can create campaigns')
    )
    
    # Relationship tracking
    created_by = models.ForeignKey(
        User,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='created_advertisers',
        verbose_name=_('Created By'),
        help_text=_('User who created this advertiser record')
    )
    
    class Meta:
        verbose_name = _('Advertiser')
        verbose_name_plural = _('Advertisers')
        ordering = ['name']
        indexes = [
            models.Index(fields=['name']),
            models.Index(fields=['code']),
            models.Index(fields=['advertiser_type']),
            models.Index(fields=['agency']),
            models.Index(fields=['is_active']),
            models.Index(fields=['created_at']),
        ]
        constraints = [
            # Ensure agency-managed advertisers have an agency
            models.CheckConstraint(
                check=~(models.Q(advertiser_type='agency_managed') & models.Q(agency__isnull=True)),
                name='agency_managed_must_have_agency'
            )
        ]
    
    def __str__(self):
        """String representation of the advertiser."""
        if self.agency:
            return f"{self.name} (via {self.agency.name})"
        return f"{self.name} ({self.code})"
    
    def get_absolute_url(self):
        """Get the absolute URL for this advertiser."""
        return reverse('advertisers:advertiser_detail', kwargs={'pk': self.pk})
    
    @property
    def is_agency_managed(self):
        """Check if this advertiser is managed by an agency."""
        return self.advertiser_type == 'agency_managed' and self.agency is not None
    
    @property
    def total_brands(self):
        """Get the total number of brands for this advertiser."""
        return self.brands.filter(is_active=True).count()
    
    @property
    def active_campaigns_count(self):
        """Get the number of active campaigns for this advertiser."""
        from apps.campaigns.models import Campaign
        return Campaign.objects.filter(
            advertiser=self,
            status='active'
        ).count()
    
    @property
    def total_spend(self):
        """Get the total spend for this advertiser."""
        from apps.campaigns.models import Campaign
        return Campaign.objects.filter(
            advertiser=self
        ).aggregate(
            total=models.Sum('budget')
        )['total'] or Decimal('0.00')


class Brand(BaseModel):
    """
    Brand model representing individual brands associated with advertisers.
    
    This model allows advertisers to manage multiple brands under their account,
    each with its own identity, category, and campaign targeting.
    """
    
    # Brand identification
    name = models.CharField(
        max_length=200,
        verbose_name=_('Brand Name'),
        help_text=_('Official name of the brand')
    )
    
    # Brand code for internal reference
    code = models.CharField(
        max_length=20,
        validators=[
            RegexValidator(
                regex=r'^[A-Z0-9-_]+$',
                message=_('Code must contain only uppercase letters, numbers, hyphens, and underscores')
            )
        ],
        verbose_name=_('Brand Code'),
        help_text=_('Unique identifier code for the brand')
    )
    
    # Brand ownership
    advertiser = models.ForeignKey(
        Advertiser,
        on_delete=models.CASCADE,
        related_name='brands',
        verbose_name=_('Advertiser'),
        help_text=_('Advertiser who owns this brand')
    )
    
    # Brand categorization
    category = models.ForeignKey(
        BrandCategory,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='brands',
        verbose_name=_('Category'),
        help_text=_('Category this brand belongs to')
    )
    
    # Brand information
    description = models.TextField(
        blank=True,
        verbose_name=_('Description'),
        help_text=_('Detailed description of the brand')
    )
    
    website = models.URLField(
        blank=True,
        verbose_name=_('Website'),
        help_text=_('Brand website URL')
    )
    
    # Brand assets
    logo = models.ImageField(
        upload_to='brands/logos/',
        blank=True,
        null=True,
        verbose_name=_('Logo'),
        help_text=_('Brand logo image')
    )
    
    # Brand colors for UI consistency
    primary_color = models.CharField(
        max_length=7,
        blank=True,
        validators=[
            RegexValidator(
                regex=r'^#[0-9A-Fa-f]{6}$',
                message=_('Enter a valid hex color code (e.g., #FF0000)')
            )
        ],
        verbose_name=_('Primary Color'),
        help_text=_('Primary brand color in hex format')
    )
    
    secondary_color = models.CharField(
        max_length=7,
        blank=True,
        validators=[
            RegexValidator(
                regex=r'^#[0-9A-Fa-f]{6}$',
                message=_('Enter a valid hex color code (e.g., #FF0000)')
            )
        ],
        verbose_name=_('Secondary Color'),
        help_text=_('Secondary brand color in hex format')
    )
    
    # Status and settings
    is_active = models.BooleanField(
        default=True,
        verbose_name=_('Active'),
        help_text=_('Whether this brand is active and can be used in campaigns')
    )
    
    class Meta:
        verbose_name = _('Brand')
        verbose_name_plural = _('Brands')
        ordering = ['name']
        unique_together = [['advertiser', 'code']]
        indexes = [
            models.Index(fields=['name']),
            models.Index(fields=['code']),
            models.Index(fields=['advertiser']),
            models.Index(fields=['category']),
            models.Index(fields=['is_active']),
        ]
    
    def __str__(self):
        """String representation of the brand."""
        return f"{self.name} ({self.advertiser.name})"
    
    def get_absolute_url(self):
        """Get the absolute URL for this brand."""
        return reverse('advertisers:brand_detail', kwargs={'pk': self.pk})
    
    @property
    def campaigns_count(self):
        """Get the number of campaigns for this brand."""
        from apps.campaigns.models import Campaign
        return Campaign.objects.filter(brand=self).count()
    
    @property
    def active_campaigns_count(self):
        """Get the number of active campaigns for this brand."""
        from apps.campaigns.models import Campaign
        return Campaign.objects.filter(
            brand=self,
            status='active'
        ).count()


class AdvertiserContact(BaseModel):
    """
    Contact information model for advertisers.
    
    This model allows multiple contacts to be associated with each advertiser,
    supporting different roles and contact types.
    """
    
    # Contact types
    CONTACT_TYPES = [
        ('primary', _('Primary Contact')),
        ('billing', _('Billing Contact')),
        ('technical', _('Technical Contact')),
        ('marketing', _('Marketing Contact')),
        ('legal', _('Legal Contact')),
    ]
    
    # Contact identification
    advertiser = models.ForeignKey(
        Advertiser,
        on_delete=models.CASCADE,
        related_name='contacts',
        verbose_name=_('Advertiser'),
        help_text=_('Advertiser this contact belongs to')
    )
    
    contact_type = models.CharField(
        max_length=20,
        choices=CONTACT_TYPES,
        default='primary',
        verbose_name=_('Contact Type'),
        help_text=_('Type of contact (primary, billing, etc.)')
    )
    
    # Personal information
    first_name = models.CharField(
        max_length=100,
        verbose_name=_('First Name'),
        help_text=_('Contact person first name')
    )
    
    last_name = models.CharField(
        max_length=100,
        verbose_name=_('Last Name'),
        help_text=_('Contact person last name')
    )
    
    title = models.CharField(
        max_length=100,
        blank=True,
        verbose_name=_('Job Title'),
        help_text=_('Job title or position')
    )
    
    # Contact information
    email = models.EmailField(
        validators=[EmailValidator()],
        verbose_name=_('Email Address'),
        help_text=_('Email address for this contact')
    )
    
    phone = models.CharField(
        max_length=20,
        blank=True,
        validators=[
            RegexValidator(
                regex=r'^\+?[1-9]\d{1,14}$',
                message=_('Enter a valid phone number')
            )
        ],
        verbose_name=_('Phone Number'),
        help_text=_('Phone number for this contact')
    )
    
    mobile = models.CharField(
        max_length=20,
        blank=True,
        validators=[
            RegexValidator(
                regex=r'^\+?[1-9]\d{1,14}$',
                message=_('Enter a valid mobile number')
            )
        ],
        verbose_name=_('Mobile Number'),
        help_text=_('Mobile number for this contact')
    )
    
    # Status
    is_active = models.BooleanField(
        default=True,
        verbose_name=_('Active'),
        help_text=_('Whether this contact is active')
    )
    
    class Meta:
        verbose_name = _('Advertiser Contact')
        verbose_name_plural = _('Advertiser Contacts')
        ordering = ['advertiser', 'contact_type', 'last_name', 'first_name']
        indexes = [
            models.Index(fields=['advertiser', 'contact_type']),
            models.Index(fields=['email']),
            models.Index(fields=['is_active']),
        ]
    
    def __str__(self):
        """String representation of the contact."""
        return f"{self.full_name} ({self.get_contact_type_display()}) - {self.advertiser.name}"
    
    @property
    def full_name(self):
        """Get the full name of the contact."""
        return f"{self.first_name} {self.last_name}".strip()


class AgencyUser(BaseModel):
    """
    Model for managing users associated with agencies.
    
    This model creates relationships between Django users and agencies,
    allowing for proper access control and permissions management.
    """
    
    # User roles within agency
    AGENCY_ROLES = [
        ('admin', _('Agency Administrator')),
        ('manager', _('Account Manager')),
        ('planner', _('Media Planner')),
        ('buyer', _('Media Buyer')),
        ('analyst', _('Analyst')),
        ('viewer', _('Viewer')),
    ]
    
    # Relationships
    user = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name='agency_memberships',
        verbose_name=_('User'),
        help_text=_('Django user associated with the agency')
    )
    
    agency = models.ForeignKey(
        Agency,
        on_delete=models.CASCADE,
        related_name='users',
        verbose_name=_('Agency'),
        help_text=_('Agency this user belongs to')
    )
    
    # Role and permissions
    role = models.CharField(
        max_length=20,
        choices=AGENCY_ROLES,
        default='viewer',
        verbose_name=_('Role'),
        help_text=_('User role within the agency')
    )
    
    # Permissions
    can_create_campaigns = models.BooleanField(
        default=False,
        verbose_name=_('Can Create Campaigns'),
        help_text=_('Whether this user can create new campaigns')
    )
    
    can_manage_advertisers = models.BooleanField(
        default=False,
        verbose_name=_('Can Manage Advertisers'),
        help_text=_('Whether this user can manage advertiser accounts')
    )
    
    can_view_reports = models.BooleanField(
        default=True,
        verbose_name=_('Can View Reports'),
        help_text=_('Whether this user can view campaign reports')
    )
    
    can_manage_billing = models.BooleanField(
        default=False,
        verbose_name=_('Can Manage Billing'),
        help_text=_('Whether this user can manage billing information')
    )
    
    # Status
    is_active = models.BooleanField(
        default=True,
        verbose_name=_('Active'),
        help_text=_('Whether this user membership is active')
    )
    
    # Tracking
    joined_at = models.DateTimeField(
        default=timezone.now,
        verbose_name=_('Joined At'),
        help_text=_('When the user joined the agency')
    )
    
    class Meta:
        verbose_name = _('Agency User')
        verbose_name_plural = _('Agency Users')
        unique_together = [['user', 'agency']]
        ordering = ['agency', 'role', 'user__last_name']
        indexes = [
            models.Index(fields=['user']),
            models.Index(fields=['agency']),
            models.Index(fields=['role']),
            models.Index(fields=['is_active']),
        ]
    
    def __str__(self):
        """String representation of the agency user."""
        return f"{self.user.get_full_name()} - {self.agency.name} ({self.get_role_display()})"
    
    @property
    def has_admin_permissions(self):
        """Check if user has admin permissions."""
        return self.role == 'admin'
    
    @property
    def has_management_permissions(self):
        """Check if user has management permissions."""
        return self.role in ['admin', 'manager']


class AdvertiserBilling(BaseModel):
    """
    Billing information model for advertisers.
    
    This model tracks billing details, payment methods, and financial
    information for each advertiser.
    """
    
    # Payment methods
    PAYMENT_METHODS = [
        ('credit_card', _('Credit Card')),
        ('bank_transfer', _('Bank Transfer')),
        ('check', _('Check')),
        ('invoice', _('Invoice')),
        ('prepaid', _('Prepaid')),
    ]
    
    # Billing cycles
    BILLING_CYCLES = [
        ('monthly', _('Monthly')),
        ('quarterly', _('Quarterly')),
        ('annually', _('Annually')),
        ('campaign', _('Per Campaign')),
    ]
    
    # Relationships
    advertiser = models.OneToOneField(
        Advertiser,
        on_delete=models.CASCADE,
        related_name='billing_info',
        verbose_name=_('Advertiser'),
        help_text=_('Advertiser this billing information belongs to')
    )
    
    # Billing details
    billing_name = models.CharField(
        max_length=200,
        verbose_name=_('Billing Name'),
        help_text=_('Name for billing purposes')
    )
    
    billing_address = models.TextField(
        verbose_name=_('Billing Address'),
        help_text=_('Address for billing purposes')
    )
    
    tax_id = models.CharField(
        max_length=50,
        blank=True,
        verbose_name=_('Tax ID'),
        help_text=_('Tax identification number')
    )
    
    # Payment information
    payment_method = models.CharField(
        max_length=20,
        choices=PAYMENT_METHODS,
        default='invoice',
        verbose_name=_('Payment Method'),
        help_text=_('Preferred payment method')
    )
    
    billing_cycle = models.CharField(
        max_length=20,
        choices=BILLING_CYCLES,
        default='monthly',
        verbose_name=_('Billing Cycle'),
        help_text=_('How often to bill this advertiser')
    )
    
    # Financial limits
    credit_limit = models.DecimalField(
        max_digits=12,
        decimal_places=2,
        default=Decimal('0.00'),
        validators=[MinValueValidator(Decimal('0.00'))],
        verbose_name=_('Credit Limit'),
        help_text=_('Maximum credit limit for this advertiser')
    )
    
    current_balance = models.DecimalField(
        max_digits=12,
        decimal_places=2,
        default=Decimal('0.00'),
        verbose_name=_('Current Balance'),
        help_text=_('Current account balance')
    )
    
    # Status
    is_active = models.BooleanField(
        default=True,
        verbose_name=_('Active'),
        help_text=_('Whether billing is active for this advertiser')
    )
    
    class Meta:
        verbose_name = _('Advertiser Billing')
        verbose_name_plural = _('Advertiser Billing')
        indexes = [
            models.Index(fields=['advertiser']),
            models.Index(fields=['payment_method']),
            models.Index(fields=['is_active']),
        ]
    
    def __str__(self):
        """String representation of the billing information."""
        return f"Billing for {self.advertiser.name}"
    
    @property
    def available_credit(self):
        """Calculate available credit."""
        return self.credit_limit - self.current_balance
    
    @property
    def is_over_limit(self):
        """Check if advertiser is over credit limit."""
        return self.current_balance > self.credit_limit