"""
HTML views for the unified map service.
Provides web interfaces for map functionality with HTMX integration.
"""

import contextlib
import json
from typing import Any

from django.core.paginator import Paginator
from django.http import HttpRequest, HttpResponse, JsonResponse
from django.shortcuts import render
from django.views.generic import TemplateView, View

from ..services.data_structures import GeoBounds, LocationType, MapFilters
from ..services.map_service import unified_map_service


class MapViewMixin:
    """Mixin providing common functionality for map views."""

    def get_map_context(self, request: HttpRequest) -> dict[str, Any]:
        """Get common context data for map views."""
        return {
            "map_api_urls": {
                "locations": "/api/map/locations/",
                "search": "/api/map/search/",
                "bounds": "/api/map/bounds/",
                "location_detail": "/api/map/locations/",
            },
            "location_types": [lt.value for lt in LocationType],
            "default_zoom": 10,
            "enable_clustering": True,
            "enable_search": True,
        }

    def parse_location_types(self, request: HttpRequest) -> set[LocationType] | None:
        """Parse location types from request parameters."""
        types_param = request.GET.get("types")
        if types_param:
            try:
                return {
                    LocationType(t.strip())
                    for t in types_param.split(",")
                    if t.strip() in [lt.value for lt in LocationType]
                }
            except ValueError:
                return None
        return None


class UniversalMapView(MapViewMixin, TemplateView):
    """
    Main universal map view showing all location types.

    URL: /maps/
    """

    template_name = "maps/universal_map.html"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context.update(self.get_map_context(self.request))

        # Additional context for universal map
        context.update(
            {
                "page_title": "Interactive Map - All Locations",
                "map_type": "universal",
                "show_all_types": True,
                "initial_location_types": [lt.value for lt in LocationType],
                "filters_enabled": True,
            }
        )

        # Handle initial bounds from query parameters
        if all(param in self.request.GET for param in ["north", "south", "east", "west"]):
            with contextlib.suppress(ValueError, TypeError):
                context["initial_bounds"] = {
                    "north": float(self.request.GET["north"]),
                    "south": float(self.request.GET["south"]),
                    "east": float(self.request.GET["east"]),
                    "west": float(self.request.GET["west"]),
                }

        return context


class ParkMapView(MapViewMixin, TemplateView):
    """
    Map view focused specifically on parks.

    URL: /maps/parks/
    """

    template_name = "maps/park_map.html"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context.update(self.get_map_context(self.request))

        # Park-specific context
        context.update(
            {
                "page_title": "Theme Parks Map",
                "map_type": "parks",
                "show_all_types": False,
                "initial_location_types": [LocationType.PARK.value],
                "filters_enabled": True,
                "park_specific_filters": True,
            }
        )

        return context


class NearbyLocationsView(MapViewMixin, TemplateView):
    """
    View for showing locations near a specific point.

    URL: /maps/nearby/
    """

    template_name = "maps/nearby_locations.html"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context.update(self.get_map_context(self.request))

        # Parse coordinates from query parameters
        lat = self.request.GET.get("lat")
        lng = self.request.GET.get("lng")
        radius = self.request.GET.get("radius", "50")  # Default 50km radius

        if lat and lng:
            try:
                center_lat = float(lat)
                center_lng = float(lng)
                # Clamp between 1-200km
                search_radius = min(200, max(1, float(radius)))

                context.update(
                    {
                        "page_title": f"Locations Near {center_lat:.4f}, {
                            center_lng:.4f}",
                        "map_type": "nearby",
                        "center_coordinates": {
                            "lat": center_lat,
                            "lng": center_lng,
                        },
                        "search_radius": search_radius,
                        "show_radius_circle": True,
                    }
                )
            except (ValueError, TypeError):
                context["error"] = "Invalid coordinates provided"
        else:
            context.update(
                {
                    "page_title": "Nearby Locations",
                    "map_type": "nearby",
                    "prompt_for_location": True,
                }
            )

        return context


class LocationFilterView(MapViewMixin, View):
    """
    HTMX endpoint for updating map when filters change.

    URL: /maps/htmx/filter/
    """

    def get(self, request: HttpRequest) -> HttpResponse:
        """Return filtered location data for HTMX updates."""
        try:
            # Parse filter parameters
            location_types = self.parse_location_types(request)
            search_query = request.GET.get("q", "").strip()
            country = request.GET.get("country", "").strip()
            state = request.GET.get("state", "").strip()

            # Create filters
            filters = None
            if any([location_types, search_query, country, state]):
                filters = MapFilters(
                    location_types=location_types,
                    search_query=search_query or None,
                    country=country or None,
                    state=state or None,
                    has_coordinates=True,
                )

            # Get filtered locations
            map_response = unified_map_service.get_map_data(
                filters=filters,
                zoom_level=int(request.GET.get("zoom", "10")),
                cluster=request.GET.get("cluster", "true").lower() == "true",
            )

            # Return JSON response for HTMX
            return JsonResponse(
                {
                    "status": "success",
                    "data": map_response.to_dict(),
                    "filters_applied": map_response.filters_applied,
                }
            )

        except Exception as e:
            return JsonResponse({"status": "error", "message": str(e)}, status=400)


