"""
Satellite CRUD operations and related views.
"""
from datetime import datetime

from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.paginator import Paginator
from django.db.models import Count, Q
from django.http import JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse, reverse_lazy
from django.views import View
from django.views.generic import CreateView, UpdateView

from ..forms import SatelliteForm
from ..mixins import RoleRequiredMixin, FormMessageMixin
from ..models import Satellite, Band
from ..permissions import PermissionRequiredMixin
from ..utils import parse_pagination_params


class SatelliteListView(LoginRequiredMixin, View):
    """View for displaying a list of satellites with filtering and pagination."""
    
    def get(self, request):
        # Get pagination parameters - default to "Все" (all items) for satellites
        # If no items_per_page is specified, use MAX_ITEMS_PER_PAGE
        from ..utils import MAX_ITEMS_PER_PAGE
        default_per_page = MAX_ITEMS_PER_PAGE if not request.GET.get("items_per_page") else None
        if default_per_page:
            page_number, items_per_page = parse_pagination_params(request, default_per_page=default_per_page)
        else:
            page_number, items_per_page = parse_pagination_params(request)
        
        # Get sorting parameters (default to name)
        sort_param = request.GET.get("sort", "name")
        
        # Get filter parameters
        search_query = request.GET.get("search", "").strip()
        selected_bands = request.GET.getlist("band_id")
        selected_location_places = request.GET.getlist("location_place")
        norad_min = request.GET.get("norad_min", "").strip()
        norad_max = request.GET.get("norad_max", "").strip()
        undersat_point_min = request.GET.get("undersat_point_min", "").strip()
        undersat_point_max = request.GET.get("undersat_point_max", "").strip()
        launch_date_from = request.GET.get("launch_date_from", "").strip()
        launch_date_to = request.GET.get("launch_date_to", "").strip()
        date_from = request.GET.get("date_from", "").strip()
        date_to = request.GET.get("date_to", "").strip()
        transponder_count_min = request.GET.get("transponder_count_min", "").strip()
        transponder_count_max = request.GET.get("transponder_count_max", "").strip()
        
        # Get all bands for filters
        bands = Band.objects.all().order_by("name")
        
        # Get all satellites with query optimization
        satellites = Satellite.objects.prefetch_related(
            'band',
            'created_by__user',
            'updated_by__user'
        ).annotate(
            transponder_count=Count('tran_satellite', distinct=True)
        )
        
        # Apply filters
        # Filter by bands
        if selected_bands:
            satellites = satellites.filter(band__id__in=selected_bands).distinct()
        
        # Filter by location_place
        if selected_location_places:
            satellites = satellites.filter(location_place__in=selected_location_places)
        
        # Filter by NORAD ID
        if norad_min:
            try:
                min_val = int(norad_min)
                satellites = satellites.filter(norad__gte=min_val)
            except ValueError:
                pass
        
        if norad_max:
            try:
                max_val = int(norad_max)
                satellites = satellites.filter(norad__lte=max_val)
            except ValueError:
                pass
        
        # Filter by undersat point
        if undersat_point_min:
            try:
                min_val = float(undersat_point_min)
                satellites = satellites.filter(undersat_point__gte=min_val)
            except ValueError:
                pass
        
        if undersat_point_max:
            try:
                max_val = float(undersat_point_max)
                satellites = satellites.filter(undersat_point__lte=max_val)
            except ValueError:
                pass
        
        # Filter by launch date range
        if launch_date_from:
            try:
                date_from_obj = datetime.strptime(launch_date_from, "%Y-%m-%d").date()
                satellites = satellites.filter(launch_date__gte=date_from_obj)
            except (ValueError, TypeError):
                pass
        
        if launch_date_to:
            try:
                from datetime import timedelta
                date_to_obj = datetime.strptime(launch_date_to, "%Y-%m-%d").date()
                # Add one day to include entire end date
                date_to_obj = date_to_obj + timedelta(days=1)
                satellites = satellites.filter(launch_date__lt=date_to_obj)
            except (ValueError, TypeError):
                pass
        
        # Filter by creation date range
        if date_from:
            try:
                date_from_obj = datetime.strptime(date_from, "%Y-%m-%d")
                satellites = satellites.filter(created_at__gte=date_from_obj)
            except (ValueError, TypeError):
                pass
        
        if date_to:
            try:
                from datetime import timedelta
                date_to_obj = datetime.strptime(date_to, "%Y-%m-%d")
                # Add one day to include entire end date
                date_to_obj = date_to_obj + timedelta(days=1)
                satellites = satellites.filter(created_at__lt=date_to_obj)
            except (ValueError, TypeError):
                pass
        
        # Search by name, alternative_name, or comment
        if search_query:
            satellites = satellites.filter(
                Q(name__icontains=search_query) |
                Q(alternative_name__icontains=search_query) |
                Q(comment__icontains=search_query)
            )
        
        # Filter by transponder count
        if transponder_count_min:
            try:
                min_val = int(transponder_count_min)
                satellites = satellites.filter(transponder_count__gte=min_val)
            except ValueError:
                pass
        
        if transponder_count_max:
            try:
                max_val = int(transponder_count_max)
                satellites = satellites.filter(transponder_count__lte=max_val)
            except ValueError:
                pass
        
        # Apply sorting
        valid_sort_fields = {
            "id": "id",
            "-id": "-id",
            "name": "name",
            "-name": "-name",
            "alternative_name": "alternative_name",
            "-alternative_name": "-alternative_name",
            "norad": "norad",
            "-norad": "-norad",
            "international_code": "international_code",
            "-international_code": "-international_code",
            "undersat_point": "undersat_point",
            "-undersat_point": "-undersat_point",
            "launch_date": "launch_date",
            "-launch_date": "-launch_date",
            "created_at": "created_at",
            "-created_at": "-created_at",
            "updated_at": "updated_at",
            "-updated_at": "-updated_at",
            "transponder_count": "transponder_count",
            "-transponder_count": "-transponder_count",
            "location_place": "location_place",
            "-location_place": "-location_place",
        }
        
        if sort_param in valid_sort_fields:
            satellites = satellites.order_by(valid_sort_fields[sort_param])
        
        # Create paginator
        paginator = Paginator(satellites, items_per_page)
        page_obj = paginator.get_page(page_number)
        
        # Prepare data for display
        processed_satellites = []
        for satellite in page_obj:
            # Get band names
            band_names = [f"{band.name}({int(band.border_start)}-{int(band.border_end)})" for band in satellite.band.all()]
            
            # Get location_place display value
            location_place_display = dict(Satellite.PLACES).get(satellite.location_place, "-") if satellite.location_place else "-"
            
            processed_satellites.append({
                'id': satellite.id,
                'name': satellite.name or "-",
                'alternative_name': satellite.alternative_name or "-",
                'location_place': location_place_display,
                'norad': satellite.norad if satellite.norad else "-",
                'international_code': satellite.international_code or "-",
                'bands': ", ".join(band_names) if band_names else "-",
                'undersat_point': f"{satellite.undersat_point:.2f}" if satellite.undersat_point is not None else "-",
                'launch_date': satellite.launch_date.strftime("%d.%m.%Y") if satellite.launch_date else "-",
                'url': satellite.url or "-",
                'comment': satellite.comment or "-",
                'transponder_count': satellite.transponder_count,
                'created_at': satellite.created_at,
                'updated_at': satellite.updated_at,
                'created_by': satellite.created_by if satellite.created_by else "-",
                'updated_by': satellite.updated_by if satellite.updated_by else "-",
            })
        
        # Prepare context for template
        context = {
            'page_obj': page_obj,
            'processed_satellites': processed_satellites,
            'items_per_page': items_per_page,
            'available_items_per_page': [50, 100, 500, 1000, 'Все'],
            'sort': sort_param,
            'search_query': search_query,
            'bands': bands,
            'selected_bands': [
                int(x) if isinstance(x, str) else x for x in selected_bands 
                if (isinstance(x, int) or (isinstance(x, str) and x.isdigit()))
            ],
            'location_places': Satellite.PLACES,
            'selected_location_places': selected_location_places,
            'norad_min': norad_min,
            'norad_max': norad_max,
            'undersat_point_min': undersat_point_min,
            'undersat_point_max': undersat_point_max,
            'launch_date_from': launch_date_from,
            'launch_date_to': launch_date_to,
            'date_from': date_from,
            'date_to': date_to,
            'transponder_count_min': transponder_count_min,
            'transponder_count_max': transponder_count_max,
            'full_width_page': True,
        }
        
        return render(request, "mainapp/satellite_list.html", context)


