"""Models for Jingles app."""

import os
import hashlib
from datetime import datetime, timedelta
from decimal import Decimal
from typing import Optional, Dict, Any

from django.db import models
from django.contrib.auth.models import User
from django.core.validators import FileExtensionValidator, MinValueValidator, MaxValueValidator
from django.utils import timezone
from django.urls import reverse
from django.core.files.storage import default_storage
from django.conf import settings

from apps.channels.models import Channel
from apps.core.models import BaseModel, TimeStampedModel


def jingle_upload_path(instance, filename):
    """Generate upload path for jingle files."""
    # Create path: jingles/channel_id/year/month/filename
    now = timezone.now()
    channel_id = instance.channel.id if instance.channel else 'no_channel'
    return f'jingles/{channel_id}/{now.year}/{now.month:02d}/{filename}'


class JingleCategory(BaseModel):
    """Categories for organizing jingles."""
    
    name = models.CharField(
        max_length=100,
        unique=True,
        help_text="Category name (e.g., Station ID, Commercial Break, Weather)"
    )
    description = models.TextField(
        blank=True,
        help_text="Description of this jingle category"
    )
    color_code = models.CharField(
        max_length=7,
        default='#007bff',
        help_text="Hex color code for UI display"
    )
    sort_order = models.PositiveIntegerField(
        default=0,
        help_text="Sort order for display"
    )
    is_active = models.BooleanField(
        default=True,
        help_text="Whether this category is active"
    )
    
    class Meta:
        db_table = 'jingles_category'
        verbose_name = 'Jingle Category'
        verbose_name_plural = 'Jingle Categories'
        ordering = ['sort_order', 'name']
    
    def __str__(self):
        return self.name
    
    def get_absolute_url(self):
        return reverse('jingles:category-detail', kwargs={'pk': self.pk})


class JingleType(BaseModel):
    """Types of jingles based on usage."""
    
    USAGE_CHOICES = [
        ('intro', 'Program Intro'),
        ('outro', 'Program Outro'),
        ('break', 'Commercial Break'),
        ('transition', 'Transition'),
        ('station_id', 'Station Identification'),
        ('weather', 'Weather Segment'),
        ('news', 'News Segment'),
        ('sports', 'Sports Segment'),
        ('promo', 'Promotional'),
        ('emergency', 'Emergency Alert'),
        ('other', 'Other'),
    ]
    
    name = models.CharField(
        max_length=100,
        unique=True,
        help_text="Type name"
    )
    usage = models.CharField(
        max_length=20,
        choices=USAGE_CHOICES,
        default='other',
        help_text="Primary usage of this jingle type"
    )
    description = models.TextField(
        blank=True,
        help_text="Description of this jingle type"
    )
    default_duration = models.PositiveIntegerField(
        null=True,
        blank=True,
        help_text="Default duration in seconds"
    )
    is_active = models.BooleanField(
        default=True,
        help_text="Whether this type is active"
    )
    
    class Meta:
        db_table = 'jingles_type'
        verbose_name = 'Jingle Type'
        verbose_name_plural = 'Jingle Types'
        ordering = ['name']
    
    def __str__(self):
        return self.name
    
    def get_absolute_url(self):
        return reverse('jingles:type-detail', kwargs={'pk': self.pk})


