"""
Stream Management Views

This module contains views for managing streaming channels, sessions,
configurations, and real-time stream control operations.
"""

import os
import glob
from pathlib import Path
from django.shortcuts import render, get_object_or_404, redirect, reverse
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.decorators import login_required
from django.views.decorators.http import require_http_methods
from django.views.decorators.csrf import csrf_protect
from django.contrib import messages
from django.http import JsonResponse, Http404, HttpResponse
from django.urls import reverse_lazy
from django.views.generic import ListView, CreateView
from django.utils.decorators import method_decorator
from django.conf import settings
from django.views import View
from django.db.models import Q
from django.core.paginator import Paginator



from .models import Channel, StreamSession, HLSSegment, VideoConfiguration, AudioConfiguration
from .tasks import start_stream_capture, stop_stream_capture, create_playlist_for_channel
from .forms import ChannelForm, VideoConfigurationForm, AudioConfigurationForm
from apps.jingles.models import JingleTemplate


class ChannelListView(LoginRequiredMixin, View):
    """
    List view for streaming channels with filtering and search,
    implemented with the base View class.
    """

    template_name = "streams/channel/list.html"
    paginate_by = 20

    # Extracted for reuse and testability
    def get_queryset(self, request):
        qs = Channel.objects.select_related("created_by").order_by("-created_at")

        # Search
        search = (request.GET.get("search") or "").strip()
        if search:
            qs = qs.filter(
                Q(name__icontains=search)
                | Q(slug__icontains=search)
                | Q(description__icontains=search)
            )

        # Status filter
        status = (request.GET.get("status") or "").lower()
        if status == "active":
            qs = qs.filter(is_active=True)
        elif status == "inactive":
            qs = qs.filter(is_active=False)

        return qs

    def get(self, request, *args, **kwargs):
        queryset = self.get_queryset(request)

        # Pagination
        paginator = Paginator(queryset, self.paginate_by)
        page_obj = paginator.get_page(request.GET.get("page"))  # safe: handles bad pages

        # Add active session info (keeps your existing API)
        channels = list(page_obj.object_list)
        for channel in channels:
            channel.active_session = channel.get_active_session()

        context = {
            "channels": channels,
            "search": request.GET.get("search", ""),
            "status_filter": request.GET.get("status", ""),
            "paginator": paginator,
            "page_obj": page_obj,
            "is_paginated": page_obj.has_other_pages(),
        }
        return render(request, self.template_name, context)


class ChannelDetailView(LoginRequiredMixin, View):
    template_name = "streams/channel/detail.html"

    def get(self, request, pk, *args, **kwargs):
        channel = get_object_or_404(Channel, pk=pk)

        context = {
            "channel": channel,
            "recent_sessions": channel.sessions.select_related(
                "video_config", "audio_config"
            ).order_by("-started_at")[:10],
            "active_session": channel.get_active_session(),
            "video_configs": VideoConfiguration.objects.all(),
            "audio_configs": AudioConfiguration.objects.all(),
        }
        return render(request, self.template_name, context)


class ChannelCreateView(LoginRequiredMixin, View):
    template_name = "streams/channel/form.html"

    def get(self, request, *args, **kwargs):
        return render(request, self.template_name, {"form": ChannelForm()})

    def post(self, request, *args, **kwargs):
        form = ChannelForm(request.POST)
        if form.is_valid():
            channel = form.save(commit=False)
            channel.created_by = request.user
            channel.save()
            return redirect(reverse("streams:channel_list"))
        return render(request, self.template_name, {"form": form})


class ChannelUpdateView(LoginRequiredMixin, View):
    template_name = "streams/channel/form.html"

    def get_object(self, pk):
        return get_object_or_404(Channel, pk=pk)

    def get(self, request, pk, *args, **kwargs):
        channel = self.get_object(pk)
        form = ChannelForm(instance=channel)
        return render(request, self.template_name, {"form": form})

    def post(self, request, pk, *args, **kwargs):
        channel = self.get_object(pk)
        form = ChannelForm(request.POST, instance=channel)
        if form.is_valid():
            form.save()
            messages.success(request, "Channel updated successfully!")
            return redirect(reverse("streams:channel_detail", kwargs={"pk": channel.pk}))
        return render(request, self.template_name, {"form": form})


