"""CMS views for the Media Hub."""
import threading

from django.contrib import messages
from django.http import JsonResponse, Http404
from django.shortcuts import render, redirect, get_object_or_404
from django.utils.text import slugify
from django.views.decorators.http import require_POST

from apps.accounts.decorators import admin_required, module_required
from .models import MediaCategory, MediaCollection, MediaSeason, MediaItem
from .utils import generate_hls, delete_hls


# ── Helpers ───────────────────────────────────────────────────────────────────

def _tenant(request):
    return getattr(request, 'tenant', None)


def _process_hls_background(item):
    """Kick off HLS transcoding in a daemon thread (no Celery required)."""
    t = threading.Thread(target=generate_hls, args=(item,), daemon=True)
    t.start()


# ══════════════════════════════════════════════════════════════════════════════
# LIBRARY
# ══════════════════════════════════════════════════════════════════════════════

@admin_required
@module_required('media')
def media_library(request):
    tenant = _tenant(request)
    qs = MediaItem.objects.select_related('collection', 'season', 'tenant')
    if tenant:
        qs = qs.filter(tenant=tenant)

    # Filters
    media_type  = request.GET.get('type', '')
    status      = request.GET.get('status', '')
    collection  = request.GET.get('collection', '')
    cat_f       = request.GET.get('cat', '')
    q           = request.GET.get('q', '').strip()

    if media_type:
        qs = qs.filter(media_type=media_type)
    if status:
        qs = qs.filter(hls_status=status)
    if collection:
        qs = qs.filter(collection_id=collection)
    if cat_f:
        from django.db.models import Q
        qs = qs.filter(Q(category_id=cat_f) | Q(collection__category_id=cat_f))
    if q:
        qs = qs.filter(title__icontains=q)

    collections = MediaCollection.objects.filter(tenant=tenant) if tenant else MediaCollection.objects.all()
    categories  = MediaCategory.objects.filter(is_active=True)

    # Annotate category counts (items directly in category OR via collection)
    from django.db.models import Q
    for cat in categories:
        base_qs = MediaItem.objects.filter(tenant=tenant) if tenant else MediaItem.objects.all()
        cat.item_count = base_qs.filter(Q(category=cat) | Q(collection__category=cat)).distinct().count()

    return render(request, 'cms/media/library.html', {
        'items':       qs.order_by('-created_at'),
        'collections': collections,
        'categories':  categories,
        'total':       MediaItem.objects.count(),
        'filter_type':   media_type,
        'filter_status': status,
        'filter_col':    collection,
        'cat_f':         cat_f,
        'q':             q,
    })


# ══════════════════════════════════════════════════════════════════════════════
# MEDIA ITEM — ADD / EDIT
# ══════════════════════════════════════════════════════════════════════════════

