from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required, user_passes_test
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.contrib import messages
from django.core.paginator import Paginator
from django.db.models import Q, Count, Prefetch
from django.http import JsonResponse, HttpResponse
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from django.urls import reverse_lazy
from django.utils import timezone
from django.db import transaction
from datetime import datetime, timedelta
import csv
import json

from .models import Codec, Zone, Channel, ChannelZone, Jingle, Show, EPGEntry
from .forms import (
    CodecForm, ZoneForm, ChannelForm, ChannelZoneForm, JingleForm,
    ShowForm, EPGEntryForm, EPGBulkImportForm, ChannelSearchForm
)


def can_manage_channels(user):
    """
    Check if user can manage channels (super admin, admin, or has channel management role).
    
    Args:
        user: User instance to check permissions for
    
    Returns:
        bool: True if user can manage channels, False otherwise
    """
    if user.is_superuser:
        return True
    
    if hasattr(user, 'roles'):
        # Check if user has admin role or channel management role
        user_roles = user.roles.filter(is_active=True).values_list('role__name', flat=True)
        return any(role in ['admin', 'channel_manager'] for role in user_roles)
    
    return False


def can_view_channels(user):
    """
    Check if user can view channels (all authenticated users).
    
    Args:
        user: User instance to check permissions for
    
    Returns:
        bool: True if user can view channels, False otherwise
    """
    return user.is_authenticated


class ChannelManagementMixin(UserPassesTestMixin):
    """
    Mixin to restrict access to channel management views.
    
    Only allows access to users with channel management permissions.
    """
    
    def test_func(self):
        """
        Test if user has channel management permissions.
        """
        return can_manage_channels(self.request.user)
    
    def handle_no_permission(self):
        """
        Handle cases where user doesn't have permission.
        """
        messages.error(
            self.request,
            'You do not have permission to manage channels. Contact your administrator.'
        )
        return redirect('channels:channel_list')


class ChannelViewMixin(LoginRequiredMixin):
    """
    Mixin to restrict access to channel viewing.
    
    Allows access to all authenticated users.
    """
    pass


# Dashboard and Overview Views

@login_required
def dashboard(request):
    """
    Main dashboard view for channels app.
    
    Displays overview statistics and recent activity for channels,
    zones, shows, and EPG entries.
    """
    # Get statistics
    stats = {
        'total_channels': Channel.objects.count(),
        'active_channels': Channel.objects.filter(is_active=True).count(),
        'total_zones': Zone.objects.count(),
        'active_zones': Zone.objects.filter(is_active=True).count(),
        'total_shows': Show.objects.count(),
        'active_shows': Show.objects.filter(is_active=True).count(),
        'total_epg_entries': EPGEntry.objects.count(),
        'today_epg_entries': EPGEntry.objects.filter(
            start_time__date=timezone.now().date()
        ).count(),
    }
    
    # Get recent activity
    recent_channels = Channel.objects.filter(is_active=True).order_by('-created_at')[:5]
    recent_shows = Show.objects.filter(is_active=True).order_by('-created_at')[:5]
    
    # Get today's EPG entries
    today_epg = EPGEntry.objects.filter(
        start_time__date=timezone.now().date()
    ).select_related('channel', 'show').order_by('start_time')[:10]
    
    # Check user permissions
    can_manage = can_manage_channels(request.user)
    
    context = {
        'stats': stats,
        'recent_channels': recent_channels,
        'recent_shows': recent_shows,
        'today_epg': today_epg,
        'can_manage': can_manage,
    }
    
    return render(request, 'channels/dashboard.html', context)


# Channel Views

