# -*- coding: utf-8 -*-
"""
Campaigns Models Module

This module contains all campaign-related models including:
- Campaign model for advertising campaigns
- CampaignSchedule for scheduling campaigns
- CampaignBudget for budget management
- CampaignPerformance for tracking metrics
"""

import uuid
from decimal import Decimal
from django.db import models
from django.urls import reverse
from django.utils import timezone
from django.core.validators import MinValueValidator, MaxValueValidator
from django.utils.translation import gettext_lazy as _

from apps.common.models import BaseModel


class Campaign(BaseModel):
    """
    Main campaign model for TV advertising campaigns.
    
    Represents an advertising campaign with all its properties including
    budget, schedule, targeting, and performance tracking.
    """
    
    CAMPAIGN_STATUS = [
        ('draft', _('Draft')),
        ('pending', _('Pending Approval')),
        ('approved', _('Approved')),
        ('active', _('Active')),
        ('paused', _('Paused')),
        ('completed', _('Completed')),
        ('cancelled', _('Cancelled')),
    ]
    
    CAMPAIGN_TYPES = [
        ('brand_awareness', _('Brand Awareness')),
        ('product_launch', _('Product Launch')),
        ('promotional', _('Promotional')),
        ('seasonal', _('Seasonal')),
        ('event_based', _('Event Based')),
        ('direct_response', _('Direct Response')),
    ]
    
    PRIORITY_LEVELS = [
        (1, _('Low')),
        (2, _('Normal')),
        (3, _('High')),
        (4, _('Critical')),
    ]
    
    # Basic campaign information
    id = models.UUIDField(
        primary_key=True,
        default=uuid.uuid4,
        editable=False,
        verbose_name=_('Campaign ID')
    )
    name = models.CharField(
        max_length=200,
        verbose_name=_('Campaign Name'),
        help_text=_('Descriptive name for the advertising campaign')
    )
    description = models.TextField(
        blank=True,
        verbose_name=_('Description'),
        help_text=_('Detailed description of the campaign objectives and strategy')
    )
    campaign_type = models.CharField(
        max_length=20,
        choices=CAMPAIGN_TYPES,
        default='brand_awareness',
        verbose_name=_('Campaign Type'),
        help_text=_('Type of advertising campaign')
    )
    
    # Campaign relationships
    advertiser = models.ForeignKey(
        'advertisers.Brand',
        on_delete=models.CASCADE,
        related_name='campaigns',
        verbose_name=_('Advertiser'),
        help_text=_('The advertiser running this campaign')
    )
    channels = models.ManyToManyField(
        'channels.TVChannel',
        through='CampaignChannelSchedule',
        related_name='campaigns',
        verbose_name=_('Target Channels'),
        help_text=_('TV channels where this campaign will run')
    )
    geographic_zones = models.ManyToManyField(
        'channels.GeographicZone',
        blank=True,
        related_name='campaigns',
        verbose_name=_('Geographic Zones'),
        help_text=_('Geographic areas to target for this campaign')
    )
    
    # Campaign timing
    start_date = models.DateTimeField(
        verbose_name=_('Start Date'),
        help_text=_('When the campaign should start running')
    )
    end_date = models.DateTimeField(
        verbose_name=_('End Date'),
        help_text=_('When the campaign should stop running')
    )
    
    # Campaign budget and pricing
    total_budget = models.DecimalField(
        max_digits=12,
        decimal_places=2,
        validators=[MinValueValidator(Decimal('0.01'))],
        verbose_name=_('Total Budget'),
        help_text=_('Total budget allocated for this campaign')
    )
    daily_budget = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        null=True,
        blank=True,
        validators=[MinValueValidator(Decimal('0.01'))],
        verbose_name=_('Daily Budget'),
        help_text=_('Maximum daily spend for this campaign')
    )
    cost_per_spot = models.DecimalField(
        max_digits=8,
        decimal_places=2,
        null=True,
        blank=True,
        validators=[MinValueValidator(Decimal('0.01'))],
        verbose_name=_('Cost Per Spot'),
        help_text=_('Cost per advertising spot/insertion')
    )
    
    # Campaign status and priority
    status = models.CharField(
        max_length=20,
        choices=CAMPAIGN_STATUS,
        default='draft',
        verbose_name=_('Status'),
        help_text=_('Current status of the campaign')
    )
    priority = models.IntegerField(
        choices=PRIORITY_LEVELS,
        default=2,
        verbose_name=_('Priority'),
        help_text=_('Campaign priority level for scheduling conflicts')
    )
    
    # Campaign targeting and demographics
    target_demographics = models.JSONField(
        default=dict,
        blank=True,
        verbose_name=_('Target Demographics'),
        help_text=_('JSON data containing demographic targeting information')
    )
    
    # Campaign performance tracking
    total_impressions = models.PositiveBigIntegerField(
        default=0,
        verbose_name=_('Total Impressions'),
        help_text=_('Total number of ad impressions delivered')
    )
    total_spend = models.DecimalField(
        max_digits=12,
        decimal_places=2,
        default=Decimal('0.00'),
        verbose_name=_('Total Spend'),
        help_text=_('Total amount spent on this campaign')
    )
    
    # Campaign metadata
    notes = models.TextField(
        blank=True,
        verbose_name=_('Notes'),
        help_text=_('Internal notes and comments about the campaign')
    )
    
    class Meta:
        verbose_name = _('Campaign')
        verbose_name_plural = _('Campaigns')
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['status', 'start_date']),
            models.Index(fields=['advertiser', 'status']),
            models.Index(fields=['start_date', 'end_date']),
            models.Index(fields=['priority', 'status']),
        ]
    
    def __str__(self):
        return f"{self.name} ({self.advertiser.name})"
    
    def get_absolute_url(self):
        return reverse('campaigns:detail', kwargs={'pk': self.pk})
    
    @property
    def is_active(self):
        """Check if campaign is currently active."""
        now = timezone.now()
        return (
            self.status == 'active' and
            self.start_date <= now <= self.end_date
        )
    
    @property
    def days_remaining(self):
        """Calculate days remaining in campaign."""
        if self.end_date:
            delta = self.end_date.date() - timezone.now().date()
            return max(0, delta.days)
        return 0
    
    @property
    def budget_utilization(self):
        """Calculate budget utilization percentage."""
        if self.total_budget > 0:
            return (self.total_spend / self.total_budget) * 100
        return 0
    
    def get_remaining_budget(self):
        """Get remaining budget amount."""
        return self.total_budget - self.total_spend
    
    def can_be_activated(self):
        """Check if campaign can be activated."""
        return (
            self.status in ['approved', 'paused'] and
            self.start_date <= timezone.now() <= self.end_date and
            self.get_remaining_budget() > 0
        )