@admin_required
@module_required('media')
def media_item_edit(request, pk=None):
    tenant = _tenant(request)
    item   = get_object_or_404(MediaItem, pk=pk, tenant=tenant) if pk else None

    collections = MediaCollection.objects.filter(tenant=tenant) if tenant else MediaCollection.objects.all()
    categories  = MediaCategory.objects.filter(is_active=True)

    if request.method == 'POST':
        title      = request.POST.get('title', '').strip()
        media_type = request.POST.get('media_type', 'video')
        subtype    = request.POST.get('subtype', '')
        col_id     = request.POST.get('collection') or None
        season_id  = request.POST.get('season') or None
        access     = request.POST.get('access', 'free')

        if not title:
            messages.error(request, 'Title is required.')
            return redirect(request.path)

        if not item:
            item = MediaItem(tenant=tenant)

        item.title          = title
        item.media_type     = media_type
        item.subtype        = subtype
        item.description    = request.POST.get('description', '')
        item.access         = access
        # Video only. Switching an item to audio or image clears any rating it
        # picked up, so nothing stale is shown against it.
        rating = request.POST.get('content_rating', '')
        valid  = {c[0] for c in MediaItem.RATING_CHOICES}
        item.content_rating = rating if (media_type == 'video' and rating in valid) else ''
        # Audio only, same reasoning
        advisory = request.POST.get('audio_advisory', '')
        valid_adv = {c[0] for c in MediaItem.ADVISORY_CHOICES}
        item.audio_advisory = advisory if (media_type == 'audio' and advisory in valid_adv) else ''
        item.year           = request.POST.get('year') or None
        item.language       = request.POST.get('language', '')
        item.tags           = request.POST.get('tags', '')
        item.category_id    = request.POST.get('category') or None
        item.episode_number = request.POST.get('episode_number') or None
        item.track_number   = request.POST.get('track_number') or None
        item.is_active      = 'is_active' in request.POST

        if col_id:
            item.collection_id = col_id
        else:
            item.collection = None
            item.season     = None

        if season_id and col_id:
            item.season_id = season_id
        elif not col_id:
            item.season = None

        if not item.slug:
            item.slug = slugify(title)

        item.source_url = request.POST.get('source_url', '').strip()

        thumb_b64 = request.POST.get('thumbnail_b64', '').strip()
        if thumb_b64 or 'thumbnail' in request.FILES:
            source = thumb_b64 if thumb_b64 else request.FILES['thumbnail']
            orig   = (title or 'thumbnail') if thumb_b64 else request.FILES['thumbnail'].name
            try:
                from apps.accounts.cms_views import _convert_to_webp
                webp_file, _w, _h = _convert_to_webp(source, orig)
                item.thumbnail = webp_file
            except Exception:
                if 'thumbnail' in request.FILES:
                    item.thumbnail = request.FILES['thumbnail']

        is_new = item.pk is None
        item.save()

        # Handle file upload or external URL
        if 'media_file' in request.FILES:
            f = request.FILES['media_file']
            item.original_file = f
            item.file_size     = f.size
            if item.media_type == 'image':
                item.hls_status = 'ready'
                item.hls_path   = ''
                item.hls_error  = ''
                item.save(update_fields=['original_file', 'file_size', 'hls_status', 'hls_path', 'hls_error'])
                messages.success(request, f'"{item.title}" saved.')
            else:
                # HLS conversion is opt in. The original file is kept and served
                # as is until someone presses Convert to HLS on the item page.
                item.hls_status = 'pending'
                item.hls_path   = ''
                item.hls_error  = ''
                item.save(update_fields=['original_file', 'file_size', 'hls_status', 'hls_path', 'hls_error'])
                messages.success(
                    request,
                    f'"{item.title}" saved. Use Convert to HLS on this item if you want streaming.'
                )
        elif item.source_url:
            # External URL, ready immediately, no HLS needed
            item.hls_status = 'ready'
            item.hls_path   = ''
            item.hls_error  = ''
            item.save(update_fields=['source_url', 'hls_status', 'hls_path', 'hls_error'])
            # SoundCloud artwork needs one lookup. YouTube does not, its
            # thumbnail is derived from the URL. Failure here is not fatal.
            if 'soundcloud.com' in item.source_url and not item.external_thumb:
                item.fetch_external_thumb()
            messages.success(request, f'"{item.title}" saved.')
        else:
            messages.success(request, f'"{item.title}" saved.')

        return redirect('cms_media_library')

    # Seasons for selected collection (populated via JS on change)
    seasons = []
    if item and item.collection_id:
        seasons = list(item.collection.seasons.values('id', 'season_number', 'title'))

    return render(request, 'cms/media/item_edit.html', {
        'item':        item,
        'collections': collections,
        'categories':  categories,
        'seasons':     seasons,
        'rating_choices':   MediaItem.RATING_CHOICES,
        'advisory_choices': MediaItem.ADVISORY_CHOICES,
    })


@admin_required
@require_POST
def media_item_thumbnail_delete(request, pk):
    tenant = _tenant(request)
    item   = get_object_or_404(MediaItem, pk=pk, tenant=tenant)
    if item.thumbnail and item.thumbnail.name:
        try:
            item.thumbnail.delete(save=False)
        except Exception:
            pass
        item.thumbnail = None
        item.save(update_fields=['thumbnail'])
        messages.success(request, 'Thumbnail deleted.')
    return redirect('cms_media_item_edit', pk=pk)


@admin_required
@require_POST
def media_item_delete(request, pk):
    tenant = _tenant(request)
    item   = get_object_or_404(MediaItem, pk=pk, tenant=tenant)
    title  = item.title
    delete_hls(item)
    if item.original_file:
        try:
            item.original_file.delete(save=False)
        except Exception:
            pass
    item.delete()
    messages.warning(request, f'"{title}" deleted.')
    return redirect('cms_media_library')