class ChannelListView(ChannelViewMixin, ListView):
    """
    List view for displaying all channels with search and filtering.
    
    Provides pagination, search functionality, and filtering options
    for efficient channel browsing.
    """
    model = Channel
    template_name = 'channels/channel_list.html'
    context_object_name = 'channels'
    paginate_by = 20
    
    def get_queryset(self):
        """
        Get filtered and searched queryset of channels.
        """
        queryset = Channel.objects.select_related().prefetch_related(
            'zones', 'jingles'
        ).annotate(
            zones_count=Count('zones'),
            jingles_count=Count('jingles')
        )
        
        # Apply search filter
        search = self.request.GET.get('search')
        if search:
            queryset = queryset.filter(
                Q(name__icontains=search) |
                Q(call_sign__icontains=search) |
                Q(description__icontains=search)
            )
        
        # Apply filters
        channel_type = self.request.GET.get('channel_type')
        if channel_type:
            queryset = queryset.filter(channel_type=channel_type)
        
        content_rating = self.request.GET.get('content_rating')
        if content_rating:
            queryset = queryset.filter(content_rating=content_rating)
        
        if self.request.GET.get('is_hd'):
            queryset = queryset.filter(is_hd=True)
        
        if self.request.GET.get('is_4k'):
            queryset = queryset.filter(is_4k=True)
        
        if self.request.GET.get('is_active', 'true') == 'true':
            queryset = queryset.filter(is_active=True)
        
        return queryset.order_by('name')
    
    def get_context_data(self, **kwargs):
        """
        Add search form and permission context.
        """
        context = super().get_context_data(**kwargs)
        context['search_form'] = ChannelSearchForm(self.request.GET)
        context['can_manage'] = can_manage_channels(self.request.user)
        return context


class ChannelDetailView(ChannelViewMixin, DetailView):
    """
    Detail view for displaying comprehensive channel information.
    
    Shows channel details, zone configurations, jingles, and recent EPG entries.
    """
    model = Channel
    template_name = 'channels/channel_detail.html'
    context_object_name = 'channel'
    
    def get_queryset(self):
        """
        Optimize queryset with related data.
        """
        return Channel.objects.select_related().prefetch_related(
            'channel_zones__zone',
            'channel_zones__video_codec',
            'channel_zones__audio_codec',
            'jingles',
            'epg_entries__show'
        )
    
    def get_context_data(self, **kwargs):
        """
        Add related data and permission context.
        """
        context = super().get_context_data(**kwargs)
        channel = self.object
        
        # Get zone configurations
        context['zone_configs'] = channel.channel_zones.filter(
            is_active=True
        ).select_related('zone', 'video_codec', 'audio_codec')
        
        # Get jingles
        context['jingles'] = channel.jingles.filter(
            is_active=True
        ).order_by('jingle_type', '-priority')
        
        # Get recent and upcoming EPG entries
        now = timezone.now()
        context['recent_epg'] = channel.epg_entries.filter(
            end_time__gte=now - timedelta(hours=6)
        ).select_related('show').order_by('start_time')[:10]
        
        context['can_manage'] = can_manage_channels(self.request.user)
        return context


class ChannelCreateView(ChannelManagementMixin, CreateView):
    """
    Create view for adding new channels.
    
    Restricted to users with channel management permissions.
    """
    model = Channel
    form_class = ChannelForm
    template_name = 'channels/channel_form.html'
    success_url = reverse_lazy('channels:channel_list')
    
    def form_valid(self, form):
        """
        Handle successful form submission.
        """
        messages.success(
            self.request,
            f'Channel "{form.instance.name}" has been created successfully.'
        )
        return super().form_valid(form)


class ChannelUpdateView(ChannelManagementMixin, UpdateView):
    """
    Update view for editing existing channels.
    
    Restricted to users with channel management permissions.
    """
    model = Channel
    form_class = ChannelForm
    template_name = 'channels/channel_form.html'
    
    def get_success_url(self):
        """
        Redirect to channel detail after successful update.
        """
        return reverse_lazy('channels:channel_detail', kwargs={'pk': self.object.pk})
    
    def form_valid(self, form):
        """
        Handle successful form submission.
        """
        messages.success(
            self.request,
            f'Channel "{form.instance.name}" has been updated successfully.'
        )
        return super().form_valid(form)