class CampaignChannelSchedule(BaseModel):
    """
    Schedule model for campaign-channel relationships.
    
    Defines when and how a campaign runs on specific channels,
    including time slots, frequency, and channel-specific budgets.
    """
    
    campaign = models.ForeignKey(
        Campaign,
        on_delete=models.CASCADE,
        related_name='channel_schedules',
        verbose_name=_('Campaign')
    )
    channel = models.ForeignKey(
        'channels.TVChannel',
        on_delete=models.CASCADE,
        related_name='campaign_schedules',
        verbose_name=_('Channel')
    )
    
    # Scheduling details
    start_time = models.TimeField(
        verbose_name=_('Start Time'),
        help_text=_('Daily start time for ads on this channel')
    )
    end_time = models.TimeField(
        verbose_name=_('End Time'),
        help_text=_('Daily end time for ads on this channel')
    )
    days_of_week = models.JSONField(
        default=list,
        verbose_name=_('Days of Week'),
        help_text=_('List of days when ads should run (0=Monday, 6=Sunday)')
    )
    
    # Frequency and budget
    spots_per_day = models.PositiveIntegerField(
        default=1,
        validators=[MinValueValidator(1), MaxValueValidator(100)],
        verbose_name=_('Spots Per Day'),
        help_text=_('Number of ad spots per day on this channel')
    )
    channel_budget = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        null=True,
        blank=True,
        validators=[MinValueValidator(Decimal('0.01'))],
        verbose_name=_('Channel Budget'),
        help_text=_('Budget allocated for this channel')
    )
    
    # Performance tracking
    impressions_delivered = models.PositiveBigIntegerField(
        default=0,
        verbose_name=_('Impressions Delivered'),
        help_text=_('Total impressions delivered on this channel')
    )
    amount_spent = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        default=Decimal('0.00'),
        verbose_name=_('Amount Spent'),
        help_text=_('Amount spent on this channel')
    )
    
    class Meta:
        verbose_name = _('Campaign Channel Schedule')
        verbose_name_plural = _('Campaign Channel Schedules')
        unique_together = ['campaign', 'channel']
        ordering = ['channel__name']
    
    def __str__(self):
        return f"{self.campaign.name} - {self.channel.name}"
    
    @property
    def daily_frequency(self):
        """Get daily frequency as a readable string."""
        return f"{self.spots_per_day} spots/day"


class CampaignPerformance(BaseModel):
    """
    Daily performance tracking for campaigns.
    
    Stores daily metrics and performance data for campaigns,
    enabling detailed analytics and reporting.
    """
    
    campaign = models.ForeignKey(
        Campaign,
        on_delete=models.CASCADE,
        related_name='performance_records',
        verbose_name=_('Campaign')
    )
    date = models.DateField(
        verbose_name=_('Date'),
        help_text=_('Date for this performance record')
    )
    
    # Performance metrics
    impressions = models.PositiveBigIntegerField(
        default=0,
        verbose_name=_('Impressions'),
        help_text=_('Number of ad impressions on this date')
    )
    spots_aired = models.PositiveIntegerField(
        default=0,
        verbose_name=_('Spots Aired'),
        help_text=_('Number of ad spots that aired on this date')
    )
    spend = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        default=Decimal('0.00'),
        verbose_name=_('Daily Spend'),
        help_text=_('Amount spent on this date')
    )
    
    # Calculated metrics
    cpm = models.DecimalField(
        max_digits=8,
        decimal_places=2,
        null=True,
        blank=True,
        verbose_name=_('CPM'),
        help_text=_('Cost per thousand impressions')
    )
    reach = models.PositiveIntegerField(
        null=True,
        blank=True,
        verbose_name=_('Reach'),
        help_text=_('Estimated unique viewers reached')
    )
    frequency = models.DecimalField(
        max_digits=4,
        decimal_places=2,
        null=True,
        blank=True,
        verbose_name=_('Frequency'),
        help_text=_('Average frequency per viewer')
    )
    
    class Meta:
        verbose_name = _('Campaign Performance')
        verbose_name_plural = _('Campaign Performance Records')
        unique_together = ['campaign', 'date']
        ordering = ['-date']
        indexes = [
            models.Index(fields=['campaign', 'date']),
            models.Index(fields=['date']),
        ]
    
    def __str__(self):
        return f"{self.campaign.name} - {self.date}"
    
    def save(self, *args, **kwargs):
        """Calculate CPM when saving."""
        if self.spend > 0 and self.impressions > 0:
            self.cpm = (self.spend / self.impressions) * 1000
        super().save(*args, **kwargs)