 
from decimal import Decimal

from django.db import models 
from django.core.exceptions import ValidationError
from django.core.validators import MinValueValidator

from apps.accounts.models import User
from apps.common.models import BaseModel
from apps.channels.models import Channel, Adbreak
from apps.agencies.models import Brand, Agency, Advertiser

from apps.campaigns.utils import get_encoded_file_upload_path, get_original_file_upload_path


class CampaignStatus(models.TextChoices):
    Draft = 'Draft'
    Pending_Approval = 'Pending Approval'
    Approved = 'Approved'
    Scheduled = 'Scheduled'
    Prebooked = 'Prebooked'
    Booked = 'Booked'
    Canceled = 'Canceled'
    In_progress = 'In progress'
    Terminated = 'Terminated'  
    Active = 'Active'
    Paused = 'Paused'
    Completed = 'Completed'
    Cancelled = 'Cancelled'


class Campaigns(BaseModel): 

    name = models.CharField(max_length=255)

    agency =  models.ForeignKey(
        Agency, 
        models.SET_NULL, 
        db_column='agency', 
        blank=True, 
        null=True
    )

    brand = models.ForeignKey(
        Brand,  
        on_delete=models.SET_NULL,
        db_column='brand',
        null=True,
        blank=True,
        related_name='campaigns'
    ) 
    
    advertiser = models.ForeignKey(
        Advertiser,
        on_delete=models.CASCADE,
        related_name='campaigns'
    )

    adpost = models.ForeignKey(
        'Adspots',
        models.SET_NULL, 
        null=True, 
        blank=True, 
        db_column='id_adpost' 
    )


    # Campaign Details 
    status = models.CharField(
        max_length=20,
        choices=CampaignStatus.choices,
        default=CampaignStatus.Draft
    )

    pacing = models.BooleanField(
        blank=True, 
        null=True
    )

    # Scheduling
    start_date = models.DateTimeField()
    end_date = models.DateTimeField()
    timezone = models.CharField(max_length=50, default='UTC')

    # Budget and Pricing
    budget = models.DecimalField(
        max_digits=12,
        decimal_places=2,
        validators=[MinValueValidator(Decimal('0.01'))]
    )

    volume = models.IntegerField(blank=True, null=True)
    delivery =  models.IntegerField(blank=True, null=True)
    cpm = models.FloatField(blank=True, null=True)
    general_rotation = models.IntegerField()
    
    # New Attributes 
    product = models.CharField(max_length=255, null=True)
    category = models.CharField(max_length=255, null=True)
    country = models.CharField(max_length=255, null=True)
    
    broadcasts_day = models.IntegerField(blank=True, null=True)
    media_type = models.CharField(max_length=255, null=True)

    is_vast = models.BooleanField(blank=True, null=True)
    vast_data = models.CharField(max_length=255, null=True)
    
    exclusivity = models.BooleanField(default=False)
    
    program_category = models.CharField(max_length=255, blank=True, null=True)
    shows_preference = models.CharField(max_length=255, blank=True, null=True)
    position_preference = models.CharField(max_length=255, blank=True, null=True)

    # Campaign Management
    campaign_manager = models.ForeignKey(
        User,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='managed_campaigns'
    )

    def __str__(self):
        return self.name

    class Meta : 
        verbose_name = "Campaign"
        verbose_name_plural = "Campaigns"
        db_table = 'Campaigns'
        ordering = ['-created_at']

    @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 is_active(self):
        """Check if campaign is currently active."""
        return self.status == CampaignStatus.Active



class AdspotsStatusChoices(models.TextChoices):
    DRAFT = 'draft', 'Draft'
    PROCESSING = 'processing', 'Processing'
    READY = 'ready', 'Ready'
    ACTIVE = 'active', 'Active'
    PAUSED = 'paused', 'Paused'
    COMPLETED = 'completed', 'Completed'
    FAILED = 'failed', 'Failed'
    ARCHIVED = 'archived', 'Archived'