class ChannelDeleteView(ChannelManagementMixin, DeleteView):
    """
    Delete view for removing channels.
    
    Restricted to users with channel management permissions.
    """
    model = Channel
    template_name = 'channels/channel_confirm_delete.html'
    success_url = reverse_lazy('channels:channel_list')
    
    def delete(self, request, *args, **kwargs):
        """
        Handle channel deletion with success message.
        """
        channel_name = self.get_object().name
        response = super().delete(request, *args, **kwargs)
        messages.success(
            request,
            f'Channel "{channel_name}" has been deleted successfully.'
        )
        return response


# Zone Views

class ZoneListView(ChannelViewMixin, ListView):
    """
    List view for displaying distribution zones.
    
    Shows all zones with channel count and activity status.
    """
    model = Zone
    template_name = 'channels/zone_list.html'
    context_object_name = 'zones'
    paginate_by = 20
    
    def get_queryset(self):
        """
        Get zones with channel count annotation.
        """
        return Zone.objects.annotate(
            channels_count=Count('zone_channels', filter=Q(zone_channels__is_active=True))
        ).order_by('name')
    
    def get_context_data(self, **kwargs):
        """
        Add permission context.
        """
        context = super().get_context_data(**kwargs)
        context['can_manage'] = can_manage_channels(self.request.user)
        return context


class ZoneCreateView(ChannelManagementMixin, CreateView):
    """
    Create view for adding new distribution zones.
    """
    model = Zone
    form_class = ZoneForm
    template_name = 'channels/zone_form.html'
    success_url = reverse_lazy('channels:zone_list')
    
    def form_valid(self, form):
        """
        Handle successful form submission.
        """
        messages.success(
            self.request,
            f'Zone "{form.instance.name}" has been created successfully.'
        )
        return super().form_valid(form)


# Show Views

class ShowListView(ChannelViewMixin, ListView):
    """
    List view for displaying TV shows.
    
    Provides search and filtering capabilities for show management.
    """
    model = Show
    template_name = 'channels/show_list.html'
    context_object_name = 'shows'
    paginate_by = 20
    
    def get_queryset(self):
        """
        Get filtered and searched queryset of shows.
        """
        queryset = Show.objects.annotate(
            epg_count=Count('epg_entries')
        )
        
        # Apply search filter
        search = self.request.GET.get('search')
        if search:
            queryset = queryset.filter(
                Q(title__icontains=search) |
                Q(description__icontains=search) |
                Q(cast__icontains=search) |
                Q(director__icontains=search)
            )
        
        # Apply filters
        show_type = self.request.GET.get('show_type')
        if show_type:
            queryset = queryset.filter(show_type=show_type)
        
        content_rating = self.request.GET.get('content_rating')
        if content_rating:
            queryset = queryset.filter(content_rating=content_rating)
        
        if self.request.GET.get('is_active', 'true') == 'true':
            queryset = queryset.filter(is_active=True)
        
        return queryset.order_by('title')
    
    def get_context_data(self, **kwargs):
        """
        Add permission context and filter choices.
        """
        context = super().get_context_data(**kwargs)
        context['can_manage'] = can_manage_channels(self.request.user)
        context['show_types'] = Show.SHOW_TYPE_CHOICES
        context['content_ratings'] = Show.CONTENT_RATING_CHOICES
        return context


class ShowDetailView(ChannelViewMixin, DetailView):
    """
    Detail view for displaying comprehensive show information.
    
    Shows show details and recent/upcoming EPG entries.
    """
    model = Show
    template_name = 'channels/show_detail.html'
    context_object_name = 'show'
    
    def get_context_data(self, **kwargs):
        """
        Add EPG entries and permission context.
        """
        context = super().get_context_data(**kwargs)
        show = self.object
        
        # Get recent and upcoming EPG entries
        now = timezone.now()
        context['recent_epg'] = show.epg_entries.filter(
            start_time__gte=now - timedelta(days=7)
        ).select_related('channel').order_by('start_time')[:20]
        
        context['can_manage'] = can_manage_channels(self.request.user)
        return context


class ShowCreateView(ChannelManagementMixin, CreateView):
    """
    Create view for adding new TV shows.
    """
    model = Show
    form_class = ShowForm
    template_name = 'channels/show_form.html'
    success_url = reverse_lazy('channels:show_list')
    
    def form_valid(self, form):
        """
        Handle successful form submission.
        """
        messages.success(
            self.request,
            f'Show "{form.instance.title}" has been created successfully.'
        )
        return super().form_valid(form)