class Jingle(TimeStampedModel):
    """Main jingle model for managing audio/video jingles."""
    
    STATUS_CHOICES = [
        ('pending', 'Pending Review'),
        ('processing', 'Processing'),
        ('approved', 'Approved'),
        ('rejected', 'Rejected'),
        ('archived', 'Archived'),
    ]
    
    QUALITY_CHOICES = [
        ('low', 'Low Quality'),
        ('medium', 'Medium Quality'),
        ('high', 'High Quality'),
        ('premium', 'Premium Quality'),
    ]
    
    # Basic information
    name = models.CharField(
        max_length=255,
        help_text="Jingle name or title"
    )
    description = models.TextField(
        blank=True,
        help_text="Description of the jingle"
    )
    channel = models.ForeignKey(
        Channel,
        on_delete=models.CASCADE,
        related_name='jingles',
        help_text="Channel this jingle belongs to"
    )
    category = models.ForeignKey(
        JingleCategory,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='jingles',
        help_text="Jingle category"
    )
    jingle_type = models.ForeignKey(
        JingleType,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='jingles',
        help_text="Type of jingle"
    )
    
    # File information
    file = models.FileField(
        upload_to=jingle_upload_path,
        validators=[
            FileExtensionValidator(
                allowed_extensions=['mp3', 'wav', 'aac', 'mp4', 'mov', 'avi', 'mkv']
            )
        ],
        help_text="Jingle audio/video file"
    )
    file_size = models.PositiveBigIntegerField(
        null=True,
        blank=True,
        help_text="File size in bytes"
    )
    file_format = models.CharField(
        max_length=10,
        blank=True,
        help_text="File format (mp3, wav, mp4, etc.)"
    )
    md5_hash = models.CharField(
        max_length=32,
        blank=True,
        help_text="MD5 hash of the file for integrity checking"
    )
    
    # Media properties
    duration_seconds = models.PositiveIntegerField(
        null=True,
        blank=True,
        help_text="Duration in seconds"
    )
    bitrate = models.PositiveIntegerField(
        null=True,
        blank=True,
        help_text="Bitrate in kbps"
    )
    sample_rate = models.PositiveIntegerField(
        null=True,
        blank=True,
        help_text="Sample rate in Hz"
    )
    channels = models.PositiveSmallIntegerField(
        null=True,
        blank=True,
        help_text="Number of audio channels (1=mono, 2=stereo)"
    )
    codec = models.CharField(
        max_length=50,
        blank=True,
        help_text="Audio/video codec"
    )
    
    # Video properties (if applicable)
    width = models.PositiveIntegerField(
        null=True,
        blank=True,
        help_text="Video width in pixels"
    )
    height = models.PositiveIntegerField(
        null=True,
        blank=True,
        help_text="Video height in pixels"
    )
    frame_rate = models.DecimalField(
        max_digits=6,
        decimal_places=2,
        null=True,
        blank=True,
        help_text="Video frame rate (fps)"
    )
    
    # Status and workflow
    status = models.CharField(
        max_length=20,
        choices=STATUS_CHOICES,
        default='pending',
        help_text="Current status of the jingle"
    )
    quality = models.CharField(
        max_length=20,
        choices=QUALITY_CHOICES,
        default='medium',
        help_text="Quality rating"
    )
    is_active = models.BooleanField(
        default=True,
        help_text="Whether this jingle is active and can be used"
    )
    is_deleted = models.BooleanField(
        default=False,
        help_text="Soft delete flag"
    )
    
    # Scheduling and usage
    start_date = models.DateField(
        null=True,
        blank=True,
        help_text="Date when jingle becomes active"
    )
    end_date = models.DateField(
        null=True,
        blank=True,
        help_text="Date when jingle expires"
    )
    priority = models.PositiveSmallIntegerField(
        default=5,
        validators=[MinValueValidator(1), MaxValueValidator(10)],
        help_text="Priority level (1=highest, 10=lowest)"
    )
    play_count = models.PositiveIntegerField(
        default=0,
        help_text="Number of times this jingle has been played"
    )
    last_played = models.DateTimeField(
        null=True,
        blank=True,
        help_text="Last time this jingle was played"
    )
    
    # User tracking
    uploaded_by = models.ForeignKey(
        User,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='uploaded_jingles',
        help_text="User who uploaded this jingle"
    )
    approved_by = models.ForeignKey(
        User,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='approved_jingles',
        help_text="User who approved this jingle"
    )
    approved_at = models.DateTimeField(
        null=True,
        blank=True,
        help_text="When the jingle was approved"
    )
    
    # Metadata
    tags = models.CharField(
        max_length=500,
        blank=True,
        help_text="Comma-separated tags for searching"
    )
    notes = models.TextField(
        blank=True,
        help_text="Internal notes about this jingle"
    )
    metadata = models.JSONField(
        default=dict,
        blank=True,
        help_text="Additional metadata as JSON"
    )
    
    class Meta:
        db_table = 'jingles_jingle'
        verbose_name = 'Jingle'
        verbose_name_plural = 'Jingles'
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['channel', 'status']),
            models.Index(fields=['category', 'jingle_type']),
            models.Index(fields=['is_active', 'is_deleted']),
            models.Index(fields=['start_date', 'end_date']),
            models.Index(fields=['priority', 'last_played']),
        ]
    
    def __str__(self):
        return f"{self.name} ({self.channel})"
    
    def save(self, *args, **kwargs):
        """Override save to calculate file properties."""
        if self.file:
            # Calculate file size
            if not self.file_size:
                self.file_size = self.file.size
            
            # Extract file format
            if not self.file_format:
                self.file_format = os.path.splitext(self.file.name)[1][1:].lower()
            
            # Calculate MD5 hash if not set
            if not self.md5_hash:
                self.md5_hash = self._calculate_md5()
        
        super().save(*args, **kwargs)
    
    def _calculate_md5(self) -> str:
        """Calculate MD5 hash of the file."""
        if not self.file:
            return ''
        
        hash_md5 = hashlib.md5()
        try:
            self.file.seek(0)
            for chunk in iter(lambda: self.file.read(4096), b""):
                hash_md5.update(chunk)
            self.file.seek(0)
            return hash_md5.hexdigest()
        except Exception:
            return ''
    
    def get_absolute_url(self):
        return reverse('jingles:jingle-detail', kwargs={'pk': self.pk})
    
    def get_file_url(self):
        """Get the URL for the jingle file."""
        if self.file:
            return self.file.url
        return None
    
    def get_file_size_display(self):
        """Get human-readable file size."""
        if not self.file_size:
            return 'Unknown'
        
        for unit in ['B', 'KB', 'MB', 'GB']:
            if self.file_size < 1024.0:
                return f"{self.file_size:.1f} {unit}"
            self.file_size /= 1024.0
        return f"{self.file_size:.1f} TB"
    
    def get_duration_display(self):
        """Get human-readable duration."""
        if not self.duration_seconds:
            return 'Unknown'
        
        minutes, seconds = divmod(self.duration_seconds, 60)
        hours, minutes = divmod(minutes, 60)
        
        if hours:
            return f"{hours:02d}:{minutes:02d}:{seconds:02d}"
        else:
            return f"{minutes:02d}:{seconds:02d}"
    
    def get_resolution_display(self):
        """Get video resolution display."""
        if self.width and self.height:
            return f"{self.width}x{self.height}"
        return None
    
    def is_video(self):
        """Check if this is a video jingle."""
        video_formats = ['mp4', 'mov', 'avi', 'mkv', 'webm']
        return self.file_format.lower() in video_formats
    
    def is_audio(self):
        """Check if this is an audio jingle."""
        audio_formats = ['mp3', 'wav', 'aac', 'ogg', 'flac']
        return self.file_format.lower() in audio_formats
    
    def is_expired(self):
        """Check if the jingle has expired."""
        if self.end_date:
            return timezone.now().date() > self.end_date
        return False
    
    def is_available(self):
        """Check if the jingle is available for use."""
        if not self.is_active or self.is_deleted or self.status != 'approved':
            return False
        
        if self.is_expired():
            return False
        
        if self.start_date and timezone.now().date() < self.start_date:
            return False
        
        return True
    
    def increment_play_count(self):
        """Increment the play count and update last played time."""
        self.play_count += 1
        self.last_played = timezone.now()
        self.save(update_fields=['play_count', 'last_played'])
    
    def get_tags_list(self):
        """Get tags as a list."""
        if self.tags:
            return [tag.strip() for tag in self.tags.split(',') if tag.strip()]
        return []
    
    def set_tags_list(self, tags_list):
        """Set tags from a list."""
        self.tags = ', '.join(tags_list)