class Adspots(BaseModel):
    """
    Model representing advertising spots/creative content for campaigns.
    """
     
    # Basic information
    name = models.CharField(
        max_length=255, 
        blank=True, 
        null=True,
        help_text="Name/title of the ad spot"
    )
    
    # Foreign key relationships with proper related_name for reverse lookups
    campaign = models.ForeignKey(
        Campaigns,  # Use string reference to avoid import issues
        on_delete=models.SET_NULL,  # Changed from DO_NOTHING for data integrity
        related_name='adspots',
        blank=True, 
        null=True,
        help_text="Associated campaign"
    )
    
    channel = models.ForeignKey(
        Channel, 
        on_delete=models.SET_NULL,  # Keep adspots if channel is deleted
        related_name='adspots',
        blank=True, 
        null=True,
        help_text="Distribution channel"
    )
    
    brand = models.ForeignKey(
        Brand, 
        on_delete=models.SET_NULL,  # Brand is critical, cascade delete
        related_name='adspots',
        blank=True, 
        null=True,
        help_text="Associated brand"
    )  

    # File management with better field types
    original_file = models.FileField(
        upload_to=get_original_file_upload_path,
        blank=True, 
        null=True,
        help_text="Original uploaded ad file"
    )
    
    encoded_file = models.FileField(
        upload_to=get_encoded_file_upload_path,
        blank=True, 
        null=True,
        help_text="Processed/encoded ad file"
    )
    
    # Duration as proper numeric field
    duration = models.PositiveIntegerField(
        blank=True, 
        null=True,
        validators=[MinValueValidator(1)],
        help_text="Duration in seconds"
    )
    
    status = models.CharField(
        max_length=20,
        choices=AdspotsStatusChoices.choices,
        default=AdspotsStatusChoices.DRAFT,
        help_text="Current status of the ad spot"
    )
    
    # VAST URL with proper URL field
    url_from_vast = models.URLField(
        blank=True, 
        null=True,
        help_text="VAST tag URL for the ad"
    )
    
    # Additional useful fields
    file_size = models.PositiveBigIntegerField(
        blank=True,
        null=True,
        help_text="File size in bytes"
    )
    
    mime_type = models.CharField(
        max_length=100,
        blank=True,
        null=True,
        help_text="MIME type of the ad file"
    )
     
    # Soft delete functionality
    is_active = models.BooleanField(
        default=True,
        help_text="Whether this ad spot is active"
    )
     
    class Meta: 
        db_table = 'Adspots'  # Follow Django naming conventions (lowercase)
        verbose_name = 'Ad Spot'
        verbose_name_plural = 'Ad Spots'
        ordering = ['-created_at']
        
        # Add database indexes for better query performance
        indexes = [
            models.Index(fields=['status']),
            models.Index(fields=['campaign', 'status']),
            models.Index(fields=['brand', 'status']),
            models.Index(fields=['channel', 'status']),
            models.Index(fields=['created_at']),
            models.Index(fields=['is_active', 'status']),
        ]
         

    def __str__(self):
        """String representation of the ad spot."""
        if self.name:
            return f"{self.name} ({self.get_status_display()})"
        return f"Ad Spot {self.id} ({self.get_status_display()})"
    
    def save(self, *args, **kwargs):
        """Override save to add custom logic."""
        # Update file size if file is present
        if self.original_file and hasattr(self.original_file, 'size'):
            self.file_size = self.original_file.size
            
        super().save(*args, **kwargs)
 
    def get_file_url(self, encoded=True):
        """Get the appropriate file URL."""
        file_field = self.encoded_file if encoded and self.encoded_file else self.original_file
        return file_field.url if file_field else None


class CampaignTimeIntervals(BaseModel): 
    campaign = models.ForeignKey(Campaigns, on_delete=models.CASCADE, verbose_name="Campaign", null=True, blank=True)
    start_time = models.TimeField(help_text="Start time for ad spot availability", verbose_name="Start Time")
    end_time = models.TimeField(help_text="End time for ad spot availability", verbose_name="End Time")

    class Meta: 
        db_table = 'CampaignTimeIntervals'
 

class Campaignairtimelog(BaseModel): 
    campaign = models.ForeignKey(Campaigns, models.SET_NULL, blank=True, null=True)
    spot = models.ForeignKey(Adspots, models.SET_NULL, blank=True, null=True)
    ad_break = models.ForeignKey(Adbreak, models.SET_NULL, blank=True, null=True)

    airtime_start = models.DateTimeField(blank=True, null=True)
    airtime_end = models.DateTimeField(blank=True, null=True)
    insertion_status = models.BooleanField(blank=True, null=True)

    class Meta: 
        db_table = 'CampaignAirtimeLog'


class CampaignPriorityScores(models.Model):
    id = models.AutoField(db_column='id', primary_key=True)
    # The foreign key referencing the associated Campaign.
    campaign = models.ForeignKey(
        Campaigns,
        on_delete=models.DO_NOTHING,
        help_text="The foreign key referencing the associated Campaign.", 
        blank=True, 
        null=True
    )
    # The priority score calculated for the campaign on the given date.
    priority_score = models.FloatField(
        blank=True,
        null=True,
        help_text="The priority score calculated for the campaign on the given date."
    ) 
    # The date and time when the priority score record was created.
    created_at = models.DateTimeField( 
        auto_now_add=True,
        help_text="The date and time when the priority score record was created."
    )
    # The date and time when the priority score record was updated.
    updated_at = models.DateTimeField(
        auto_now=True,
        help_text="The date and time when the priority score record was last updated."
    )

    class Meta: 
        db_table = 'CampaignPriorityScores'


class AdSpotExclusion(models.Model):
    ad_spot_1 = models.ForeignKey(Adspots, related_name='exclusions_1', on_delete=models.CASCADE, null=True, blank=True)
    ad_spot_2 = models.ForeignKey(Adspots, related_name='exclusions_2', on_delete=models.CASCADE, null=True, blank=True)

    def clean(self):
        if self.ad_spot_1 == self.ad_spot_2:
            raise ValidationError("Ad spots cannot exclude themselves.")

    class Meta: 
        db_table = 'AdSpotExclusion'
        constraints = [
            models.UniqueConstraint(fields=['ad_spot_1', 'ad_spot_2'], name='unique_ad_spot_exclusion')
        ]


class Pending(models.Model):
    id_pending=models.AutoField(primary_key=True)
    creative_id=models.CharField(max_length=255)
    url=models.CharField(max_length=255)
    duration=models.CharField(max_length=255)

    class Meta: 
        db_table = 'Pending'


class DayTime(models.Model):
    id_time = models.AutoField(primary_key=True)
    name = models.CharField(max_length=255)
    start = models.CharField(max_length=255)
    end = models.CharField(max_length=255)

    class Meta: 
        db_table = 'Day_time'


class Placement(models.Model):
    id_placement = models.AutoField(primary_key=True)
    id_time = models.ForeignKey(DayTime,models.DO_NOTHING , db_column='id_time',blank=True, null=True )
    id_campaign = models.ForeignKey(Campaigns,models.DO_NOTHING , db_column='id_campaign',blank=True, null=True )
    id_channel = models.ForeignKey(Channel, models.DO_NOTHING, db_column='id_channel', blank=True, null=True)

    class Meta : 
        db_table = 'Placement'