"""
Base views and utilities.
"""
from datetime import datetime, timedelta
from django.contrib.auth import logout
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.paginator import Paginator
from django.db.models import Count, Q
from django.shortcuts import redirect, render
from django.views import View

from ..models import Source, ObjItem, Satellite, Modulation, Polarization, ObjectMark
from ..utils import parse_pagination_params


class HomeView(LoginRequiredMixin, View):
    """
    Main page with filters for displaying sources or objitems.
    """
    
    def get(self, request):
        # Get pagination parameters
        page_number, items_per_page = parse_pagination_params(request)
        
        # Get display mode: 'sources' or 'objitems'
        display_mode = request.GET.get("display_mode", "sources")
        
        # Get filter parameters
        selected_satellites = request.GET.getlist("satellite_id")
        date_from = request.GET.get("date_from", "").strip()
        date_to = request.GET.get("date_to", "").strip()
        freq_min = request.GET.get("freq_min", "").strip()
        freq_max = request.GET.get("freq_max", "").strip()
        range_min = request.GET.get("range_min", "").strip()
        range_max = request.GET.get("range_max", "").strip()
        selected_modulations = request.GET.getlist("modulation")
        selected_polarizations = request.GET.getlist("polarization")
        
        # Source-specific filters
        has_coords_average = request.GET.get("has_coords_average")
        has_kupsat = request.GET.get("has_kupsat")
        has_valid = request.GET.get("has_valid")
        has_reference = request.GET.get("has_reference")
        
        # ObjItem-specific filters
        has_geo = request.GET.get("has_geo")
        has_lyngsat = request.GET.get("has_lyngsat")
        
        # Marks filters
        show_marks = request.GET.get("show_marks", "0")
        marks_date_from = request.GET.get("marks_date_from", "").strip()
        marks_date_to = request.GET.get("marks_date_to", "").strip()
        marks_status = request.GET.get("marks_status", "")  # all, present, absent
        
        # Get all satellites, modulations, polarizations for filters
        satellites = Satellite.objects.all().order_by("name")
        modulations = Modulation.objects.all().order_by("name")
        polarizations = Polarization.objects.all().order_by("name")
        
        # Prepare context
        context = {
            'display_mode': display_mode,
            'satellites': satellites,
            'modulations': modulations,
            'polarizations': polarizations,
            'selected_satellites': [int(x) for x in selected_satellites if x.isdigit()],
            'selected_modulations': [int(x) for x in selected_modulations if x.isdigit()],
            'selected_polarizations': [int(x) for x in selected_polarizations if x.isdigit()],
            'date_from': date_from,
            'date_to': date_to,
            'freq_min': freq_min,
            'freq_max': freq_max,
            'range_min': range_min,
            'range_max': range_max,
            'has_coords_average': has_coords_average,
            'has_kupsat': has_kupsat,
            'has_valid': has_valid,
            'has_reference': has_reference,
            'has_geo': has_geo,
            'has_lyngsat': has_lyngsat,
            'show_marks': show_marks,
            'marks_date_from': marks_date_from,
            'marks_date_to': marks_date_to,
            'marks_status': marks_status,
            'items_per_page': items_per_page,
            'available_items_per_page': [50, 100, 500, 1000],
            'full_width_page': True,
        }
        
        if display_mode == "objitems":
            # Display ObjItems
            queryset = self._get_objitems_queryset(
                selected_satellites, date_from, date_to,
                freq_min, freq_max, range_min, range_max,
                selected_modulations, selected_polarizations,
                has_geo, has_lyngsat
            )
            
            paginator = Paginator(queryset, items_per_page)
            page_obj = paginator.get_page(page_number)
            
            processed_objitems = self._process_objitems(
                page_obj, show_marks, marks_date_from, marks_date_to, marks_status
            )
            
            context.update({
                'page_obj': page_obj,
                'processed_objitems': processed_objitems,
            })
        else:
            # Display Sources
            queryset = self._get_sources_queryset(
                selected_satellites, date_from, date_to,
                freq_min, freq_max, range_min, range_max,
                selected_modulations, selected_polarizations,
                has_coords_average, has_kupsat, has_valid, has_reference
            )
            
            paginator = Paginator(queryset, items_per_page)
            page_obj = paginator.get_page(page_number)
            
            processed_sources = self._process_sources(
                page_obj, show_marks, marks_date_from, marks_date_to, marks_status
            )
            
            context.update({
                'page_obj': page_obj,
                'processed_sources': processed_sources,
            })
        
        return render(request, "mainapp/home.html", context)
    
    def _get_sources_queryset(self, selected_satellites, date_from, date_to,
                              freq_min, freq_max, range_min, range_max,
                              selected_modulations, selected_polarizations,
                              has_coords_average, has_kupsat, has_valid, has_reference):
        """Build queryset for sources with filters."""
        sources = Source.objects.prefetch_related(
            'source_objitems',
            'source_objitems__parameter_obj',
            'source_objitems__parameter_obj__id_satellite',
            'source_objitems__geo_obj'
        ).annotate(objitem_count=Count('source_objitems'))
        
        # Filter by satellites
        if selected_satellites:
            sources = sources.filter(
                source_objitems__parameter_obj__id_satellite_id__in=selected_satellites
            ).distinct()
        
        # Filter by date range (using Geo timestamps)
        if date_from or date_to:
            geo_filter = Q()
            if date_from:
                try:
                    date_from_obj = datetime.strptime(date_from, "%Y-%m-%d")
                    geo_filter &= Q(source_objitems__geo_obj__timestamp__gte=date_from_obj)
                except (ValueError, TypeError):
                    pass
            
            if date_to:
                try:
                    date_to_obj = datetime.strptime(date_to, "%Y-%m-%d") + timedelta(days=1)
                    geo_filter &= Q(source_objitems__geo_obj__timestamp__lt=date_to_obj)
                except (ValueError, TypeError):
                    pass
            
            if geo_filter:
                sources = sources.filter(geo_filter).distinct()
        
        # Filter by frequency
        if freq_min:
            try:
                sources = sources.filter(
                    source_objitems__parameter_obj__frequency__gte=float(freq_min)
                ).distinct()
            except ValueError:
                pass
        
        if freq_max:
            try:
                sources = sources.filter(
                    source_objitems__parameter_obj__frequency__lte=float(freq_max)
                ).distinct()
            except ValueError:
                pass
        
        # Filter by frequency range
        if range_min:
            try:
                sources = sources.filter(
                    source_objitems__parameter_obj__freq_range__gte=float(range_min)
                ).distinct()
            except ValueError:
                pass
        
        if range_max:
            try:
                sources = sources.filter(
                    source_objitems__parameter_obj__freq_range__lte=float(range_max)
                ).distinct()
            except ValueError:
                pass
        
        # Filter by modulation
        if selected_modulations:
            sources = sources.filter(
                source_objitems__parameter_obj__modulation_id__in=selected_modulations
            ).distinct()
        
        # Filter by polarization
        if selected_polarizations:
            sources = sources.filter(
                source_objitems__parameter_obj__polarization_id__in=selected_polarizations
            ).distinct()
        
        # Filter by coordinates presence
        if has_coords_average == "1":
            sources = sources.filter(coords_average__isnull=False)
        elif has_coords_average == "0":
            sources = sources.filter(coords_average__isnull=True)
        
        if has_kupsat == "1":
            sources = sources.filter(coords_kupsat__isnull=False)
        elif has_kupsat == "0":
            sources = sources.filter(coords_kupsat__isnull=True)
        
        if has_valid == "1":
            sources = sources.filter(coords_valid__isnull=False)
        elif has_valid == "0":
            sources = sources.filter(coords_valid__isnull=True)
        
        if has_reference == "1":
            sources = sources.filter(coords_reference__isnull=False)
        elif has_reference == "0":
            sources = sources.filter(coords_reference__isnull=True)
        
        return sources.order_by('-id')
    
    def _get_objitems_queryset(self, selected_satellites, date_from, date_to,
                               freq_min, freq_max, range_min, range_max,
                               selected_modulations, selected_polarizations,
                               has_geo, has_lyngsat):
        """Build queryset for objitems with filters."""
        objitems = ObjItem.objects.select_related(
            'parameter_obj',
            'parameter_obj__id_satellite',
            'parameter_obj__modulation',
            'parameter_obj__polarization',
            'geo_obj',
            'source',
            'lyngsat_source'
        )
        
        # Filter by satellites
        if selected_satellites:
            objitems = objitems.filter(parameter_obj__id_satellite_id__in=selected_satellites)
        
        # Filter by date range
        if date_from:
            try:
                date_from_obj = datetime.strptime(date_from, "%Y-%m-%d")
                objitems = objitems.filter(geo_obj__timestamp__gte=date_from_obj)
            except (ValueError, TypeError):
                pass
        
        if date_to:
            try:
                date_to_obj = datetime.strptime(date_to, "%Y-%m-%d") + timedelta(days=1)
                objitems = objitems.filter(geo_obj__timestamp__lt=date_to_obj)
            except (ValueError, TypeError):
                pass
        
        # Filter by frequency
        if freq_min:
            try:
                objitems = objitems.filter(parameter_obj__frequency__gte=float(freq_min))
            except ValueError:
                pass
        
        if freq_max:
            try:
                objitems = objitems.filter(parameter_obj__frequency__lte=float(freq_max))
            except ValueError:
                pass
        
        # Filter by frequency range
        if range_min:
            try:
                objitems = objitems.filter(parameter_obj__freq_range__gte=float(range_min))
            except ValueError:
                pass
        
        if range_max:
            try:
                objitems = objitems.filter(parameter_obj__freq_range__lte=float(range_max))
            except ValueError:
                pass
        
        # Filter by modulation
        if selected_modulations:
            objitems = objitems.filter(parameter_obj__modulation_id__in=selected_modulations)
        
        # Filter by polarization
        if selected_polarizations:
            objitems = objitems.filter(parameter_obj__polarization_id__in=selected_polarizations)
        
        # Filter by coordinates presence
        if has_geo == "1":
            objitems = objitems.filter(geo_obj__isnull=False)
        elif has_geo == "0":
            objitems = objitems.filter(geo_obj__isnull=True)
        
        # Filter by LyngSat connection
        if has_lyngsat == "1":
            objitems = objitems.filter(lyngsat_source__isnull=False)
        elif has_lyngsat == "0":
            objitems = objitems.filter(lyngsat_source__isnull=True)
        
        return objitems.order_by('-id')
    
    def _process_sources(self, page_obj, show_marks="0", marks_date_from="", marks_date_to="", marks_status=""):
        """Process sources for display."""
        processed = []
        
        for source in page_obj:
            # Get satellites
            satellite_names = set()
            for objitem in source.source_objitems.all():
                if objitem.parameter_obj and objitem.parameter_obj.id_satellite:
                    satellite_names.add(objitem.parameter_obj.id_satellite.name)
            
            # Format coordinates
            def format_coords(point):
                if point:
                    lon, lat = point.coords[0], point.coords[1]
                    lon_str = f"{lon}E" if lon > 0 else f"{abs(lon)}W"
                    lat_str = f"{lat}N" if lat > 0 else f"{abs(lat)}S"
                    return f"{lat_str} {lon_str}"
                return "-"
            
            # Отметки теперь привязаны к TechAnalyze, а не к Source
            # marks_data оставляем пустым для обратной совместимости
            marks_data = []
            
            processed.append({
                'id': source.id,
                'satellites': ", ".join(sorted(satellite_names)) if satellite_names else "-",
                'objitem_count': source.objitem_count,
                'coords_average': format_coords(source.coords_average),
                'coords_kupsat': format_coords(source.coords_kupsat),
                'coords_valid': format_coords(source.coords_valid),
                'coords_reference': format_coords(source.coords_reference),
                'created_at': source.created_at,
                'marks': marks_data,
            })
        
        return processed
    
    def _process_objitems(self, page_obj, show_marks="0", marks_date_from="", marks_date_to="", marks_status=""):
        """Process objitems for display."""
        processed = []
        
        for objitem in page_obj:
            param = objitem.parameter_obj
            geo = objitem.geo_obj
            source = objitem.source
            
            # Format geo coordinates
            geo_coords = "-"
            geo_date = "-"
            if geo and geo.coords:
                lon, lat = geo.coords.coords[0], geo.coords.coords[1]
                lon_str = f"{lon}E" if lon > 0 else f"{abs(lon)}W"
                lat_str = f"{lat}N" if lat > 0 else f"{abs(lat)}S"
                geo_coords = f"{lat_str} {lon_str}"
                if geo.timestamp:
                    geo_date = geo.timestamp.strftime("%Y-%m-%d")
            
            # Format source coordinates
            def format_coords(point):
                if point:
                    lon, lat = point.coords[0], point.coords[1]
                    lon_str = f"{lon}E" if lon > 0 else f"{abs(lon)}W"
                    lat_str = f"{lat}N" if lat > 0 else f"{abs(lat)}S"
                    return f"{lat_str} {lon_str}"
                return "-"
            
            kupsat_coords = format_coords(source.coords_kupsat) if source else "-"
            valid_coords = format_coords(source.coords_valid) if source else "-"
            
            # Отметки теперь привязаны к TechAnalyze, а не к ObjItem
            marks_data = []
            
            processed.append({
                'id': objitem.id,
                'name': objitem.name or "-",
                'satellite': param.id_satellite.name if param and param.id_satellite else "-",
                'frequency': param.frequency if param else "-",
                'freq_range': param.freq_range if param else "-",
                'polarization': param.polarization.name if param and param.polarization else "-",
                'modulation': param.modulation.name if param and param.modulation else "-",
                'bod_velocity': param.bod_velocity if param else "-",
                'snr': param.snr if param else "-",
                'geo_coords': geo_coords,
                'geo_date': geo_date,
                'kupsat_coords': kupsat_coords,
                'valid_coords': valid_coords,
                'source_id': source.id if source else None,
                'lyngsat_id': objitem.lyngsat_source.id if objitem.lyngsat_source else None,
                'marks': marks_data,
            })
        
        return processed


class ActionsPageView(View):
    """View for displaying the actions page."""
    
    def get(self, request):
        if request.user.is_authenticated:
            return render(request, "mainapp/actions.html")
        else:
            return render(request, "mainapp/login_required.html")


def custom_logout(request):
    """Custom logout view."""
    logout(request)
    return redirect("mainapp:source_list")