@admin_required
@require_POST
def media_item_reprocess(request, pk):
    """Re-trigger HLS transcoding (e.g. after an error)."""
    tenant = _tenant(request)
    item   = get_object_or_404(MediaItem, pk=pk, tenant=tenant)
    if not item.original_file:
        return JsonResponse({'ok': False, 'error': 'No source file.'})
    delete_hls(item.pk)
    _process_hls_background(item)
    return JsonResponse({'ok': True})


@admin_required
@require_POST
def media_collection_thumbnail_delete(request, pk):
    """Remove a collection thumbnail and return to its edit page."""
    tenant = _tenant(request)
    col    = get_object_or_404(MediaCollection, pk=pk, tenant=tenant)
    if col.thumbnail and col.thumbnail.name:
        try:
            col.thumbnail.delete(save=False)
        except Exception:
            pass
        col.thumbnail = None
        col.save(update_fields=['thumbnail'])
        messages.success(request, 'Thumbnail removed.')
    return redirect('cms_media_collection_edit', pk=col.pk)


@admin_required
@require_POST
def media_item_convert(request, pk):
    """Start HLS conversion for an item on request. The original file is kept."""
    tenant = _tenant(request)
    item   = get_object_or_404(MediaItem, pk=pk, tenant=tenant)

    if not item.original_file:
        messages.error(request, 'There is no uploaded file to convert.')
        return redirect('cms_media_item_edit', pk=item.pk)
    if item.media_type == 'image':
        messages.error(request, 'Images do not need HLS conversion.')
        return redirect('cms_media_item_edit', pk=item.pk)
    if item.hls_status == 'processing':
        messages.info(request, 'Conversion is already running.')
        return redirect('cms_media_item_edit', pk=item.pk)

    delete_hls(item.pk)
    item.hls_status = 'processing'
    item.hls_path   = ''
    item.hls_error  = ''
    item.save(update_fields=['hls_status', 'hls_path', 'hls_error'])
    _process_hls_background(item)
    messages.success(request, 'Conversion started. This page will update when it finishes.')
    return redirect('cms_media_item_edit', pk=item.pk)


@admin_required
@module_required('media')
def media_item_status(request, pk):
    """Polled by JS to get current hls_status."""
    tenant = _tenant(request)
    item   = get_object_or_404(MediaItem, pk=pk, tenant=tenant)
    return JsonResponse({
        'status':   item.hls_status,
        'duration': item.duration_display,
        'error':    item.hls_error,
    })


# ══════════════════════════════════════════════════════════════════════════════
# COLLECTIONS
# ══════════════════════════════════════════════════════════════════════════════

@admin_required
@module_required('media')
def media_collection_list(request):
    tenant = _tenant(request)
    qs     = list(MediaCollection.objects.select_related('category').filter(tenant=tenant) if tenant else MediaCollection.objects.select_related('category'))
    for col in qs:
        # Gather up to 6 preview URLs from items
        thumbs = []
        for it in col.items.filter(is_active=True)[:12]:
            if it.thumbnail and it.thumbnail.name:
                thumbs.append(it.thumbnail.url)
            elif it.media_type == 'image' and it.original_file and it.original_file.name:
                thumbs.append(it.original_file.url)
            if len(thumbs) >= 6:
                break
        col.preview_thumbs = thumbs
    return render(request, 'cms/media/collection_list.html', {'collections': qs})


@admin_required
@module_required('media')
def media_collection_edit(request, pk=None):
    tenant = _tenant(request)
    col    = get_object_or_404(MediaCollection, pk=pk, tenant=tenant) if pk else None
    cats   = MediaCategory.objects.filter(is_active=True)

    if request.method == 'POST':
        title = request.POST.get('title', '').strip()
        if not title:
            messages.error(request, 'Title is required.')
            return redirect(request.path)

        if not col:
            col = MediaCollection(tenant=tenant)

        col.title           = title
        col.slug            = slugify(title)
        col.description     = request.POST.get('description', '')
        col.collection_type = request.POST.get('collection_type', 'series')
        col.has_seasons     = 'has_seasons' in request.POST

        col.gallery_layout = request.POST.get('gallery_layout') or 'slider'
        col.access          = request.POST.get('access', 'subscription')
        col.year            = request.POST.get('year') or None
        col.language        = request.POST.get('language', '')
        col.cast            = request.POST.get('cast', '')
        col.director        = request.POST.get('director', '')
        col.sort_order      = int(request.POST.get('sort_order', 0) or 0)
        col.is_active       = 'is_active' in request.POST
        cat_id              = request.POST.get('category') or None
        col.category_id     = cat_id

        thumb_b64 = request.POST.get('thumbnail_b64', '').strip()
        if thumb_b64 or 'thumbnail' in request.FILES:
            source = thumb_b64 if thumb_b64 else request.FILES['thumbnail']
            orig   = (title or 'collection') if thumb_b64 else request.FILES['thumbnail'].name
            try:
                from apps.accounts.cms_views import _convert_to_webp
                webp_file, _w, _h = _convert_to_webp(source, orig)
                col.thumbnail = webp_file
            except Exception:
                if 'thumbnail' in request.FILES:
                    col.thumbnail = request.FILES['thumbnail']

        if not col.collection_type:
            messages.error(request, 'Please choose a collection type.')
            return redirect(request.path)

        col.save()
        messages.success(request, f'"{col.title}" saved.')
        return redirect('cms_media_collections')

    seasons = col.seasons.all() if col else []
    return render(request, 'cms/media/collection_edit.html', {
        'col':     col,
        'cats':    cats,
        'seasons': seasons,
    })