# EPG Views

class EPGListView(ChannelViewMixin, ListView):
    """
    List view for displaying EPG entries with filtering.
    
    Provides date-based filtering and channel-specific views.
    """
    model = EPGEntry
    template_name = 'channels/epg_list.html'
    context_object_name = 'epg_entries'
    paginate_by = 50
    
    def get_queryset(self):
        """
        Get filtered EPG entries.
        """
        queryset = EPGEntry.objects.select_related('channel', 'show')
        
        # Filter by date
        date_str = self.request.GET.get('date')
        if date_str:
            try:
                filter_date = datetime.strptime(date_str, '%Y-%m-%d').date()
                queryset = queryset.filter(start_time__date=filter_date)
            except ValueError:
                pass
        else:
            # Default to today
            queryset = queryset.filter(start_time__date=timezone.now().date())
        
        # Filter by channel
        channel_id = self.request.GET.get('channel')
        if channel_id:
            queryset = queryset.filter(channel_id=channel_id)
        
        return queryset.order_by('start_time')
    
    def get_context_data(self, **kwargs):
        """
        Add channels and permission context.
        """
        context = super().get_context_data(**kwargs)
        context['channels'] = Channel.objects.filter(is_active=True).order_by('name')
        context['can_manage'] = can_manage_channels(self.request.user)
        context['selected_date'] = self.request.GET.get('date', timezone.now().date().isoformat())
        context['selected_channel'] = self.request.GET.get('channel', '')
        return context


class EPGCreateView(ChannelManagementMixin, CreateView):
    """
    Create view for adding new EPG entries.
    """
    model = EPGEntry
    form_class = EPGEntryForm
    template_name = 'channels/epg_form.html'
    success_url = reverse_lazy('channels:epg_list')
    
    def form_valid(self, form):
        """
        Handle successful form submission.
        """
        messages.success(
            self.request,
            f'EPG entry for "{form.instance.show.title}" has been created successfully.'
        )
        return super().form_valid(form)


# API Views for AJAX requests

@login_required
def channel_zones_api(request, channel_id):
    """
    API endpoint to get zones for a specific channel.
    
    Returns JSON data of zone configurations for the specified channel.
    """
    try:
        channel = get_object_or_404(Channel, id=channel_id)
        zones = channel.channel_zones.filter(is_active=True).select_related(
            'zone', 'video_codec', 'audio_codec'
        )
        
        data = []
        for zone_config in zones:
            data.append({
                'id': zone_config.id,
                'zone_name': zone_config.zone.name,
                'zone_code': zone_config.zone.code,
                'video_codec': zone_config.video_codec.name,
                'audio_codec': zone_config.audio_codec.name,
                'bitrate': zone_config.bitrate,
                'resolution': zone_config.resolution_display,
                'frame_rate': float(zone_config.frame_rate),
            })
        
        return JsonResponse({'zones': data})
    
    except Exception as e:
        return JsonResponse({'error': str(e)}, status=400)


@login_required
def epg_calendar_api(request):
    """
    API endpoint for EPG calendar data.
    
    Returns JSON data for calendar display of EPG entries.
    """
    try:
        start_date = request.GET.get('start')
        end_date = request.GET.get('end')
        channel_id = request.GET.get('channel')
        
        queryset = EPGEntry.objects.select_related('channel', 'show')
        
        if start_date and end_date:
            start = datetime.fromisoformat(start_date.replace('Z', '+00:00'))
            end = datetime.fromisoformat(end_date.replace('Z', '+00:00'))
            queryset = queryset.filter(
                start_time__gte=start,
                start_time__lt=end
            )
        
        if channel_id:
            queryset = queryset.filter(channel_id=channel_id)
        
        events = []
        for entry in queryset:
            events.append({
                'id': entry.id,
                'title': f"{entry.channel.name}: {entry.show.title}",
                'start': entry.start_time.isoformat(),
                'end': entry.end_time.isoformat(),
                'backgroundColor': '#007bff' if not entry.is_repeat else '#6c757d',
                'borderColor': '#007bff' if not entry.is_repeat else '#6c757d',
                'extendedProps': {
                    'channel': entry.channel.name,
                    'show': entry.show.title,
                    'episode_title': entry.episode_title or '',
                    'is_live': entry.is_live,
                    'is_premiere': entry.is_premiere,
                    'is_repeat': entry.is_repeat,
                }
            })
        
        return JsonResponse(events, safe=False)
    
    except Exception as e:
        return JsonResponse({'error': str(e)}, status=400)