class SatelliteCreateView(PermissionRequiredMixin, FormMessageMixin, CreateView):
    """View for creating a new satellite."""
    permission_required = 'satellite_create'
    model = Satellite
    form_class = SatelliteForm
    template_name = "mainapp/satellite_form.html"
    success_url = reverse_lazy("mainapp:satellite_list")
    success_message = "Спутник успешно создан!"
    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['action'] = 'create'
        context['title'] = 'Создание спутника'
        return context
    
    def form_valid(self, form):
        form.instance.created_by = self.request.user.customuser
        form.instance.updated_by = self.request.user.customuser
        return super().form_valid(form)


class SatelliteUpdateView(PermissionRequiredMixin, FormMessageMixin, UpdateView):
    """View for updating an existing satellite."""
    permission_required = 'satellite_edit'
    model = Satellite
    form_class = SatelliteForm
    template_name = "mainapp/satellite_form.html"
    success_url = reverse_lazy("mainapp:satellite_list")
    success_message = "Спутник успешно обновлен!"
    
    def get_context_data(self, **kwargs):
        import json
        context = super().get_context_data(**kwargs)
        context['action'] = 'update'
        context['title'] = f'Редактирование спутника: {self.object.name}'
        
        # Get transponders for this satellite for frequency plan
        from mapsapp.models import Transponders
        transponders = Transponders.objects.filter(
            sat_id=self.object
        ).select_related('polarization').order_by('downlink')
        
        # Prepare transponder data for frequency plan visualization
        transponder_data = []
        for t in transponders:
            if t.downlink and t.frequency_range:
                transponder_data.append({
                    'id': t.id,
                    'name': t.name or f"TP-{t.id}",
                    'downlink': float(t.downlink),
                    'uplink': float(t.uplink) if t.uplink else None,
                    'frequency_range': float(t.frequency_range),
                    'polarization': t.polarization.name if t.polarization else '-',
                    'zone_name': t.zone_name or '-',
                })
        
        context['transponders'] = json.dumps(transponder_data)
        context['transponder_count'] = len(transponder_data)
        
        return context
    
    def form_valid(self, form):
        form.instance.updated_by = self.request.user.customuser
        return super().form_valid(form)


