"""
Media Hub — Video & Audio library with HLS/AES-128 self-hosted streaming.
"""
from django.db import models
from django.utils.text import slugify


class MediaCategory(models.Model):
    MEDIA_TYPE_CHOICES = [
        ('video', 'Video'),
        ('audio', 'Audio'),
        ('image', 'Image'),
        ('both', 'Both'),
    ]
    tenant      = models.ForeignKey('tenants.Tenant', on_delete=models.CASCADE, null=True, blank=True)
    name        = models.CharField(max_length=100)
    slug        = models.SlugField(max_length=120)
    media_type  = models.CharField(max_length=10, choices=MEDIA_TYPE_CHOICES, default='both')
    sort_order  = models.PositiveIntegerField(default=0)
    is_active   = models.BooleanField(default=True)

    class Meta:
        ordering = ['sort_order', 'name']
        verbose_name_plural = 'Media Categories'

    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = slugify(self.name)
        super().save(*args, **kwargs)


class MediaCollection(models.Model):
    COLLECTION_TYPES = [
        ('series',       'TV Series'),
        ('movie',        'Movie / Film'),
        ('documentary',  'Documentary'),
        ('podcast',      'Podcast'),
        ('audiobook',    'Audiobook'),
        ('album',        'Music Album'),
        ('playlist',     'Playlist'),
        ('gallery',      'Photo Gallery'),
    ]
    GALLERY_LAYOUTS = [
        ('slider',  'Slider: one image at a time, arrows and thumbnails'),
        ('grid',    'Grid: equal tiles, click to enlarge'),
        ('masonry', 'Masonry: natural heights, staggered columns'),
    ]
    ACCESS_CHOICES = [
        ('free',         'Free'),
        ('premium',      'Premium'),
        ('subscription', 'Subscription'),
    ]

    tenant          = models.ForeignKey('tenants.Tenant', on_delete=models.CASCADE, null=True, blank=True)
    title           = models.CharField(max_length=200)
    slug            = models.SlugField(max_length=220)
    description     = models.TextField(blank=True)
    thumbnail       = models.ImageField(upload_to='media/thumbnails/', blank=True, null=True)
    category        = models.ForeignKey(MediaCategory, on_delete=models.SET_NULL, null=True, blank=True)
    collection_type = models.CharField(max_length=20, choices=COLLECTION_TYPES, default='series')
    has_seasons     = models.BooleanField(default=False, help_text='Enable season grouping within this collection')
    gallery_layout  = models.CharField(
        max_length=20, choices=GALLERY_LAYOUTS, default='slider',
        help_text='How this collection is displayed when embedded'
    )
    access          = models.CharField(max_length=20, choices=ACCESS_CHOICES, default='free')
    year            = models.PositiveSmallIntegerField(null=True, blank=True)
    language        = models.CharField(max_length=100, blank=True)
    cast            = models.TextField(blank=True, help_text='Comma-separated cast list')
    director        = models.CharField(max_length=200, blank=True)
    is_active       = models.BooleanField(default=True)
    sort_order      = models.PositiveIntegerField(default=0)
    created_at      = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['sort_order', 'title']

    def __str__(self):
        return self.title

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = slugify(self.title)
        super().save(*args, **kwargs)

    @property
    def item_count(self):
        return self.items.filter(is_active=True).count()


class MediaSeason(models.Model):
    collection    = models.ForeignKey(MediaCollection, on_delete=models.CASCADE, related_name='seasons')
    title         = models.CharField(max_length=200, blank=True, help_text='Leave blank to auto-label "Season N"')
    season_number = models.PositiveSmallIntegerField(default=1)
    description   = models.TextField(blank=True)
    thumbnail     = models.ImageField(upload_to='media/thumbnails/', blank=True, null=True)
    is_active     = models.BooleanField(default=True)

    class Meta:
        ordering = ['season_number']
        unique_together = ['collection', 'season_number']

    def __str__(self):
        label = self.title or f'Season {self.season_number}'
        return f'{self.collection.title} — {label}'

    @property
    def display_title(self):
        return self.title or f'Season {self.season_number}'