# Utility Views

@user_passes_test(can_manage_channels)
def bulk_epg_import(request):
    """
    View for bulk importing EPG entries from CSV files.
    
    Allows channel managers to upload CSV files with EPG data.
    """
    if request.method == 'POST':
        form = EPGBulkImportForm(request.POST, request.FILES)
        if form.is_valid():
            try:
                csv_file = form.cleaned_data['csv_file']
                overwrite = form.cleaned_data['overwrite_existing']
                
                # Process CSV file
                decoded_file = csv_file.read().decode('utf-8')
                csv_reader = csv.DictReader(decoded_file.splitlines())
                
                created_count = 0
                updated_count = 0
                error_count = 0
                
                with transaction.atomic():
                    for row in csv_reader:
                        try:
                            # Parse row data
                            channel = Channel.objects.get(name=row['channel'])
                            show = Show.objects.get(title=row['show'])
                            start_time = datetime.fromisoformat(row['start_time'])
                            end_time = datetime.fromisoformat(row['end_time'])
                            
                            # Check for existing entry
                            existing = EPGEntry.objects.filter(
                                channel=channel,
                                start_time=start_time
                            ).first()
                            
                            if existing and overwrite:
                                # Update existing entry
                                existing.show = show
                                existing.end_time = end_time
                                existing.episode_title = row.get('episode_title', '')
                                existing.save()
                                updated_count += 1
                            elif not existing:
                                # Create new entry
                                EPGEntry.objects.create(
                                    channel=channel,
                                    show=show,
                                    start_time=start_time,
                                    end_time=end_time,
                                    episode_title=row.get('episode_title', '')
                                )
                                created_count += 1
                        
                        except Exception:
                            error_count += 1
                            continue
                
                messages.success(
                    request,
                    f'Import completed: {created_count} created, '
                    f'{updated_count} updated, {error_count} errors.'
                )
                return redirect('channels:epg_list')
            
            except Exception as e:
                messages.error(request, f'Import failed: {str(e)}')
    else:
        form = EPGBulkImportForm()
    
    return render(request, 'channels/epg_bulk_import.html', {'form': form})


@login_required
def export_epg_csv(request):
    """
    Export EPG entries to CSV format.
    
    Allows users to download EPG data for external processing.
    """
    # Get filter parameters
    start_date = request.GET.get('start_date')
    end_date = request.GET.get('end_date')
    channel_id = request.GET.get('channel')
    
    # Build queryset
    queryset = EPGEntry.objects.select_related('channel', 'show')
    
    if start_date:
        queryset = queryset.filter(start_time__date__gte=start_date)
    if end_date:
        queryset = queryset.filter(start_time__date__lte=end_date)
    if channel_id:
        queryset = queryset.filter(channel_id=channel_id)
    
    # Create CSV response
    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename="epg_export.csv"'
    
    writer = csv.writer(response)
    writer.writerow([
        'Channel', 'Show', 'Start Time', 'End Time', 'Episode Title',
        'Season', 'Episode', 'Is Live', 'Is Premiere', 'Is Repeat'
    ])
    
    for entry in queryset.order_by('start_time'):
        writer.writerow([
            entry.channel.name,
            entry.show.title,
            entry.start_time.isoformat(),
            entry.end_time.isoformat(),
            entry.episode_title or '',
            entry.season_number or '',
            entry.episode_number or '',
            entry.is_live,
            entry.is_premiere,
            entry.is_repeat,
        ])
    
    return response