class StreamSessionListView(LoginRequiredMixin, View):
    template_name = "streams/session/list.html"
    paginate_by = 20

    def get_queryset(self, request):
        qs = StreamSession.objects.select_related(
            "channel", "video_config", "audio_config"
        ).order_by("-started_at")

        channel_id = request.GET.get("channel")
        if channel_id:
            qs = qs.filter(channel_id=channel_id)

        status = request.GET.get("status")
        if status:
            qs = qs.filter(status=status)

        # Optional: support search by channel name if present in template
        search = (request.GET.get("search") or "").strip()
        if search:
            qs = qs.filter(channel__name__icontains=search)

        return qs

    def get(self, request, *args, **kwargs):
        queryset = self.get_queryset(request)
        paginator = Paginator(queryset, self.paginate_by)
        page_obj = paginator.get_page(request.GET.get("page"))

        context = {
            "sessions": list(page_obj.object_list),
            "channels": Channel.objects.filter(is_active=True),
            "channel_filter": request.GET.get("channel", ""),
            "status_filter": request.GET.get("status", ""),
            "status_choices": StreamSession.STATUS_CHOICES,
            "paginator": paginator,
            "page_obj": page_obj,
            "is_paginated": page_obj.has_other_pages(),
            "request": request,  # some templates read request.GET directly
        }
        return render(request, self.template_name, context)


class StreamSessionDetailView(LoginRequiredMixin, View):
    template_name = "streams/session/detail.html"

    def get(self, request, pk, *args, **kwargs):
        session = get_object_or_404(StreamSession, pk=pk)

        segments_qs = session.segments.all()
        agg = segments_qs.aggregate(
            total_duration=Sum("duration"), total_size=Sum("file_size")
        )

        context = {
            "session": session,
            "recent_segments": session.segments.order_by("-processed_at")[:20],
            "total_segments": segments_qs.count(),
            "available_segments": segments_qs.filter(is_available=True).count(),
            "total_duration": agg["total_duration"],
            "total_size": agg["total_size"],
        }
        return render(request, self.template_name, context)


class ConfigurationListView(LoginRequiredMixin, ListView):
    """
    Combined list view for video and audio configurations.
    """
    
    model = VideoConfiguration  # Required for ListView
    template_name = 'streams/config/list.html'
    context_object_name = 'configurations'
    
    def get_queryset(self):
        """Return empty queryset since we handle data in get_context_data."""
        return VideoConfiguration.objects.none()
    
    def get_context_data(self, **kwargs):
        """Get both video and audio configurations."""
        context = super().get_context_data(**kwargs)
        context['video_configs'] = VideoConfiguration.objects.all().order_by('name')
        context['audio_configs'] = AudioConfiguration.objects.all().order_by('name')
        return context


class VideoConfigurationCreateView(LoginRequiredMixin, CreateView):
    """
    Create view for video configurations.
    """
    
    model = VideoConfiguration
    form_class = VideoConfigurationForm
    template_name = 'streams/config/video_form.html'
    success_url = reverse_lazy('streams:config_list')
    
    def form_valid(self, form):
        """Add success message."""
        messages.success(self.request, 'Video configuration created successfully!')
        return super().form_valid(form)


class AudioConfigurationCreateView(LoginRequiredMixin, CreateView):
    """
    Create view for audio configurations.
    """
    
    model = AudioConfiguration
    form_class = AudioConfigurationForm
    template_name = 'streams/config/audio_form.html'
    success_url = reverse_lazy('streams:config_list')
    
    def form_valid(self, form):
        """Add success message."""
        messages.success(self.request, 'Audio configuration created successfully!')
        return super().form_valid(form)


# Media Player Views
@login_required
def hls_player_view(request, channel_id):
    """
    HLS media player for live stream viewing.
    """
    channel = get_object_or_404(Channel, id=channel_id)
    active_session = channel.sessions.filter(status='active').first()
    
    context = {
        'channel': channel,
        'active_session': active_session,
        'hls_url': f'/media/streams/{channel.slug}/hls/index.m3u8' if active_session else None,
    }
    return render(request, 'streams/hls_player.html', context)


