"""
Selectors for park-related data retrieval.
Following Django styleguide pattern for separating data access from business logic.
"""

from typing import Any

from django.contrib.gis.geos import Point
from django.contrib.gis.measure import Distance
from django.db.models import Avg, Count, Prefetch, Q, QuerySet

from apps.rides.models import Ride

from .models import Park, ParkArea, ParkReview


def park_list_with_stats(*, filters: dict[str, Any] | None = None) -> QuerySet[Park]:
    """
    Get parks optimized for list display with basic stats.

    Args:
        filters: Optional dictionary of filter parameters

    Returns:
        QuerySet of parks with optimized queries
    """
    queryset = (
        Park.objects.select_related("operator", "property_owner")
        .prefetch_related("location")
        .annotate(
            ride_count_calculated=Count("rides", distinct=True),
            coaster_count_calculated=Count(
                "rides",
                filter=Q(rides__category__in=["RC", "WC"]),
                distinct=True,
            ),
            average_rating_calculated=Avg("reviews__rating"),
        )
    )

    if filters:
        if "status" in filters:
            queryset = queryset.filter(status=filters["status"])
        if "operator" in filters:
            queryset = queryset.filter(operator=filters["operator"])
        if "country" in filters:
            queryset = queryset.filter(location__country=filters["country"])
        if "search" in filters:
            search_term = filters["search"]
            queryset = queryset.filter(Q(name__icontains=search_term) | Q(description__icontains=search_term))

    return queryset.order_by("name")


def park_detail_optimized(*, slug: str) -> Park:
    """
    Get a single park with all related data optimized for detail view.

    Args:
        slug: Park slug identifier

    Returns:
        Park instance with optimized prefetches

    Raises:
        Park.DoesNotExist: If park with slug doesn't exist
    """
    return (
        Park.objects.select_related("operator", "property_owner")
        .prefetch_related(
            "location",
            "areas",
            Prefetch(
                "rides",
                queryset=Ride.objects.select_related("manufacturer", "designer", "ride_model"),
            ),
            Prefetch(
                "reviews",
                queryset=ParkReview.objects.select_related("user").filter(is_published=True),
            ),
            "photos",
        )
        .get(slug=slug)
    )


def parks_near_location(*, point: Point, distance_km: float = 50, limit: int = 10) -> QuerySet[Park]:
    """
    Get parks near a specific geographic location.

    Args:
        point: Geographic point (longitude, latitude)
        distance_km: Maximum distance in kilometers
        limit: Maximum number of results

    Returns:
        QuerySet of nearby parks ordered by distance
    """
    return (
        Park.objects.filter(
            location__coordinates__distance_lte=(
                point,
                Distance(km=distance_km),
            )
        )
        .select_related("operator")
        .prefetch_related("location")
        .distance(point)
        .order_by("distance")[:limit]
    )


def park_statistics() -> dict[str, Any]:
    """
    Get overall park statistics for dashboard/analytics.

    Returns:
        Dictionary containing park statistics
    """
    total_parks = Park.objects.count()
    operating_parks = Park.objects.filter(status="OPERATING").count()
    total_rides = Ride.objects.count()
    total_coasters = Ride.objects.filter(category__in=["RC", "WC"]).count()

    return {
        "total_parks": total_parks,
        "operating_parks": operating_parks,
        "closed_parks": total_parks - operating_parks,
        "total_rides": total_rides,
        "total_coasters": total_coasters,
        "average_rides_per_park": (total_rides / total_parks if total_parks > 0 else 0),
    }


def parks_by_operator(*, operator_id: int) -> QuerySet[Park]:
    """
    Get all parks operated by a specific company.

    Args:
        operator_id: Company ID of the operator

    Returns:
        QuerySet of parks operated by the company
    """
    return (
        Park.objects.filter(operator_id=operator_id)
        .select_related("operator")
        .prefetch_related("location")
        .annotate(ride_count_calculated=Count("rides"))
        .order_by("name")
    )


def parks_with_recent_reviews(*, days: int = 30) -> QuerySet[Park]:
    """
    Get parks that have received reviews in the last N days.

    Args:
        days: Number of days to look back for reviews

    Returns:
        QuerySet of parks with recent reviews
    """
    from datetime import timedelta

    from django.utils import timezone

    cutoff_date = timezone.now() - timedelta(days=days)

    return (
        Park.objects.filter(reviews__created_at__gte=cutoff_date, reviews__is_published=True)
        .select_related("operator")
        .prefetch_related("location")
        .annotate(recent_review_count=Count("reviews", filter=Q(reviews__created_at__gte=cutoff_date)))
        .order_by("-recent_review_count")
        .distinct()
    )


def park_search_autocomplete(*, query: str, limit: int = 10) -> QuerySet[Park]:
    """
    Get parks matching a search query for autocomplete functionality.

    Args:
        query: Search string
        limit: Maximum number of results

    Returns:
        QuerySet of matching parks for autocomplete
    """
    return (
        Park.objects.filter(
            Q(name__icontains=query) | Q(location__city__icontains=query) | Q(location__region__icontains=query)
        )
        .select_related("operator")
        .prefetch_related("location")
        .order_by("name")[:limit]
    )


def park_areas_for_park(*, park_slug: str) -> QuerySet[ParkArea]:
    """
    Get all areas for a specific park.

    Args:
        park_slug: Slug of the park

    Returns:
        QuerySet of park areas with related data
    """
    return (
        ParkArea.objects.filter(park__slug=park_slug)
        .select_related("park")
        .prefetch_related("rides")
        .annotate(ride_count=Count("rides"))
        .order_by("name")
    )


def park_reviews_for_park(*, park_id: int, limit: int = 20) -> QuerySet[ParkReview]:
    """
    Get reviews for a specific park.

    Args:
        park_id: Park ID
        limit: Maximum number of reviews to return

    Returns:
        QuerySet of park reviews
    """
    return (
        ParkReview.objects.filter(park_id=park_id, is_published=True)
        .select_related("user", "park")
        .order_by("-created_at")[:limit]
    )
