"""
Map related views for displaying objects on maps.
"""

from collections import defaultdict

from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import JsonResponse
from django.shortcuts import redirect, render
from django.utils.decorators import method_decorator
from django.views import View

# from ..clusters import get_clusters
from ..mixins import RoleRequiredMixin
from ..models import ObjItem


@method_decorator(staff_member_required, name="dispatch")
class ShowMapView(RoleRequiredMixin, View):
    """View for displaying objects on map (admin interface)."""

    required_roles = ["admin", "moderator"]

    def get(self, request):
        ids = request.GET.get("ids", "")
        points = []
        if ids:
            id_list = [int(x) for x in ids.split(",") if x.isdigit()]
            locations = ObjItem.objects.filter(id__in=id_list).select_related(
                "parameter_obj",
                "parameter_obj__id_satellite",
                "parameter_obj__polarization",
                "parameter_obj__modulation",
                "parameter_obj__standard",
                "geo_obj",
            )
            for obj in locations:
                if (
                    not hasattr(obj, "geo_obj")
                    or not obj.geo_obj
                    or not obj.geo_obj.coords
                ):
                    continue
                param = getattr(obj, "parameter_obj", None)
                if not param:
                    continue
                points.append(
                    {
                        "name": f"{obj.name}",
                        "freq": f"{param.frequency} [{param.freq_range}] МГц",
                        "point": (obj.geo_obj.coords.x, obj.geo_obj.coords.y),
                    }
                )
        else:
            return redirect("admin")

        grouped = defaultdict(list)
        for p in points:
            grouped[p["name"]].append({"point": p["point"], "frequency": p["freq"]})

        groups = [
            {"name": name, "points": coords_list}
            for name, coords_list in grouped.items()
        ]

        context = {
            "groups": groups,
        }
        return render(request, "admin/map_custom.html", context)


class ShowSelectedObjectsMapView(LoginRequiredMixin, View):
    """View for displaying selected objects on map."""

    def get(self, request):
        ids = request.GET.get("ids", "")
        points = []
        if ids:
            id_list = [int(x) for x in ids.split(",") if x.isdigit()]
            locations = ObjItem.objects.filter(id__in=id_list).select_related(
                "parameter_obj",
                "parameter_obj__id_satellite",
                "parameter_obj__polarization",
                "parameter_obj__modulation",
                "parameter_obj__standard",
                "geo_obj",
            )
            for obj in locations:
                if (
                    not hasattr(obj, "geo_obj")
                    or not obj.geo_obj
                    or not obj.geo_obj.coords
                ):
                    continue
                param = getattr(obj, "parameter_obj", None)
                if not param:
                    continue
                points.append(
                    {
                        "name": f"{obj.name}",
                        "freq": f"{param.frequency} [{param.freq_range}] МГц",
                        "point": (obj.geo_obj.coords.x, obj.geo_obj.coords.y),
                    }
                )
        else:
            return redirect("mainapp:objitem_list")

        # Group points by object name
        grouped = defaultdict(list)
        for p in points:
            grouped[p["name"]].append({"point": p["point"], "frequency": p["freq"]})

        groups = [
            {"name": name, "points": coords_list}
            for name, coords_list in grouped.items()
        ]

        context = {
            "groups": groups,
        }
        return render(request, "mainapp/objitem_map.html", context)


class ShowSourcesMapView(LoginRequiredMixin, View):
    """View for displaying selected sources on map."""

    def get(self, request):
        import json
        from ..models import Source

        ids = request.GET.get("ids", "")
        groups = []

        if ids:
            id_list = [int(x) for x in ids.split(",") if x.isdigit()]
            sources = Source.objects.filter(id__in=id_list)

            # Define coordinate types with their labels and colors
            coord_types = [
                ("coords_average", "Усредненные координаты", "blue"),
                ("coords_kupsat", "Координаты Кубсата", "orange"),
                ("coords_valid", "Координаты оперативников", "green"),
                ("coords_reference", "Координаты справочные", "violet"),
            ]

            # Group points by coordinate type
            for coord_field, label, color in coord_types:
                points = []
                for source in sources:
                    coords = getattr(source, coord_field)
                    if coords:
                        # coords is a Point object with x (longitude) and y (latitude)
                        points.append(
                            {
                                "point": (coords.x, coords.y),  # (lon, lat)
                                "source_id": f"Объект #{source.id}",
                            }
                        )

                if points:
                    groups.append(
                        {
                            "name": label,
                            "points": points,
                            "color": color,
                        }
                    )
        else:
            return redirect("mainapp:source_list")

        # Get polygon filter from URL if present
        polygon_coords_str = request.GET.get("polygon", "").strip()
        polygon_coords = None
        if polygon_coords_str:
            try:
                polygon_coords = json.loads(polygon_coords_str)
            except (json.JSONDecodeError, ValueError, TypeError):
                polygon_coords = None

        context = {
            "groups": groups,
            "polygon_coords": json.dumps(polygon_coords) if polygon_coords else None,
        }
        return render(request, "mainapp/source_map.html", context)


