"""
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
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, DetailView, CreateView, UpdateView
from django.core.paginator import Paginator
from django.contrib.auth.mixins import LoginRequiredMixin
from django.utils.decorators import method_decorator
from django.conf import settings

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, ListView):
    """
    List view for streaming channels with filtering and search capabilities.
    """
    
    model = Channel
    template_name = 'streams/channels/list.html'
    context_object_name = 'channels'
    paginate_by = 20
    
    def get_queryset(self):
        """Filter channels based on search and status parameters."""
        queryset = Channel.objects.select_related('created_by').order_by('-created_at')
        
        # Search functionality
        search = self.request.GET.get('search')
        if search:
            queryset = queryset.filter(
                name__icontains=search
            ) | queryset.filter(
                slug__icontains=search
            ) | queryset.filter(
                description__icontains=search
            )
        
        # Status filter
        status = self.request.GET.get('status')
        if status == 'active':
            queryset = queryset.filter(is_active=True)
        elif status == 'inactive':
            queryset = queryset.filter(is_active=False)
        
        return queryset
    
    def get_context_data(self, **kwargs):
        """Add additional context for the template."""
        context = super().get_context_data(**kwargs)
        context['search'] = self.request.GET.get('search', '')
        context['status_filter'] = self.request.GET.get('status', '')
        
        # Add active session information
        for channel in context['channels']:
            channel.active_session = channel.get_active_session()
        
        return context


class ChannelDetailView(LoginRequiredMixin, DetailView):
    """
    Detail view for individual channels with session history and controls.
    """
    
    model = Channel
    template_name = 'streams/channels/detail.html'
    context_object_name = 'channel'
    
    def get_context_data(self, **kwargs):
        """Add session history and configuration information."""
        context = super().get_context_data(**kwargs)
        
        # Get recent sessions
        context['recent_sessions'] = self.object.sessions.select_related(
            'video_config', 'audio_config'
        ).order_by('-started_at')[:10]
        
        # Get active session
        context['active_session'] = self.object.get_active_session()
        
        # Get available configurations
        context['video_configs'] = VideoConfiguration.objects.all()
        context['audio_configs'] = AudioConfiguration.objects.all()
        
        return context


class ChannelCreateView(LoginRequiredMixin, CreateView):
    """
    Create view for new streaming channels.
    """
    
    model = Channel
    form_class = ChannelForm
    template_name = 'streams/channels/form.html'
    success_url = reverse_lazy('streams:channel_list')
    
    def form_valid(self, form):
        """Set the current user as the channel creator."""
        form.instance.created_by = self.request.user
        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


class ChannelUpdateView(LoginRequiredMixin, UpdateView):
    """
    Update view for existing streaming channels.
    """
    
    model = Channel
    form_class = ChannelForm
    template_name = 'streams/channels/form.html'
    
    def get_success_url(self):
        """Redirect to channel detail after successful update."""
        return reverse_lazy('streams:channel_detail', kwargs={'pk': self.object.pk})
    
    def form_valid(self, form):
        """Add success message after update."""
        messages.success(self.request, 'Channel updated successfully!')
        return super().form_valid(form)


class StreamSessionListView(LoginRequiredMixin, ListView):
    """
    List view for stream sessions with filtering and pagination.
    """
    
    model = StreamSession
    template_name = 'streams/sessions/list.html'
    context_object_name = 'sessions'
    paginate_by = 20
    
    def get_queryset(self):
        """Filter sessions based on channel and status."""
        queryset = StreamSession.objects.select_related(
            'channel', 'video_config', 'audio_config'
        ).order_by('-started_at')
        
        # Filter by channel
        channel_id = self.request.GET.get('channel')
        if channel_id:
            queryset = queryset.filter(channel_id=channel_id)
        
        # Filter by status
        status = self.request.GET.get('status')
        if status:
            queryset = queryset.filter(status=status)
        
        return queryset
    
    def get_context_data(self, **kwargs):
        """Add filter options to context."""
        context = super().get_context_data(**kwargs)
        context['channels'] = Channel.objects.filter(is_active=True)
        context['channel_filter'] = self.request.GET.get('channel', '')
        context['status_filter'] = self.request.GET.get('status', '')
        context['status_choices'] = StreamSession.STATUS_CHOICES
        return context


class StreamSessionDetailView(LoginRequiredMixin, DetailView):
    """
    Detail view for individual stream sessions with segment information.
    """
    
    model = StreamSession
    template_name = 'streams/sessions/detail.html'
    context_object_name = 'session'
    
    def get_context_data(self, **kwargs):
        """Add segment information and statistics."""
        context = super().get_context_data(**kwargs)
        
        # Get recent segments
        context['recent_segments'] = self.object.segments.order_by('-processed_at')[:20]
        
        # Calculate statistics
        segments = self.object.segments.all()
        context['total_segments'] = segments.count()
        context['available_segments'] = segments.filter(is_available=True).count()
        
        if segments.exists():
            total_duration = sum(seg.duration for seg in segments if seg.duration)
            context['total_duration'] = total_duration
            
            total_size = sum(seg.file_size for seg in segments if seg.file_size)
            context['total_size'] = total_size
        
        return context


@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)


class ConfigurationListView(LoginRequiredMixin, ListView):
    """
    Combined list view for video and audio configurations.
    """
    
    model = VideoConfiguration  # Required for ListView
    template_name = 'streams/configs/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/configs/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/configs/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)


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)


@login_required
def iframe_gallery(request, channel_id):
    """
    Display iframe gallery for a specific channel.
    
    Shows extracted I-frames from HLS segments for jingle detection
    and visual monitoring of the stream content.
    
    Args:
        request: HTTP request object
        channel_id: UUID of the channel
        
    Returns:
        HttpResponse: Rendered iframe gallery page
    """
    channel = get_object_or_404(Channel, id=channel_id)
    
    # Get iframe directory path
    try:
        base_path = Path(settings.STREAM_CONFIG['OUTPUT_DIR']) / channel.slug
        iframes_path = base_path / 'iframes'
    except (KeyError, AttributeError):
        # Fallback to media root
        base_path = Path(settings.MEDIA_ROOT) / 'streams' / channel.slug
        iframes_path = base_path / 'iframes'
    
    # Get all iframe files
    iframe_files = []
    if iframes_path.exists():
        # Get all PNG files in the iframes directory
        for pattern in ['*.png', '*.jpg', '*.jpeg']:
            iframe_files.extend(glob.glob(str(iframes_path / pattern)))
        
        # Sort by modification time (newest first)
        iframe_files.sort(key=lambda x: os.path.getmtime(x), reverse=True)
        
        # Convert to relative paths for serving
        iframe_files = [
            {
                'path': os.path.relpath(f, settings.MEDIA_ROOT),
                'filename': os.path.basename(f),
                'size': os.path.getsize(f),
                'modified': os.path.getmtime(f)
            }
            for f in iframe_files
        ]
    
    # Pagination
    page = request.GET.get('page', 1)
    paginator = Paginator(iframe_files, 24)  # 24 images per page
    iframe_page = paginator.get_page(page)
    
    # Get recent sessions for context
    recent_sessions = channel.sessions.order_by('-started_at')[:5]
    
    context = {
        'channel': channel,
        'iframe_files': iframe_page,
        'recent_sessions': recent_sessions,
        'total_iframes': len(iframe_files),
        'iframes_path': str(iframes_path)
    }
    
    return render(request, 'streams/iframe_gallery.html', context)


@login_required
def hls_player_view(request, channel_id):
    """
    HLS player view for a specific channel.
    
    Args:
        request: HTTP request object
        channel_id: UUID of the channel
        
    Returns:
        HttpResponse: Rendered HLS player page
    """
    channel = get_object_or_404(Channel, id=channel_id)
    active_session = channel.get_active_session()
    
    context = {
        'channel': channel,
        'active_session': active_session
    }
    
    return render(request, 'streams/hls_player.html', context)


@login_required
def ts_file_browser(request, channel_id):
    """
    TS file browser for a specific channel.
    
    Args:
        request: HTTP request object
        channel_id: UUID of the channel
        
    Returns:
        HttpResponse: Rendered TS file browser page
    """
    channel = get_object_or_404(Channel, id=channel_id)
    
    context = {
        'channel': channel
    }
    
    return render(request, 'streams/ts_browser.html', context)


@login_required
def jingle_preview(request, channel_id):
    """
    Jingle preview for a specific channel.
    
    Args:
        request: HTTP request object
        channel_id: UUID of the channel
        
    Returns:
        HttpResponse: Rendered jingle preview page
    """
    channel = get_object_or_404(Channel, id=channel_id)
    
    context = {
        'channel': channel
    }
    
    return render(request, 'streams/jingle_preview.html', context)


@login_required
def serve_media_file(request, channel_slug, file_type, filename):
    """
    Serve media files for a specific channel.
    
    Args:
        request: HTTP request object
        channel_slug: Slug of the channel
        file_type: Type of file (segments, iframes, etc.)
        filename: Name of the file
        
    Returns:
        HttpResponse: File response or 404
    """
    try:
        # Get the file path
        base_path = Path(settings.MEDIA_ROOT) / 'streams' / channel_slug / file_type
        file_path = base_path / filename
        
        # Security check - ensure file is within allowed directory
        if not str(file_path.resolve()).startswith(str(base_path.resolve())):
            raise Http404("File not found")
        
        if not file_path.exists():
            raise Http404("File not found")
        
        # Determine content type
        content_type = 'application/octet-stream'
        if filename.endswith('.ts'):
            content_type = 'video/mp2t'
        elif filename.endswith('.m3u8'):
            content_type = 'application/vnd.apple.mpegurl'
        elif filename.endswith(('.png', '.jpg', '.jpeg')):
            content_type = f'image/{filename.split(".")[-1]}'
        
        # 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
            
    except Exception:
        raise Http404("File not found")