class JinglePlaylist(TimeStampedModel):
    """Playlists for organizing and scheduling jingles."""
    
    PLAYLIST_TYPES = [
        ('manual', 'Manual'),
        ('auto', 'Automatic'),
        ('scheduled', 'Scheduled'),
        ('random', 'Random'),
    ]
    
    name = models.CharField(
        max_length=255,
        help_text="Playlist name"
    )
    description = models.TextField(
        blank=True,
        help_text="Playlist description"
    )
    channel = models.ForeignKey(
        Channel,
        on_delete=models.CASCADE,
        related_name='jingle_playlists',
        help_text="Channel this playlist belongs to"
    )
    playlist_type = models.CharField(
        max_length=20,
        choices=PLAYLIST_TYPES,
        default='manual',
        help_text="Type of playlist"
    )
    jingles = models.ManyToManyField(
        Jingle,
        through='JinglePlaylistItem',
        related_name='playlists',
        help_text="Jingles in this playlist"
    )
    is_active = models.BooleanField(
        default=True,
        help_text="Whether this playlist is active"
    )
    created_by = models.ForeignKey(
        User,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='created_playlists',
        help_text="User who created this playlist"
    )
    
    # Scheduling
    start_time = models.TimeField(
        null=True,
        blank=True,
        help_text="Daily start time for scheduled playlists"
    )
    end_time = models.TimeField(
        null=True,
        blank=True,
        help_text="Daily end time for scheduled playlists"
    )
    days_of_week = models.CharField(
        max_length=20,
        blank=True,
        help_text="Days of week (1=Monday, 7=Sunday), comma-separated"
    )
    
    # Auto-generation rules
    auto_rules = models.JSONField(
        default=dict,
        blank=True,
        help_text="Rules for automatic playlist generation"
    )
    
    class Meta:
        db_table = 'jingles_playlist'
        verbose_name = 'Jingle Playlist'
        verbose_name_plural = 'Jingle Playlists'
        ordering = ['name']
        unique_together = ['channel', 'name']
    
    def __str__(self):
        return f"{self.name} ({self.channel})"
    
    def get_absolute_url(self):
        return reverse('jingles:playlist-detail', kwargs={'pk': self.pk})
    
    def get_total_duration(self):
        """Get total duration of all jingles in the playlist."""
        total = 0
        for item in self.playlist_items.all():
            if item.jingle.duration_seconds:
                total += item.jingle.duration_seconds
        return total
    
    def get_jingle_count(self):
        """Get number of jingles in the playlist."""
        return self.playlist_items.count()
    
    def get_next_jingle(self, current_jingle=None):
        """Get the next jingle to play."""
        items = self.playlist_items.filter(
            jingle__is_active=True,
            jingle__status='approved'
        ).order_by('order')
        
        if not items.exists():
            return None
        
        if current_jingle:
            try:
                current_item = items.get(jingle=current_jingle)
                next_items = items.filter(order__gt=current_item.order)
                if next_items.exists():
                    return next_items.first().jingle
                else:
                    # Loop back to first
                    return items.first().jingle
            except JinglePlaylistItem.DoesNotExist:
                pass
        
        # Return first jingle
        return items.first().jingle if items.exists() else None


