"""
Park Filter Service

Provides filtering functionality, aggregations, and caching for park filters.
This service handles complex filter logic and provides useful filter statistics.
"""

from typing import Any

from django.conf import settings
from django.core.cache import cache
from django.db.models import Count, Q, QuerySet

from ..models import Company, Park
from ..querysets import get_base_park_queryset


class ParkFilterService:
    """
    Service class for handling park filtering operations, aggregations,
    and providing filter suggestions based on available data.
    """

    CACHE_TIMEOUT = getattr(settings, "PARK_FILTER_CACHE_TIMEOUT", 300)  # 5 minutes

    def __init__(self):
        self.cache_prefix = "park_filter"

    def get_filter_counts(self, base_queryset: QuerySet | None = None) -> dict[str, Any]:
        """
        Get counts for various filter options to show users what's available.

        Args:
            base_queryset: Optional base queryset to use for calculations

        Returns:
            Dictionary containing counts for different filter categories
        """
        cache_key = f"{self.cache_prefix}:filter_counts"
        cached_result = cache.get(cache_key)

        if cached_result is not None:
            return cached_result

        if base_queryset is None:
            base_queryset = get_base_park_queryset()

        # Calculate filter counts
        filter_counts = {
            "total_parks": base_queryset.count(),
            "operating_parks": base_queryset.filter(status="OPERATING").count(),
            "parks_with_coasters": base_queryset.filter(coaster_count__gt=0).count(),
            "big_parks": base_queryset.filter(ride_count__gte=10).count(),
            "highly_rated": base_queryset.filter(average_rating__gte=4.0).count(),
            "park_types": self._get_park_type_counts(base_queryset),
            "top_operators": self._get_top_operators(base_queryset),
            "countries": self._get_country_counts(base_queryset),
        }

        # Cache the result
        cache.set(cache_key, filter_counts, self.CACHE_TIMEOUT)
        return filter_counts

    def _get_park_type_counts(self, queryset: QuerySet) -> dict[str, int]:
        """Get counts for different park types based on operator names."""
        return {
            "disney": queryset.filter(operator__name__icontains="Disney").count(),
            "universal": queryset.filter(operator__name__icontains="Universal").count(),
            "six_flags": queryset.filter(operator__name__icontains="Six Flags").count(),
            "cedar_fair": queryset.filter(
                Q(operator__name__icontains="Cedar Fair")
                | Q(operator__name__icontains="Cedar Point")
                | Q(operator__name__icontains="Kings Island")
            ).count(),
        }

    def _get_top_operators(self, queryset: QuerySet, limit: int = 10) -> list[dict[str, Any]]:
        """Get the top operators by number of parks."""
        return list(
            queryset.values("operator__name", "operator__id")
            .annotate(park_count=Count("id"))
            .filter(park_count__gt=0)
            .order_by("-park_count")[:limit]
        )

    def _get_country_counts(self, queryset: QuerySet, limit: int = 10) -> list[dict[str, Any]]:
        """Get countries with the most parks."""
        return list(
            queryset.filter(location__country__isnull=False)
            .values("location__country")
            .annotate(park_count=Count("id"))
            .filter(park_count__gt=0)
            .order_by("-park_count")[:limit]
        )

    def get_filter_suggestions(self, query: str) -> dict[str, list[str]]:
        """
        Get filter suggestions based on a search query.

        Args:
            query: Search query string

        Returns:
            Dictionary with suggestion categories
        """
        cache_key = f"{self.cache_prefix}:suggestions:{query.lower()}"
        cached_result = cache.get(cache_key)

        if cached_result is not None:
            return cached_result

        suggestions = {
            "parks": [],
            "operators": [],
            "locations": [],
        }

        if len(query) >= 2:  # Only search for queries of 2+ characters
            # Park name suggestions
            park_names = Park.objects.filter(name__icontains=query).values_list("name", flat=True)[:5]
            suggestions["parks"] = list(park_names)

            # Operator suggestions
            operator_names = Company.objects.filter(roles__contains=["OPERATOR"], name__icontains=query).values_list(
                "name", flat=True
            )[:5]
            suggestions["operators"] = list(operator_names)

            # Location suggestions (cities and countries)
            locations = Park.objects.filter(
                Q(location__city__icontains=query) | Q(location__country__icontains=query)
            ).values_list("location__city", "location__country")[:5]

            location_suggestions = []
            for city, country in locations:
                if city and city.lower().startswith(query.lower()):
                    location_suggestions.append(city)
                elif country and country.lower().startswith(query.lower()):
                    location_suggestions.append(country)

            suggestions["locations"] = list(set(location_suggestions))[:5]

        # Cache suggestions for a shorter time
        cache.set(cache_key, suggestions, 60)  # 1 minute cache
        return suggestions

    def get_popular_filters(self) -> dict[str, Any]:
        """
        Get commonly used filter combinations and popular filter values.

        Returns:
            Dictionary containing popular filter configurations
        """
        cache_key = f"{self.cache_prefix}:popular_filters"
        cached_result = cache.get(cache_key)

        if cached_result is not None:
            return cached_result

        base_qs = get_base_park_queryset()

        popular_filters = {
            "quick_filters": [
                {
                    "label": "Disney Parks",
                    "filters": {"park_type": "disney"},
                    "count": base_qs.filter(operator__name__icontains="Disney").count(),
                },
                {
                    "label": "Parks with Coasters",
                    "filters": {"has_coasters": True},
                    "count": base_qs.filter(coaster_count__gt=0).count(),
                },
                {
                    "label": "Highly Rated",
                    "filters": {"min_rating": "4"},
                    "count": base_qs.filter(average_rating__gte=4.0).count(),
                },
                {
                    "label": "Major Parks",
                    "filters": {"big_parks_only": True},
                    "count": base_qs.filter(ride_count__gte=10).count(),
                },
            ],
            "recommended_sorts": [
                {"value": "-average_rating", "label": "Highest Rated"},
                {"value": "-coaster_count", "label": "Most Coasters"},
                {"value": "name", "label": "A-Z"},
            ],
        }

        # Cache for longer since these don't change often
        cache.set(cache_key, popular_filters, self.CACHE_TIMEOUT * 2)
        return popular_filters

    def clear_filter_cache(self) -> None:
        """Clear all cached filter data."""
        # Simple cache clearing - delete known keys
        cache_keys = [
            f"{self.cache_prefix}:filter_counts",
            f"{self.cache_prefix}:popular_filters",
        ]
        for key in cache_keys:
            cache.delete(key)

    def get_filtered_queryset(self, filters: dict[str, Any]) -> QuerySet:  # noqa: C901
        """
        Apply filters to get a filtered queryset with optimizations.

        Args:
            filters: Dictionary of filter parameters

        Returns:
            Filtered and optimized QuerySet
        """
        queryset = (
            get_base_park_queryset()
            .select_related("operator", "property_owner", "location")
            .prefetch_related("photos", "rides__manufacturer")
        )

        # Apply status filter
        if filters.get("status"):
            queryset = queryset.filter(status=filters["status"])

        # Apply park type filter
        if filters.get("park_type"):
            queryset = self._apply_park_type_filter(queryset, filters["park_type"])

        # Apply coaster filter
        if filters.get("has_coasters"):
            queryset = queryset.filter(coaster_count__gt=0)

        # Apply rating filter
        if filters.get("min_rating"):
            try:
                min_rating = float(filters["min_rating"])
                queryset = queryset.filter(average_rating__gte=min_rating)
            except (ValueError, TypeError):
                pass

        # Apply big parks filter
        if filters.get("big_parks_only"):
            queryset = queryset.filter(ride_count__gte=10)

        # Apply search
        if filters.get("search"):
            search_query = filters["search"]
            queryset = queryset.filter(
                Q(name__icontains=search_query)
                | Q(description__icontains=search_query)
                | Q(location__city__icontains=search_query)
                | Q(location__country__icontains=search_query)
            )

        # Apply location filters
        if filters.get("country_filter"):
            queryset = queryset.filter(location__country__icontains=filters["country_filter"])

        if filters.get("state_filter"):
            queryset = queryset.filter(location__state__icontains=filters["state_filter"])

        # Apply ordering
        if filters.get("ordering"):
            queryset = queryset.order_by(filters["ordering"])

        return queryset.distinct()

    def _apply_park_type_filter(self, queryset: QuerySet, park_type: str) -> QuerySet:
        """Apply park type filter logic."""
        type_filters = {
            "disney": Q(operator__name__icontains="Disney"),
            "universal": Q(operator__name__icontains="Universal"),
            "six_flags": Q(operator__name__icontains="Six Flags"),
            "cedar_fair": (
                Q(operator__name__icontains="Cedar Fair")
                | Q(operator__name__icontains="Cedar Point")
                | Q(operator__name__icontains="Kings Island")
                | Q(operator__name__icontains="Canada's Wonderland")
            ),
            "independent": ~(
                Q(operator__name__icontains="Disney")
                | Q(operator__name__icontains="Universal")
                | Q(operator__name__icontains="Six Flags")
                | Q(operator__name__icontains="Cedar Fair")
                | Q(operator__name__icontains="Cedar Point")
            ),
        }

        if park_type in type_filters:
            return queryset.filter(type_filters[park_type])

        return queryset