class ShowSourceWithPointsMapView(LoginRequiredMixin, View):
    """View for displaying a single source with all its related ObjItem points."""

    def get(self, request, source_id):
        from ..models import Source

        try:
            source = Source.objects.prefetch_related(
                "source_objitems",
                "source_objitems__parameter_obj",
                "source_objitems__geo_obj",
            ).get(id=source_id)
        except Source.DoesNotExist:
            return redirect("mainapp:source_list")

        groups = []

        # Цвета для разных типов координат источника
        source_coord_types = [
            ("coords_average", "Усредненные координаты", "blue"),
            ("coords_kupsat", "Координаты Кубсата", "orange"),
            ("coords_valid", "Координаты оперативников", "green"),
            ("coords_reference", "Координаты справочные", "violet"),
        ]

        # Добавляем координаты источника
        for coord_field, label, color in source_coord_types:
            coords = getattr(source, coord_field)
            if coords:
                groups.append(
                    {
                        "name": label,
                        "points": [
                            {
                                "point": (coords.x, coords.y),
                                "source_id": f"Источник #{source.id}",
                            }
                        ],
                        "color": color,
                    }
                )

        # Добавляем все точки ГЛ одной группой
        gl_points = source.source_objitems.select_related(
            "parameter_obj", "geo_obj"
        ).all()

        # Собираем все точки ГЛ в одну группу с сортировкой по времени
        all_gl_points = []
        for obj in gl_points:
            if (
                not hasattr(obj, "geo_obj")
                or not obj.geo_obj
                or not obj.geo_obj.coords
            ):
                continue
            param = getattr(obj, "parameter_obj", None)
            if not param:
                continue

            all_gl_points.append(
                {
                    "point": (obj.geo_obj.coords.x, obj.geo_obj.coords.y),
                    "name": obj.name,
                    "frequency": f"{param.frequency} [{param.freq_range}] МГц",
                    "timestamp": obj.geo_obj.timestamp.isoformat() if obj.geo_obj.timestamp else None,
                }
            )

        # Сортируем точки по времени (от старой к новой)
        all_gl_points.sort(key=lambda x: x["timestamp"] if x["timestamp"] else "")

        # Добавляем все точки ГЛ одним цветом (красный)
        if all_gl_points:
            groups.append(
                {"name": "Точки ГЛ", "points": all_gl_points, "color": "red"}
            )

        context = {
            "groups": groups,
            "source_id": source_id,
        }
        return render(request, "mainapp/source_with_points_map.html", context)