class JinglePlaylistItem(BaseModel):
    """Individual items in a jingle playlist."""
    
    playlist = models.ForeignKey(
        JinglePlaylist,
        on_delete=models.CASCADE,
        related_name='playlist_items',
        help_text="Playlist this item belongs to"
    )
    jingle = models.ForeignKey(
        Jingle,
        on_delete=models.CASCADE,
        related_name='playlist_items',
        help_text="Jingle in this playlist item"
    )
    order = models.PositiveIntegerField(
        default=0,
        help_text="Order of this item in the playlist"
    )
    is_active = models.BooleanField(
        default=True,
        help_text="Whether this item is active in the playlist"
    )
    
    # Scheduling overrides
    start_date = models.DateField(
        null=True,
        blank=True,
        help_text="Override start date for this item"
    )
    end_date = models.DateField(
        null=True,
        blank=True,
        help_text="Override end date for this item"
    )
    
    # Play statistics
    play_count = models.PositiveIntegerField(
        default=0,
        help_text="Number of times this item has been played"
    )
    last_played = models.DateTimeField(
        null=True,
        blank=True,
        help_text="Last time this item was played"
    )
    
    class Meta:
        db_table = 'jingles_playlist_item'
        verbose_name = 'Playlist Item'
        verbose_name_plural = 'Playlist Items'
        ordering = ['playlist', 'order']
        unique_together = ['playlist', 'jingle']
    
    def __str__(self):
        return f"{self.playlist.name} - {self.jingle.name}"
    
    def is_available(self):
        """Check if this playlist item is available for play."""
        if not self.is_active or not self.jingle.is_available():
            return False
        
        now = timezone.now().date()
        
        if self.start_date and now < self.start_date:
            return False
        
        if self.end_date and now > self.end_date:
            return False
        
        return True
    
    def increment_play_count(self):
        """Increment play count and update last played time."""
        self.play_count += 1
        self.last_played = timezone.now()
        self.save(update_fields=['play_count', 'last_played'])
        
        # Also increment the jingle's play count
        self.jingle.increment_play_count()