@admin_required
@require_POST
def media_collection_delete(request, pk):
    tenant = _tenant(request)
    col    = get_object_or_404(MediaCollection, pk=pk, tenant=tenant)
    title  = col.title
    col.delete()
    messages.success(request, f'"{title}" deleted.')
    return redirect('cms_media_collections')


# ── Seasons (inline within collection edit) ───────────────────────────────────

@admin_required
@require_POST
def media_season_save(request, collection_pk):
    tenant = _tenant(request)
    col    = get_object_or_404(MediaCollection, pk=collection_pk, tenant=tenant)
    pk     = request.POST.get('pk') or None

    season = get_object_or_404(MediaSeason, pk=pk, collection=col) if pk else MediaSeason(collection=col)
    season.season_number = int(request.POST.get('season_number', 1))
    season.title         = request.POST.get('title', '')
    season.description   = request.POST.get('description', '')
    season.is_active     = 'is_active' in request.POST
    if 'thumbnail' in request.FILES:
        season.thumbnail = request.FILES['thumbnail']
    season.save()
    return redirect('cms_media_collection_edit', pk=collection_pk)


@admin_required
@require_POST
def media_season_delete(request, pk):
    tenant = _tenant(request)
    season = get_object_or_404(MediaSeason, pk=pk)
    if season.collection.tenant != tenant and tenant:
        raise Http404
    col_pk = season.collection_id
    season.delete()
    return redirect('cms_media_collection_edit', pk=col_pk)


# Season items API (for JS autocomplete in item edit form)
@admin_required
@module_required('media')
def media_season_list_api(request, collection_pk):
    tenant  = _tenant(request)
    col     = get_object_or_404(MediaCollection, pk=collection_pk, tenant=tenant)
    seasons = list(col.seasons.values('id', 'season_number', 'title'))
    return JsonResponse({'seasons': seasons})


# ══════════════════════════════════════════════════════════════════════════════
# CATEGORIES
# ══════════════════════════════════════════════════════════════════════════════

@admin_required
@module_required('media')
def media_category_list(request):
    tenant = _tenant(request)
    qs     = MediaCategory.objects.all()
    if tenant:
        qs = qs.filter(tenant=tenant)
    return render(request, 'cms/media/category_list.html', {'categories': qs})


@admin_required
@module_required('media')
def media_category_edit(request, pk=None):
    tenant = _tenant(request)
    cat    = get_object_or_404(MediaCategory, pk=pk) if pk else None

    if request.method == 'POST':
        name = request.POST.get('name', '').strip()
        if not name:
            messages.error(request, 'Name is required.')
            return redirect(request.path)

        if not cat:
            cat = MediaCategory(tenant=tenant)

        cat.name       = name
        cat.slug       = slugify(name)
        cat.media_type = request.POST.get('media_type', 'both')
        cat.sort_order = int(request.POST.get('sort_order', 0) or 0)
        cat.is_active  = 'is_active' in request.POST
        cat.save()
        messages.success(request, f'"{cat.name}" saved.')
        return redirect('cms_media_categories')

    return render(request, 'cms/media/category_edit.html', {'cat': cat})


@admin_required
@require_POST
def media_category_delete(request, pk):
    cat = get_object_or_404(MediaCategory, pk=pk)
    cat.delete()
    messages.success(request, 'Category deleted.')
    return redirect('cms_media_categories')