class ShowSourceAveragingStepsMapView(LoginRequiredMixin, View):
    """View for displaying source averaging steps visualization."""

    def get(self, request, source_id):
        from ..models import Source
        from ..utils import calculate_mean_coords, RANGE_DISTANCE

        try:
            source = Source.objects.prefetch_related(
                "source_objitems",
                "source_objitems__parameter_obj",
                "source_objitems__geo_obj",
            ).get(id=source_id)
        except Source.DoesNotExist:
            return redirect("mainapp:source_list")

        # Получаем все ObjItem, отсортированные по ID (порядок добавления)
        objitems = source.source_objitems.select_related(
            "parameter_obj", "geo_obj"
        ).order_by("id")

        # Собираем координаты всех точек
        original_points = []
        for obj in objitems:
            if (
                not hasattr(obj, "geo_obj")
                or not obj.geo_obj
                or not obj.geo_obj.coords
            ):
                continue
            param = getattr(obj, "parameter_obj", None)
            if not param:
                continue

            original_points.append(
                {
                    "point": (obj.geo_obj.coords.x, obj.geo_obj.coords.y),
                    "name": obj.name,
                    "frequency": f"{param.frequency} [{param.freq_range}] МГц",
                    "objitem_id": obj.id,
                }
            )

        # Воспроизводим алгоритм усреднения
        averaging_steps = []
        
        if original_points:
            # Первая точка становится начальным средним
            current_avg = original_points[0]["point"]
            
            # Обрабатываем остальные точки
            for i, point_data in enumerate(original_points[1:], start=1):
                current_coord = point_data["point"]
                
                # Вычисляем новое среднее и расстояние
                new_avg, distance = calculate_mean_coords(current_avg, current_coord)
                
                # Сохраняем шаг усреднения
                averaging_steps.append(
                    {
                        "point": new_avg,
                        "step": i,
                        "distance": round(distance, 2),
                        "within_range": distance <= RANGE_DISTANCE,
                    }
                )
                
                # Обновляем текущее среднее
                current_avg = new_avg

        # Формируем группы для отображения на карте
        groups = []

        # 1. Исходные точки ObjItem (красный)
        if original_points:
            groups.append(
                {
                    "name": "Исходные точки ГЛ",
                    "points": original_points,
                    "color": "red",
                }
            )

        # 2. Промежуточные точки усреднения (оранжевый)
        if averaging_steps:
            intermediate_points = [
                {
                    "point": step["point"],
                    "step": f"Шаг {step['step']}",
                    "distance": f"{step['distance']} км",
                }
                for step in averaging_steps[:-1]  # Все кроме последней
            ]
            if intermediate_points:
                groups.append(
                    {
                        "name": "Промежуточные шаги усреднения",
                        "points": intermediate_points,
                        "color": "orange",
                    }
                )

        # 3. Финальная усредненная точка (синий)
        if averaging_steps:
            final_step = averaging_steps[-1]
            groups.append(
                {
                    "name": "Финальная усредненная координата",
                    "points": [
                        {
                            "point": final_step["point"],
                            "step": f"Шаг {final_step['step']} (финальный)",
                            "distance": f"{final_step['distance']} км",
                        }
                    ],
                    "color": "blue",
                }
            )

        # 4. Координаты источника для сравнения (если есть)
        source_coord_types = [
            ("coords_average", "Сохраненные усредненные координаты", "green"),
            ("coords_kupsat", "Координаты Кубсата", "purple"),
            ("coords_valid", "Координаты оперативников", "cyan"),
            ("coords_reference", "Координаты справочные", "violet"),
        ]

        for coord_field, label, color in source_coord_types:
            coords = getattr(source, coord_field)
            if coords:
                groups.append(
                    {
                        "name": label,
                        "points": [
                            {
                                "point": (coords.x, coords.y),
                                "source_id": f"Источник #{source.id}",
                            }
                        ],
                        "color": color,
                    }
                )

        context = {
            "groups": groups,
            "source_id": source_id,
            "total_points": len(original_points),
            "total_steps": len(averaging_steps),
        }
        return render(request, "mainapp/source_averaging_map.html", context)


class MultiSourcesPlaybackMapView(LoginRequiredMixin, View):
    """View for displaying animated playback of multiple sources on map."""

    def get(self, request):
        from ..models import Source

        ids = request.GET.get("ids", "")
        
        if not ids:
            return redirect("mainapp:source_list")
        
        id_list = [int(x) for x in ids.split(",") if x.isdigit()]
        if not id_list:
            return redirect("mainapp:source_list")

        context = {
            "source_ids": ",".join(str(x) for x in id_list),
            "source_count": len(id_list),
        }
        return render(request, "mainapp/multi_sources_playback_map.html", context)


# class ClusterTestView(LoginRequiredMixin, View):
#     """Test view for clustering functionality."""

#     def get(self, request):
#         objs = ObjItem.objects.filter(
#             name__icontains="! Astra 4A 12654,040 [1,962] МГц H"
#         )
#         coords = []
#         for obj in objs:
#             if hasattr(obj, "geo_obj") and obj.geo_obj and obj.geo_obj.coords:
#                 coords.append(
#                     (obj.geo_obj.coords.coords[1], obj.geo_obj.coords.coords[0])
#                 )
#         get_clusters(coords)

#         return JsonResponse({"success": "ок"})