class JingleSchedule(TimeStampedModel):
    """Scheduling rules for automatic jingle playback."""
    
    SCHEDULE_TYPES = [
        ('interval', 'Time Interval'),
        ('program_break', 'Program Break'),
        ('hourly', 'Hourly'),
        ('daily', 'Daily'),
        ('event', 'Event-based'),
    ]
    
    name = models.CharField(
        max_length=255,
        help_text="Schedule name"
    )
    channel = models.ForeignKey(
        Channel,
        on_delete=models.CASCADE,
        related_name='jingle_schedules',
        help_text="Channel this schedule applies to"
    )
    schedule_type = models.CharField(
        max_length=20,
        choices=SCHEDULE_TYPES,
        help_text="Type of schedule"
    )
    playlist = models.ForeignKey(
        JinglePlaylist,
        on_delete=models.CASCADE,
        related_name='schedules',
        help_text="Playlist to use for this schedule"
    )
    
    # Time-based scheduling
    start_time = models.TimeField(
        null=True,
        blank=True,
        help_text="Start time for the schedule"
    )
    end_time = models.TimeField(
        null=True,
        blank=True,
        help_text="End time for the schedule"
    )
    interval_minutes = models.PositiveIntegerField(
        null=True,
        blank=True,
        help_text="Interval in minutes for interval-based schedules"
    )
    
    # Date-based scheduling
    start_date = models.DateField(
        null=True,
        blank=True,
        help_text="Start date for the schedule"
    )
    end_date = models.DateField(
        null=True,
        blank=True,
        help_text="End date for the schedule"
    )
    days_of_week = models.CharField(
        max_length=20,
        blank=True,
        help_text="Days of week (1=Monday, 7=Sunday), comma-separated"
    )
    
    # Status and priority
    is_active = models.BooleanField(
        default=True,
        help_text="Whether this schedule is active"
    )
    priority = models.PositiveSmallIntegerField(
        default=5,
        validators=[MinValueValidator(1), MaxValueValidator(10)],
        help_text="Priority level (1=highest, 10=lowest)"
    )
    
    # Rules and conditions
    conditions = models.JSONField(
        default=dict,
        blank=True,
        help_text="Additional conditions for schedule activation"
    )
    
    created_by = models.ForeignKey(
        User,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='created_schedules',
        help_text="User who created this schedule"
    )
    
    class Meta:
        db_table = 'jingles_schedule'
        verbose_name = 'Jingle Schedule'
        verbose_name_plural = 'Jingle Schedules'
        ordering = ['priority', 'name']
    
    def __str__(self):
        return f"{self.name} ({self.channel})"
    
    def get_absolute_url(self):
        return reverse('jingles:schedule-detail', kwargs={'pk': self.pk})
    
    def is_active_now(self):
        """Check if the schedule is active right now."""
        if not self.is_active:
            return False
        
        now = timezone.now()
        current_date = now.date()
        current_time = now.time()
        current_weekday = now.weekday() + 1  # Monday = 1
        
        # Check date range
        if self.start_date and current_date < self.start_date:
            return False
        if self.end_date and current_date > self.end_date:
            return False
        
        # Check days of week
        if self.days_of_week:
            allowed_days = [int(d.strip()) for d in self.days_of_week.split(',') if d.strip().isdigit()]
            if allowed_days and current_weekday not in allowed_days:
                return False
        
        # Check time range
        if self.start_time and self.end_time:
            if self.start_time <= self.end_time:
                # Same day range
                if not (self.start_time <= current_time <= self.end_time):
                    return False
            else:
                # Overnight range
                if not (current_time >= self.start_time or current_time <= self.end_time):
                    return False
        
        return True
    
    def get_next_execution_time(self):
        """Get the next time this schedule should execute."""
        if self.schedule_type == 'interval' and self.interval_minutes:
            now = timezone.now()
            next_time = now + timedelta(minutes=self.interval_minutes)
            return next_time
        
        # For other schedule types, this would need more complex logic
        return None