class MediaItem(models.Model):
    MEDIA_TYPES = [
        ('video', 'Video'),
        ('audio', 'Audio'),
        ('image', 'Image'),
    ]
    SUBTYPES = [
        ('movie',        'Movie'),
        ('episode',      'Episode'),
        ('clip',         'Clip / Trailer'),
        ('documentary',  'Documentary'),
        ('music',        'Music Track'),
        ('podcast_ep',   'Podcast Episode'),
        ('audiobook_ch', 'Audiobook Chapter'),
        ('audiobook',    'Full Audiobook'),
        ('photo',        'Photo'),
        ('gallery',      'Gallery'),
    ]
    HLS_STATUS = [
        ('pending',    'Pending'),
        ('processing', 'Processing'),
        ('ready',      'Ready'),
        ('error',      'Error'),
    ]
    ACCESS_CHOICES = [
        ('free',         'Free'),
        ('premium',      'Premium'),
        ('subscription', 'Subscription'),
    ]

    # BBFC age ratings. Video only, and optional, since audio and images do not
    # carry one. Blank means unrated rather than suitable for all.
    RATING_CHOICES = [
        ('U',   'U, suitable for all'),
        ('PG',  'PG, parental guidance'),
        ('12A', '12A, 12 and over, under 12 with an adult'),
        ('12',  '12, suitable for 12 and over'),
        ('15',  '15, suitable for 15 and over'),
        ('18',  '18, adults only'),
        ('R18', 'R18, licensed premises only'),
    ]
    # shape and background for the badge. Drawn in CSS, so nothing is
    # downloaded and it stays sharp at any size.
    RATING_STYLES = {
        'U':   ('triangle', '#6cc04a'),
        'PG':  ('triangle', '#f5b335'),
        '12A': ('circle',   '#f07c22'),
        '12':  ('circle',   '#f07c22'),
        '15':  ('circle',   '#ec5f96'),
        '18':  ('circle',   '#c8102e'),
        'R18': ('square',   '#0f62c8'),
    }

    # Audio advisory. Music has no age ladder, only the RIAA Parental Advisory
    # scheme, which every platform mirrors: explicit, clean, or unflagged.
    # 'clean' means an edited version of a track that exists in explicit form.
    ADVISORY_CHOICES = [
        ('explicit', 'Explicit'),
        ('clean',    'Clean, edited version'),
    ]

    tenant         = models.ForeignKey('tenants.Tenant', on_delete=models.CASCADE, null=True, blank=True)
    title          = models.CharField(max_length=200)
    slug           = models.SlugField(max_length=220)
    description    = models.TextField(blank=True)
    thumbnail      = models.ImageField(upload_to='media/thumbnails/', blank=True, null=True)
    media_type     = models.CharField(max_length=10, choices=MEDIA_TYPES)
    subtype        = models.CharField(max_length=20, choices=SUBTYPES, default='episode')

    # Category (direct — for standalone items not inside a collection)
    category       = models.ForeignKey(MediaCategory, on_delete=models.SET_NULL, null=True, blank=True, related_name='items')

    # Hierarchy
    collection     = models.ForeignKey(MediaCollection, on_delete=models.SET_NULL, null=True, blank=True, related_name='items')
    season         = models.ForeignKey(MediaSeason, on_delete=models.SET_NULL, null=True, blank=True, related_name='items')
    episode_number = models.PositiveSmallIntegerField(null=True, blank=True)
    track_number   = models.PositiveSmallIntegerField(null=True, blank=True)

    # External source URL (YouTube, SoundCloud, etc.) — mutually exclusive with file upload
    source_url     = models.URLField(max_length=500, blank=True, help_text='YouTube or SoundCloud URL, leave blank if uploading a file')
    external_thumb = models.URLField(
        max_length=500, blank=True,
        help_text='Artwork fetched from the external platform, used when no thumbnail is uploaded'
    )

    # Upload — original file (kept until HLS is ready, then can be deleted)
    original_file  = models.FileField(upload_to='media/originals/', blank=True, null=True)

    # HLS output (stored in MEDIA_ROOT/hls/{pk}/)
    hls_path       = models.CharField(max_length=500, blank=True, help_text='Relative path to m3u8 inside MEDIA_ROOT')
    hls_status     = models.CharField(max_length=20, choices=HLS_STATUS, default='pending')
    hls_error      = models.TextField(blank=True)

    # Metadata
    duration       = models.PositiveIntegerField(null=True, blank=True, help_text='Duration in seconds')
    file_size      = models.PositiveBigIntegerField(null=True, blank=True, help_text='Original file size in bytes')

    access         = models.CharField(max_length=20, choices=ACCESS_CHOICES, default='subscription')
    content_rating = models.CharField(
        max_length=3, choices=RATING_CHOICES, blank=True,
        help_text='BBFC age rating. Leave blank if the title has not been rated.'
    )
    audio_advisory = models.CharField(
        max_length=8, choices=ADVISORY_CHOICES, blank=True,
        help_text='Parental Advisory flag. Leave blank for audio that is not flagged.'
    )
    year           = models.PositiveSmallIntegerField(null=True, blank=True)
    language       = models.CharField(max_length=100, blank=True)
    tags           = models.CharField(max_length=500, blank=True, help_text='Comma-separated tags')

    sort_order     = models.PositiveIntegerField(default=0)
    is_active      = models.BooleanField(default=True)
    created_at     = models.DateTimeField(auto_now_add=True)
    updated_at     = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['sort_order', 'episode_number', 'track_number', 'title']

    def __str__(self):
        return self.title

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = slugify(self.title)
        super().save(*args, **kwargs)

    @property
    def rating_shape(self):
        """'triangle', 'circle', 'square' or '' when unrated. For the badge."""
        style = self.RATING_STYLES.get(self.content_rating)
        return style[0] if style else ''

    @property
    def rating_colour(self):
        """Background for the badge, '' when unrated."""
        style = self.RATING_STYLES.get(self.content_rating)
        return style[1] if style else ''

    @property
    def external_platform(self):
        """Which platform an external item comes from: youtube, soundcloud or ''."""
        if not self.source_url:
            return ''
        url = self.source_url.lower()
        if 'youtube.com' in url or 'youtu.be' in url:
            return 'youtube'
        if 'soundcloud.com' in url:
            return 'soundcloud'
        return 'other'

    @property
    def youtube_thumb_url(self):
        """
        YouTube artwork without an API call. Thumbnails live at a predictable
        address, so the id from the URL is enough. Returns '' for anything else.
        """
        if not self.source_url:
            return ''
        from apps.media.embeds import YOUTUBE_ID
        match = YOUTUBE_ID.search(self.source_url)
        if not match:
            return ''
        return f'https://img.youtube.com/vi/{match.group(1)}/hqdefault.jpg'

    def fetch_external_thumb(self, timeout=6):
        """
        Ask SoundCloud for the track artwork through its public oEmbed
        endpoint. No key needed. Stores the result so it is only fetched once.
        Returns the URL, or '' if unavailable. Never raises.
        """
        if not self.source_url or 'soundcloud.com' not in self.source_url:
            return ''
        try:
            import json
            from urllib.parse import quote
            from urllib.request import Request, urlopen

            endpoint = (
                'https://soundcloud.com/oembed?format=json&url='
                + quote(self.source_url, safe='')
            )
            request = Request(endpoint, headers={'User-Agent': 'ThePanelCMS/1.0'})
            with urlopen(request, timeout=timeout) as response:
                payload = json.loads(response.read().decode('utf-8'))
            thumb = (payload.get('thumbnail_url') or '').strip()
        except Exception:
            return ''

        if thumb and thumb != self.external_thumb:
            self.external_thumb = thumb[:500]
            if self.pk:
                MediaItem.objects.filter(pk=self.pk).update(external_thumb=self.external_thumb)
        return self.external_thumb

    @property
    def display_thumb_url(self):
        """
        Best available image for this item, in order of preference:
        an uploaded thumbnail, then platform artwork, then the file itself.
        """
        if self.thumbnail and getattr(self.thumbnail, 'name', ''):
            return self.thumbnail.url
        yt = self.youtube_thumb_url
        if yt:
            return yt
        if self.external_thumb:
            return self.external_thumb
        if self.media_type == 'image' and self.original_file:
            return self.original_file.url
        return ''

    @property
    def duration_display(self):
        if not self.duration:
            return ''
        h, rem = divmod(self.duration, 3600)
        m, s = divmod(rem, 60)
        if h:
            return f'{h}:{m:02d}:{s:02d}'
        return f'{m}:{s:02d}'

    @property
    def is_ready(self):
        return self.hls_status == 'ready'


class MediaEncryptionKey(models.Model):
    """
    AES-128 key used to encrypt this item's HLS segments.
    The key is served via a token-protected Django endpoint — never directly.
    """
    item       = models.OneToOneField(MediaItem, on_delete=models.CASCADE, related_name='encryption_key')
    key_hex    = models.CharField(max_length=32)   # 16 bytes → 32 hex chars
    token      = models.CharField(max_length=64)   # random token in key URL
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f'Key for {self.item}'
