"""
HLS transcoding with AES-128 encryption.
Requires ffmpeg and ffprobe installed on the server.
"""
import json
import os
import secrets
import subprocess
from pathlib import Path

from django.conf import settings


def _hls_root() -> Path:
    """Protected HLS store — outside MEDIA_ROOT so Nginx can't serve it directly."""
    root = getattr(settings, 'HLS_ROOT', None)
    if root:
        return Path(root)
    # Default: sibling of MEDIA_ROOT called 'hls_store'
    return Path(settings.MEDIA_ROOT).parent / 'hls_store'


def _hls_dir(item) -> Path:
    """Per-item HLS directory, organised by media type."""
    media_type = getattr(item, 'media_type', None) or 'video'
    pk         = item if isinstance(item, int) else item.pk
    return _hls_root() / media_type / str(pk)


def generate_hls(item):
    """
    Convert item.original_file to encrypted HLS and update item.hls_* fields.
    Runs synchronously — call from a background thread or Celery task for large files.
    """
    from apps.media.models import MediaEncryptionKey

    item.hls_status = 'processing'
    item.hls_error = ''
    item.save(update_fields=['hls_status', 'hls_error'])

    try:
        out_dir = _hls_dir(item)
        out_dir.mkdir(parents=True, exist_ok=True)

        # ── 1. Generate AES-128 key ──────────────────────────────────────────
        key_bytes = secrets.token_bytes(16)
        key_hex   = key_bytes.hex()
        token     = secrets.token_urlsafe(48)

        key_file      = out_dir / 'enc.key'
        key_info_file = out_dir / 'key.info'
        key_file.write_bytes(key_bytes)

        key_url  = f'/stream/key/{item.pk}/?token={token}'
        key_info_file.write_text(f'{key_url}\n{key_file}\n')

        # ── 2. Build ffmpeg command ──────────────────────────────────────────
        input_path   = item.original_file.path
        output_m3u8  = out_dir / 'index.m3u8'
        seg_pattern  = str(out_dir / 'seg_%04d.ts')

        if item.media_type == 'video':
            codec_args = ['-c:v', 'libx264', '-preset', 'fast', '-crf', '23', '-c:a', 'aac', '-b:a', '128k']
        else:
            # Audio only — strip video stream if any
            codec_args = ['-vn', '-c:a', 'aac', '-b:a', '192k']

        cmd = [
            'ffmpeg', '-i', str(input_path),
            *codec_args,
            '-hls_time', '10',
            '-hls_playlist_type', 'vod',
            '-hls_key_info_file', str(key_info_file),
            '-hls_segment_filename', seg_pattern,
            '-y', str(output_m3u8),
        ]

        result = subprocess.run(cmd, capture_output=True, text=True, timeout=7200)

        if result.returncode != 0:
            raise RuntimeError(result.stderr[-2000:])  # last 2 KB of stderr

        # ── 3. Probe duration ────────────────────────────────────────────────
        try:
            probe = subprocess.run(
                ['ffprobe', '-v', 'quiet', '-print_format', 'json',
                 '-show_format', str(input_path)],
                capture_output=True, text=True, timeout=60,
            )
            info = json.loads(probe.stdout)
            item.duration = int(float(info['format']['duration']))
        except Exception:
            pass

        # ── 4. Persist key ───────────────────────────────────────────────────
        MediaEncryptionKey.objects.update_or_create(
            item=item,
            defaults={'key_hex': key_hex, 'token': token},
        )

        # ── 5. Update item ───────────────────────────────────────────────────
        item.hls_path   = f'{item.media_type}/{item.pk}/index.m3u8'  # relative to HLS_ROOT
        item.hls_status = 'ready'
        item.hls_error  = ''
        item.save(update_fields=['hls_path', 'hls_status', 'hls_error', 'duration'])

    except Exception as exc:
        item.hls_status = 'error'
        item.hls_error  = str(exc)
        item.save(update_fields=['hls_status', 'hls_error'])
        raise


def delete_hls(item):
    """Remove all HLS segments + key files for this item."""
    import shutil
    out_dir = _hls_dir(item)
    if out_dir.exists():
        shutil.rmtree(out_dir)