class LocationSearchView(MapViewMixin, View):
    """
    HTMX endpoint for real-time location search.

    URL: /maps/htmx/search/
    """

    def get(self, request: HttpRequest) -> HttpResponse:
        """Return search results for HTMX updates."""
        query = request.GET.get("q", "").strip()

        if not query or len(query) < 3:
            return render(
                request,
                "maps/partials/search_results.html",
                {
                    "results": [],
                    "query": query,
                    "message": "Enter at least 3 characters to search",
                },
            )

        try:
            # Parse optional location types
            location_types = self.parse_location_types(request)
            limit = min(20, max(5, int(request.GET.get("limit", "10"))))

            # Perform search
            results = unified_map_service.search_locations(query=query, location_types=location_types, limit=limit)

            return render(
                request,
                "maps/partials/search_results.html",
                {"results": results, "query": query, "count": len(results)},
            )

        except Exception as e:
            return render(
                request,
                "maps/partials/search_results.html",
                {"results": [], "query": query, "error": str(e)},
            )


class MapBoundsUpdateView(MapViewMixin, View):
    """
    HTMX endpoint for updating locations when map bounds change.

    URL: /maps/htmx/bounds/
    """

    def post(self, request: HttpRequest) -> HttpResponse:
        """Update map data when bounds change."""
        try:
            data = json.loads(request.body)

            # Parse bounds
            bounds = GeoBounds(
                north=float(data["north"]),
                south=float(data["south"]),
                east=float(data["east"]),
                west=float(data["west"]),
            )

            # Parse additional parameters
            zoom_level = int(data.get("zoom", 10))
            location_types = None
            if "types" in data:
                location_types = {LocationType(t) for t in data["types"] if t in [lt.value for lt in LocationType]}

            # Location types are used directly in the service call

            # Get updated map data
            map_response = unified_map_service.get_locations_by_bounds(
                north=bounds.north,
                south=bounds.south,
                east=bounds.east,
                west=bounds.west,
                location_types=location_types,
                zoom_level=zoom_level,
            )

            return JsonResponse({"status": "success", "data": map_response.to_dict()})

        except (json.JSONDecodeError, ValueError, KeyError) as e:
            return JsonResponse(
                {
                    "status": "error",
                    "message": f"Invalid request data: {str(e)}",
                },
                status=400,
            )
        except Exception as e:
            return JsonResponse({"status": "error", "message": str(e)}, status=500)


class LocationDetailModalView(MapViewMixin, View):
    """
    HTMX endpoint for showing location details in modal.

    URL: /maps/htmx/location/<type>/<id>/
    """

    def get(self, request: HttpRequest, location_type: str, location_id: int) -> HttpResponse:
        """Return location detail modal content."""
        try:
            # Validate location type
            if location_type not in [lt.value for lt in LocationType]:
                return render(
                    request,
                    "maps/partials/location_modal.html",
                    {"error": f"Invalid location type: {location_type}"},
                )

            # Get location details
            location = unified_map_service.get_location_details(location_type, location_id)

            if not location:
                return render(
                    request,
                    "maps/partials/location_modal.html",
                    {"error": "Location not found"},
                )

            return render(
                request,
                "maps/partials/location_modal.html",
                {"location": location, "location_type": location_type},
            )

        except Exception as e:
            return render(request, "maps/partials/location_modal.html", {"error": str(e)})


class LocationListView(MapViewMixin, TemplateView):
    """
    View for listing locations with pagination (non-map view).

    URL: /maps/list/
    """

    template_name = "maps/location_list.html"
    paginate_by = 20

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)

        # Parse filters
        location_types = self.parse_location_types(self.request)
        search_query = self.request.GET.get("q", "").strip()
        country = self.request.GET.get("country", "").strip()
        state = self.request.GET.get("state", "").strip()

        # Create filters
        filters = None
        if any([location_types, search_query, country, state]):
            filters = MapFilters(
                location_types=location_types,
                search_query=search_query or None,
                country=country or None,
                state=state or None,
                has_coordinates=True,
            )

        # Get locations without clustering
        map_response = unified_map_service.get_map_data(filters=filters, cluster=False, use_cache=True)

        # Paginate results
        paginator = Paginator(map_response.locations, self.paginate_by)
        page_number = self.request.GET.get("page")
        page_obj = paginator.get_page(page_number)

        context.update(
            {
                "page_title": "All Locations",
                "locations": page_obj,
                "total_count": map_response.total_count,
                "applied_filters": filters,
                "location_types": [lt.value for lt in LocationType],
                "current_filters": {
                    "types": self.request.GET.getlist("types"),
                    "q": search_query,
                    "country": country,
                    "state": state,
                },
            }
        )

        return context
