from datetime import timedelta

import pghistory
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.db.models import Count
from django.utils import timezone


@pghistory.track()
class PageView(models.Model):
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, related_name="page_views")
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey("content_type", "object_id")

    timestamp = models.DateTimeField(auto_now_add=True, db_index=True)
    ip_address = models.GenericIPAddressField()
    user_agent = models.CharField(max_length=512, blank=True)

    class Meta:
        indexes = [
            models.Index(fields=["timestamp"]),
            models.Index(fields=["content_type", "object_id"]),
        ]

    @classmethod
    def get_trending_items(cls, model_class, hours=168, limit=10):
        """Get trending items of a specific model class based on views in last X hours.

        Args:
            model_class: The model class to get trending items for (e.g., Park, Ride)
            hours (int): Number of hours to look back for views (default: 168 = 7 days)
            limit (int): Maximum number of items to return (default: 10)

        Returns:
            QuerySet: The trending items ordered by view count
        """
        content_type = ContentType.objects.get_for_model(model_class)
        cutoff = timezone.now() - timedelta(hours=hours)

        # Query through the ContentType relationship
        item_ids = (
            cls.objects.filter(content_type=content_type, timestamp__gte=cutoff)
            .values("object_id")
            .annotate(view_count=Count("id"))
            .filter(view_count__gt=0)
            .order_by("-view_count")
            .values_list("object_id", flat=True)[:limit]
        )

        # Get the actual items in the correct order
        if item_ids:
            # Convert the list to a string of comma-separated values
            id_list = list(item_ids)
            # Use Case/When to preserve the ordering
            from django.db.models import Case, When

            preserved = Case(*[When(pk=pk, then=pos) for pos, pk in enumerate(id_list)])
            return model_class.objects.filter(pk__in=id_list).order_by(preserved)

        return model_class.objects.none()

    @classmethod
    def get_views_growth(cls, content_type, object_id, current_period_hours, previous_period_hours):
        """Get view growth statistics between two time periods.

        Args:
            content_type: ContentType instance for the model
            object_id: ID of the specific object
            current_period_hours: Hours for current period (e.g., 24)
            previous_period_hours: Hours for previous period (e.g., 48)

        Returns:
            tuple: (current_views, previous_views, growth_percentage)
        """
        from datetime import timedelta

        now = timezone.now()

        # Current period: last X hours
        current_start = now - timedelta(hours=current_period_hours)
        current_views = cls.objects.filter(
            content_type=content_type, object_id=object_id, timestamp__gte=current_start
        ).count()

        # Previous period: X hours before current period
        previous_start = now - timedelta(hours=previous_period_hours)
        previous_end = current_start
        previous_views = cls.objects.filter(
            content_type=content_type,
            object_id=object_id,
            timestamp__gte=previous_start,
            timestamp__lt=previous_end,
        ).count()

        # Calculate growth percentage
        if previous_views == 0:
            growth_percentage = current_views * 100 if current_views > 0 else 0
        else:
            growth_percentage = ((current_views - previous_views) / previous_views) * 100

        return current_views, previous_views, growth_percentage

    @classmethod
    def get_total_views_count(cls, content_type, object_id, hours=168):
        """Get total view count for an object within specified hours.

        Args:
            content_type: ContentType instance for the model
            object_id: ID of the specific object
            hours: Number of hours to look back (default: 168 = 7 days)

        Returns:
            int: Total view count
        """
        cutoff = timezone.now() - timedelta(hours=hours)
        return cls.objects.filter(content_type=content_type, object_id=object_id, timestamp__gte=cutoff).count()
