# Standard library imports
from typing import Any, Dict

# Django imports
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpResponse, HttpResponseNotFound, JsonResponse
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.cache import cache_page
from django.views.decorators.http import require_GET
from django.views.generic import TemplateView

# Third-party imports
import requests

# Local imports
from mainapp.models import Satellite
from .models import Transponders
from .utils import get_band_names


class CesiumMapView(LoginRequiredMixin, TemplateView):
    """
    Представление для отображения 3D карты с использованием Cesium.

    Отображает спутники и их зоны покрытия на интерактивной 3D карте.
    """

    template_name = "mapsapp/map3d.html"

    def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
        context = super().get_context_data(**kwargs)
        # Оптимизированный запрос - загружаем только необходимые поля
        # Фильтруем спутники, у которых есть параметры с привязанными объектами
        context["sats"] = (
            Satellite.objects.filter(parameters__objitem__isnull=False)
            .distinct()
            .only("id", "name")
            .order_by("name")
        )
        return context


class GetFootprintsView(LoginRequiredMixin, View):
    """
    API для получения зон покрытия (footprints) спутника.

    Возвращает список названий зон покрытия для указанного спутника.
    """

    def get(self, request, sat_id):
        try:
            # Оптимизированный запрос - загружаем только поле name
            sat_name = Satellite.objects.only("name").get(id=sat_id).name
            footprint_names = get_band_names(sat_name)

            return JsonResponse(footprint_names, safe=False)
        except Satellite.DoesNotExist:
            return JsonResponse({"error": "Спутник не найден"}, status=404)
        except Exception as e:
            return JsonResponse({"error": str(e)}, status=500)


class TileProxyView(View):
    """
    Прокси для загрузки тайлов карты покрытия спутников.

    Кэширует тайлы на 7 дней для улучшения производительности.
    """

    # Константы
    TILE_BASE_URL = "https://static.satbeams.com/tiles"
    CACHE_DURATION = 60 * 60 * 24 * 7  # 7 дней
    REQUEST_TIMEOUT = 10  # секунд

    @method_decorator(require_GET)
    @method_decorator(cache_page(CACHE_DURATION))
    def dispatch(self, *args, **kwargs):
        return super().dispatch(*args, **kwargs)

    def get(self, request, footprint_name, z, x, y):
        # Валидация имени footprint
        if not footprint_name.replace("-", "").replace("_", "").isalnum():
            return HttpResponse("Invalid footprint name", status=400)

        url = f"{self.TILE_BASE_URL}/{footprint_name}/{z}/{x}/{y}.png"

        try:
            resp = requests.get(url, timeout=self.REQUEST_TIMEOUT)
            if resp.status_code == 200:
                response = HttpResponse(resp.content, content_type="image/png")
                response["Access-Control-Allow-Origin"] = "*"
                response["Cache-Control"] = f"public, max-age={self.CACHE_DURATION}"
                return response
            else:
                return HttpResponseNotFound("Tile not found")
        except requests.Timeout:
            return HttpResponse("Request timeout", status=504)
        except requests.RequestException as e:
            return HttpResponse(f"Proxy error: {e}", status=500)


class LeafletMapView(LoginRequiredMixin, TemplateView):
    """
    Представление для отображения 2D карты с использованием Leaflet.

    Отображает спутники и транспондеры на интерактивной 2D карте.
    """

    template_name = "mapsapp/map2d.html"

    def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
        context = super().get_context_data(**kwargs)
        # Оптимизированные запросы - загружаем только необходимые поля
        # Фильтруем спутники, у которых есть параметры с привязанными объектами
        context["sats"] = (
            Satellite.objects.filter(parameters__objitem__isnull=False)
            .distinct()
            .only("id", "name")
            .order_by("name")
        )

        context["trans"] = Transponders.objects.select_related(
            "sat_id", "polarization"
        ).only(
            "id",
            "name",
            "sat_id__name",
            "polarization__name",
            "downlink",
            "frequency_range",
            "zone_name",
        )
        return context


class GetTransponderOnSatIdView(LoginRequiredMixin, View):
    """
    API для получения транспондеров спутника.

    Возвращает список транспондеров для указанного спутника с оптимизированными запросами.
    """

    def get(self, request, sat_id):
        # Оптимизированный запрос с select_related и only
        trans = (
            Transponders.objects.filter(sat_id=sat_id)
            .select_related("polarization")
            .only(
                "name", "downlink", "frequency_range", "zone_name", "polarization__name"
            )
        )

        if not trans.exists():
            return JsonResponse({"error": "Объектов не найдено"}, status=404)

        # Используем list comprehension для лучшей производительности
        output = [
            {
                "name": tran.name,
                "frequency": tran.downlink,
                "frequency_range": tran.frequency_range,
                "zone_name": tran.zone_name,
                "polarization": tran.polarization.name if tran.polarization else "-",
            }
            for tran in trans
        ]

        return JsonResponse(output, safe=False)