@login_required
def ts_file_browser(request, channel_id):
    """
    Browse and view TS segment files for a channel.
    """
    channel = get_object_or_404(Channel, id=channel_id)
    
    # Get TS files from the channel's HLS directory
    hls_path = Path(settings.STREAM_CONFIG['OUTPUT_DIR']) / channel.slug / 'hls'
    ts_files = []
    
    if hls_path.exists():
        ts_pattern = hls_path / '*.ts'
        ts_files = sorted(glob.glob(str(ts_pattern)), key=os.path.getmtime, reverse=True)
        ts_files = [{'path': f, 'name': os.path.basename(f), 'size': os.path.getsize(f)} for f in ts_files[:50]]
    
    context = {
        'channel': channel,
        'ts_files': ts_files,
    }
    return render(request, 'streams/ts_browser.html', context)


@login_required
def iframe_gallery(request, channel_id):
    """
    View extracted iframes for a channel.
    """
    channel = get_object_or_404(Channel, id=channel_id)
    
    # Get iframe files from the channel's iframes directory
    iframes_path = Path(settings.STREAM_CONFIG['OUTPUT_DIR']) / channel.slug / 'iframes'
    iframe_files = []
    
    if iframes_path.exists():
        iframe_pattern = iframes_path / '*.png'
        iframe_files = sorted(glob.glob(str(iframe_pattern)), key=os.path.getmtime, reverse=True)
        iframe_files = [{'path': f, 'name': os.path.basename(f)} for f in iframe_files[:100]]
    
    context = {
        'channel': channel,
        'iframe_files': iframe_files,
    }
    return render(request, 'streams/iframe_gallery.html', context)


@login_required
def jingle_preview(request, channel_id):
    """
    Preview jingle templates for a channel.
    """
    channel = get_object_or_404(Channel, id=channel_id)
    jingle_templates = channel.jingle_templates.filter(is_active=True)
    
    context = {
        'channel': channel,
        'jingle_templates': jingle_templates,
    }
    return render(request, 'streams/jingle_preview.html', context)


@login_required
def serve_media_file(request, channel_slug, file_type, filename):
    """
    Serve media files (TS, PNG, M3U8) for the media players.
    """
    channel = get_object_or_404(Channel, slug=channel_slug)
    
    # Define allowed file types and their directories
    file_mappings = {
        'hls': 'hls',
        'iframes': 'iframes',
        'jingles': '../jingles',  # Jingles are stored separately
    }
    
    if file_type not in file_mappings:
        raise Http404("Invalid file type")
    
    # Build file path
    if file_type == 'jingles':
        # Jingles are stored in the jingles app media directory
        file_path = Path(settings.MEDIA_ROOT) / 'jingles' / filename
    else:
        file_path = Path(settings.STREAM_CONFIG['OUTPUT_DIR']) / channel.slug / file_mappings[file_type] / filename
    
    if not file_path.exists() or not file_path.is_file():
        raise Http404("File not found")
    
    # Determine content type
    content_types = {
        '.ts': 'video/mp2t',
        '.m3u8': 'application/vnd.apple.mpegurl',
        '.png': 'image/png',
        '.jpg': 'image/jpeg',
        '.jpeg': 'image/jpeg',
    }
    
    file_ext = file_path.suffix.lower()
    content_type = content_types.get(file_ext, 'application/octet-stream')
    
    # Serve the file
    with open(file_path, 'rb') as f:
        response = HttpResponse(f.read(), content_type=content_type)
        response['Content-Disposition'] = f'inline; filename="{filename}"'
        return response



