"""
Custom managers and QuerySets for optimized database patterns.
Following Django styleguide best practices for database access.
"""

from datetime import timedelta

from django.contrib.gis.geos import Point
from django.contrib.gis.measure import Distance
from django.db import models
from django.db.models import Avg, Count, Max, Q
from django.utils import timezone


class BaseQuerySet(models.QuerySet):
    """Base QuerySet with common optimizations and patterns."""

    def active(self):
        """Filter for active/enabled records."""
        if hasattr(self.model, "is_active"):
            return self.filter(is_active=True)
        return self

    def published(self):
        """Filter for published records."""
        if hasattr(self.model, "is_published"):
            return self.filter(is_published=True)
        return self

    def recent(self, *, days: int = 30):
        """Filter for recently created records."""
        cutoff_date = timezone.now() - timedelta(days=days)
        return self.filter(created_at__gte=cutoff_date)

    def search(self, *, query: str, fields: list[str] | None = None):
        """
        Full-text search across specified fields.

        Args:
            query: Search query string
            fields: List of field names to search (defaults to name, description)
        """
        if not query:
            return self

        if fields is None:
            fields = ["name", "description"] if hasattr(self.model, "name") else []

        q_objects = Q()
        for field in fields:
            if hasattr(self.model, field):
                q_objects |= Q(**{f"{field}__icontains": query})

        return self.filter(q_objects) if q_objects else self

    def with_stats(self):
        """Add basic statistics annotations."""
        return self

    def optimized_for_list(self):
        """Optimize queryset for list display."""
        return self.select_related().prefetch_related()

    def optimized_for_detail(self):
        """Optimize queryset for detail display."""
        return self.select_related().prefetch_related()


class BaseManager(models.Manager):
    """Base manager with common patterns."""

    def get_queryset(self):
        return BaseQuerySet(self.model, using=self._db)

    def active(self):
        return self.get_queryset().active()

    def published(self):
        return self.get_queryset().published()

    def recent(self, *, days: int = 30):
        return self.get_queryset().recent(days=days)

    def search(self, *, query: str, fields: list[str] | None = None):
        return self.get_queryset().search(query=query, fields=fields)


class LocationQuerySet(BaseQuerySet):
    """QuerySet for location-based models with geographic functionality."""

    def near_point(self, *, point: Point, distance_km: float = 50):
        """Filter locations near a geographic point."""
        if hasattr(self.model, "point"):
            return (
                self.filter(point__distance_lte=(point, Distance(km=distance_km))).distance(point).order_by("distance")
            )
        return self

    def within_bounds(self, *, north: float, south: float, east: float, west: float):
        """Filter locations within geographic bounds."""
        if hasattr(self.model, "point"):
            return self.filter(
                point__latitude__gte=south,
                point__latitude__lte=north,
                point__longitude__gte=west,
                point__longitude__lte=east,
            )
        return self

    def by_country(self, *, country: str):
        """Filter by country."""
        if hasattr(self.model, "country"):
            return self.filter(country__iexact=country)
        return self

    def by_region(self, *, state: str):
        """Filter by state/region."""
        if hasattr(self.model, "state"):
            return self.filter(state__iexact=state)
        return self

    def by_city(self, *, city: str):
        """Filter by city."""
        if hasattr(self.model, "city"):
            return self.filter(city__iexact=city)
        return self


class LocationManager(BaseManager):
    """Manager for location-based models."""

    def get_queryset(self):
        return LocationQuerySet(self.model, using=self._db)

    def near_point(self, *, point: Point, distance_km: float = 50):
        return self.get_queryset().near_point(point=point, distance_km=distance_km)

    def within_bounds(self, *, north: float, south: float, east: float, west: float):
        return self.get_queryset().within_bounds(north=north, south=south, east=east, west=west)


class ReviewableQuerySet(BaseQuerySet):
    """QuerySet for models that can be reviewed."""

    def with_review_stats(self):
        """Add review statistics annotations."""
        return self.annotate(
            review_count=Count("reviews", filter=Q(reviews__is_published=True)),
            average_rating=Avg("reviews__rating", filter=Q(reviews__is_published=True)),
            latest_review_date=Max("reviews__created_at", filter=Q(reviews__is_published=True)),
        )

    def highly_rated(self, *, min_rating: float = 8.0):
        """Filter for highly rated items."""
        return self.with_review_stats().filter(average_rating__gte=min_rating)

    def recently_reviewed(self, *, days: int = 30):
        """Filter for items with recent reviews."""
        cutoff_date = timezone.now() - timedelta(days=days)
        return self.filter(reviews__created_at__gte=cutoff_date, reviews__is_published=True).distinct()


class ReviewableManager(BaseManager):
    """Manager for reviewable models."""

    def get_queryset(self):
        return ReviewableQuerySet(self.model, using=self._db)

    def with_review_stats(self):
        return self.get_queryset().with_review_stats()

    def highly_rated(self, *, min_rating: float = 8.0):
        return self.get_queryset().highly_rated(min_rating=min_rating)


class HierarchicalQuerySet(BaseQuerySet):
    """QuerySet for hierarchical models (with parent/child relationships)."""

    def root_level(self):
        """Filter for root-level items (no parent)."""
        if hasattr(self.model, "parent"):
            return self.filter(parent__isnull=True)
        return self

    def children_of(self, *, parent_id: int):
        """Get children of a specific parent."""
        if hasattr(self.model, "parent"):
            return self.filter(parent_id=parent_id)
        return self

    def with_children_count(self):
        """Add count of children."""
        if hasattr(self.model, "children"):
            return self.annotate(children_count=Count("children"))
        return self


class HierarchicalManager(BaseManager):
    """Manager for hierarchical models."""

    def get_queryset(self):
        return HierarchicalQuerySet(self.model, using=self._db)

    def root_level(self):
        return self.get_queryset().root_level()


class TimestampedQuerySet(BaseQuerySet):
    """QuerySet for models with created_at/updated_at timestamps."""

    def created_between(self, *, start_date, end_date):
        """Filter by creation date range."""
        return self.filter(created_at__date__range=[start_date, end_date])

    def updated_since(self, *, since_date):
        """Filter for records updated since a date."""
        return self.filter(updated_at__gte=since_date)

    def by_creation_date(self, *, descending: bool = True):
        """Order by creation date."""
        order = "-created_at" if descending else "created_at"
        return self.order_by(order)


class TimestampedManager(BaseManager):
    """Manager for timestamped models."""

    def get_queryset(self):
        return TimestampedQuerySet(self.model, using=self._db)

    def created_between(self, *, start_date, end_date):
        return self.get_queryset().created_between(start_date=start_date, end_date=end_date)


class StatusQuerySet(BaseQuerySet):
    """QuerySet for models with status fields."""

    def with_status(self, *, status: str | list[str]):
        """Filter by status."""
        if isinstance(status, list):
            return self.filter(status__in=status)
        return self.filter(status=status)

    def operating(self):
        """Filter for operating/active status."""
        return self.filter(status="OPERATING")

    def closed(self):
        """Filter for closed status."""
        return self.filter(status__in=["CLOSED_TEMP", "CLOSED_PERM"])


class StatusManager(BaseManager):
    """Manager for status-based models."""

    def get_queryset(self):
        return StatusQuerySet(self.model, using=self._db)

    def operating(self):
        return self.get_queryset().operating()

    def closed(self):
        return self.get_queryset().closed()
