"""
Selectors for ride-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 .choices import RIDE_CATEGORIES
from .models import Ride, RideModel, RideReview


def ride_list_for_display(*, filters: dict[str, Any] | None = None) -> QuerySet[Ride]:
    """
    Get rides optimized for list display with related data.

    Args:
        filters: Optional dictionary of filter parameters

    Returns:
        QuerySet of rides with optimized queries
    """
    queryset = (
        Ride.objects.select_related(
            "park",
            "park__operator",
            "manufacturer",
            "designer",
            "ride_model",
            "park_area",
        )
        .prefetch_related("park__location")
        .annotate(average_rating_calculated=Avg("reviews__rating"))
    )

    if filters:
        if "status" in filters and filters["status"]:
            queryset = queryset.filter(status__in=filters["status"])
        if "category" in filters and filters["category"]:
            queryset = queryset.filter(category__in=filters["category"])
        if "manufacturer" in filters:
            queryset = queryset.filter(manufacturer=filters["manufacturer"])
        if "park" in filters:
            queryset = queryset.filter(park=filters["park"])
        if "search" in filters:
            search_term = filters["search"]
            queryset = queryset.filter(
                Q(name__icontains=search_term)
                | Q(description__icontains=search_term)
                | Q(park__name__icontains=search_term)
            )

    return queryset.order_by("park__name", "name")


def ride_detail_optimized(*, slug: str, park_slug: str) -> Ride:
    """
    Get a single ride with all related data optimized for detail view.

    Args:
        slug: Ride slug identifier
        park_slug: Park slug for the ride

    Returns:
        Ride instance with optimized prefetches

    Raises:
        Ride.DoesNotExist: If ride doesn't exist
    """
    return (
        Ride.objects.select_related(
            "park",
            "park__operator",
            "manufacturer",
            "designer",
            "ride_model",
            "park_area",
        )
        .prefetch_related(
            "park__location",
            Prefetch(
                "reviews",
                queryset=RideReview.objects.select_related("user").filter(is_published=True),
            ),
            "photos",
        )
        .get(slug=slug, park__slug=park_slug)
    )


def rides_by_category(*, category: str) -> QuerySet[Ride]:
    """
    Get all rides in a specific category.

    Args:
        category: Ride category code

    Returns:
        QuerySet of rides in the category
    """
    return (
        Ride.objects.filter(category=category)
        .select_related("park", "manufacturer", "designer")
        .prefetch_related("park__location")
        .annotate(average_rating_calculated=Avg("reviews__rating"))
        .order_by("park__name", "name")
    )


def rides_by_manufacturer(*, manufacturer_id: int) -> QuerySet[Ride]:
    """
    Get all rides manufactured by a specific company.

    Args:
        manufacturer_id: Company ID of the manufacturer

    Returns:
        QuerySet of rides by the manufacturer
    """
    return (
        Ride.objects.filter(manufacturer_id=manufacturer_id)
        .select_related("park", "manufacturer", "ride_model")
        .prefetch_related("park__location")
        .annotate(average_rating_calculated=Avg("reviews__rating"))
        .order_by("park__name", "name")
    )


def rides_by_designer(*, designer_id: int) -> QuerySet[Ride]:
    """
    Get all rides designed by a specific company.

    Args:
        designer_id: Company ID of the designer

    Returns:
        QuerySet of rides by the designer
    """
    return (
        Ride.objects.filter(designer_id=designer_id)
        .select_related("park", "designer", "ride_model")
        .prefetch_related("park__location")
        .annotate(average_rating_calculated=Avg("reviews__rating"))
        .order_by("park__name", "name")
    )


def rides_in_park(*, park_slug: str) -> QuerySet[Ride]:
    """
    Get all rides in a specific park.

    Args:
        park_slug: Slug of the park

    Returns:
        QuerySet of rides in the park
    """
    return (
        Ride.objects.filter(park__slug=park_slug)
        .select_related("manufacturer", "designer", "ride_model", "park_area")
        .prefetch_related()
        .annotate(average_rating_calculated=Avg("reviews__rating"))
        .order_by("park_area__name", "name")
    )


def rides_near_location(*, point: Point, distance_km: float = 50, limit: int = 10) -> QuerySet[Ride]:
    """
    Get rides 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 rides ordered by distance
    """
    return (
        Ride.objects.filter(
            park__location__coordinates__distance_lte=(
                point,
                Distance(km=distance_km),
            )
        )
        .select_related("park", "manufacturer")
        .prefetch_related("park__location")
        .distance(point)
        .order_by("distance")[:limit]
    )


def ride_models_with_installations() -> QuerySet[RideModel]:
    """
    Get ride models that have installations with counts.

    Returns:
        QuerySet of ride models with installation counts
    """
    return (
        RideModel.objects.annotate(installation_count=Count("rides"))
        .filter(installation_count__gt=0)
        .select_related("manufacturer")
        .order_by("-installation_count", "name")
    )


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

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

    Returns:
        QuerySet of matching rides for autocomplete
    """
    return (
        Ride.objects.filter(
            Q(name__icontains=query) | Q(park__name__icontains=query) | Q(manufacturer__name__icontains=query)
        )
        .select_related("park", "manufacturer")
        .prefetch_related("park__location")
        .order_by("park__name", "name")[:limit]
    )


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

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

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

    from django.utils import timezone

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

    return (
        Ride.objects.filter(reviews__created_at__gte=cutoff_date, reviews__is_published=True)
        .select_related("park", "manufacturer")
        .prefetch_related("park__location")
        .annotate(recent_review_count=Count("reviews", filter=Q(reviews__created_at__gte=cutoff_date)))
        .order_by("-recent_review_count")
        .distinct()
    )


def ride_statistics_by_category() -> dict[str, Any]:
    """
    Get ride statistics grouped by category.

    Returns:
        Dictionary containing ride statistics by category
    """

    stats = {}
    for category in RIDE_CATEGORIES:
        if category.value:  # Skip empty choice
            count = Ride.objects.filter(category=category.value).count()
            stats[category.value] = {"name": category.label, "count": count}

    return stats


def rides_by_opening_year(*, year: int) -> QuerySet[Ride]:
    """
    Get rides that opened in a specific year.

    Args:
        year: The opening year

    Returns:
        QuerySet of rides that opened in the specified year
    """
    return (
        Ride.objects.filter(opening_date__year=year)
        .select_related("park", "manufacturer")
        .prefetch_related("park__location")
        .order_by("opening_date", "park__name", "name")
    )