@login_required
@csrf_protect
@require_http_methods(["POST"])
def start_stream(request, channel_id):
    """
    HTMX endpoint to start stream capture for a channel.
    
    Args:
        request: HTTP request object
        channel_id: ID of the channel to start streaming
        
    Returns:
        HttpResponse: HTML content for HTMX swap
    """
    channel = get_object_or_404(Channel, id=channel_id)
    
    # Check if channel already has an active session
    if channel.get_active_session():
        messages.error(request, 'Channel already has an active stream session')
        return redirect('streams:channel_detail', pk=channel.pk)
    
    try:
        # Get optional configuration IDs from request
        video_config_id = request.POST.get('video_config')
        audio_config_id = request.POST.get('audio_config')
        
        # Start the stream capture task
        task = start_stream_capture.delay(
            channel_id=str(channel.id),
            video_config_id=video_config_id,
            audio_config_id=audio_config_id
        )
        
        messages.success(request, f'Stream capture started for {channel.name}')
        
        # For HTMX requests, return updated status section
        if request.headers.get('HX-Request'):
            # Get updated channel data
            channel.refresh_from_db()
            active_session = channel.get_active_session()
            
            return render(request, 'streams/partials/stream_status.html', {
                'channel': channel,
                'active_session': active_session
            })
        
        return redirect('streams:channel_detail', pk=channel.pk)
        
    except Exception as e:
        messages.error(request, f'Failed to start stream: {str(e)}')
        return redirect('streams:channel_detail', pk=channel.pk)


@login_required
@csrf_protect
@require_http_methods(["POST"])
def stop_stream(request, session_id):
    """
    HTMX endpoint to stop an active stream session.
    
    Args:
        request: HTTP request object
        session_id: ID of the session to stop
        
    Returns:
        HttpResponse: HTML content for HTMX swap
    """
    session = get_object_or_404(StreamSession, id=session_id)
    
    # Check if session is active
    if not session.is_running():
        messages.error(request, 'Session is not currently running')
        return redirect('streams:channel_detail', pk=session.channel.pk)
    
    try:
        # Stop the stream capture task
        task = stop_stream_capture.delay(str(session.id))
        
        messages.success(request, f'Stream stopped for {session.channel.name}')
        
        # For HTMX requests, return updated status section
        if request.headers.get('HX-Request'):
            # Get updated channel data
            channel = session.channel
            channel.refresh_from_db()
            active_session = channel.get_active_session()
            
            return render(request, 'streams/partials/stream_status.html', {
                'channel': channel,
                'active_session': active_session
            })
        
        return redirect('streams:channel_detail', pk=session.channel.pk)
        
    except Exception as e:
        messages.error(request, f'Failed to stop stream: {str(e)}')
        return redirect('streams:channel_detail', pk=session.channel.pk)


@login_required
@csrf_protect
@require_http_methods(["POST"])
def delete_session(request, session_id):
    """
    Delete a stream session and all associated segments.
    
    Args:
        request: HTTP request object
        session_id: ID of the session to delete
        
    Returns:
        HttpResponse: Redirect to session list
    """
    session = get_object_or_404(StreamSession, id=session_id)
    
    # Check if session is not running
    if session.is_running():
        messages.error(request, 'Cannot delete a running session. Stop it first.')
        return redirect('streams:session_detail', pk=session.id)
    
    try:
        channel_name = session.channel.name
        session.delete()
        
        messages.success(request, f'Session for {channel_name} has been deleted')
        return redirect('streams:session_list')
        
    except Exception as e:
        messages.error(request, f'Failed to delete session: {str(e)}')
        return redirect('streams:session_detail', pk=session.id)



def stream_status(request, session_id):
    """
    HTMX endpoint for real-time stream status updates.
    
    Args:
        request: HTTP request object
        session_id: ID of the session to check
        
    Returns:
        HttpResponse: HTML fragment with session status
    """
    try:
        session = get_object_or_404(StreamSession, id=session_id)
        
        context = {
            'session': session,
            'duration': session.duration(),
            'is_running': session.is_running()
        }
        
        return render(request, 'streams/partials/session_status.html', context)
        
    except Exception as e:
        return HttpResponse(f'<div class="text-red-500">Error: {e}</div>')


def segment_list(request, session_id):
    """
    HTMX endpoint for paginated segment list.
    
    Args:
        request: HTTP request object
        session_id: ID of the session
        
    Returns:
        HttpResponse: HTML fragment with segment list
    """
    session = get_object_or_404(StreamSession, id=session_id)
    segments = session.segments.order_by('-processed_at')
    
    # Pagination
    page = request.GET.get('page', 1)
    paginator = Paginator(segments, 10)
    segments_page = paginator.get_page(page)
    
    context = {
        'session': session,
        'segments': segments_page
    }
    
    return render(request, 'streams/partials/segment_list.html', context)