class DeleteSelectedSatellitesView(PermissionRequiredMixin, View):
    """View for deleting multiple selected satellites with confirmation."""
    permission_required = 'satellite_delete'
    
    def get(self, request):
        """Show confirmation page with details about satellites to be deleted."""
        ids = request.GET.get("ids", "")
        if not ids:
            messages.error(request, "Не выбраны спутники для удаления")
            return redirect('mainapp:satellite_list')
        
        try:
            id_list = [int(x) for x in ids.split(",") if x.isdigit()]
            satellites = Satellite.objects.filter(id__in=id_list).prefetch_related(
                'band'
            ).annotate(
                transponder_count=Count('tran_satellite', distinct=True)
            )
            
            # Prepare detailed information about satellites
            satellites_info = []
            total_transponders = 0
            
            for satellite in satellites:
                transponder_count = satellite.transponder_count
                total_transponders += transponder_count
                
                satellites_info.append({
                    'id': satellite.id,
                    'name': satellite.name or "-",
                    'norad': satellite.norad if satellite.norad else "-",
                    'transponder_count': transponder_count,
                })
            
            context = {
                'satellites_info': satellites_info,
                'total_satellites': len(satellites_info),
                'total_transponders': total_transponders,
                'ids': ids,
            }
            
            return render(request, 'mainapp/satellite_bulk_delete_confirm.html', context)
            
        except Exception as e:
            messages.error(request, f'Ошибка при подготовке удаления: {str(e)}')
            return redirect('mainapp:satellite_list')
    
    def post(self, request):
        """Actually delete the selected satellites."""
        ids = request.POST.get("ids", "")
        if not ids:
            return JsonResponse({"error": "Нет ID для удаления"}, status=400)
        
        try:
            id_list = [int(x) for x in ids.split(",") if x.isdigit()]
            
            # Get count before deletion
            satellites = Satellite.objects.filter(id__in=id_list)
            deleted_count = satellites.count()
            
            # Delete satellites (cascade will handle related objects)
            satellites.delete()
            
            messages.success(
                request, 
                f'Успешно удалено спутников: {deleted_count}'
            )
            
            return JsonResponse({
                "success": True,
                "message": f"Успешно удалено спутников: {deleted_count}",
                "deleted_count": deleted_count,
            })
            
        except Exception as e:
            return JsonResponse({"error": f"Ошибка при удалении: {str(e)}"}, status=500)
