"""
Models for ride ranking system using Internet Roller Coaster Poll algorithm.

This system calculates rankings based on pairwise comparisons between rides,
where each ride is compared to every other ride to determine which one
more riders preferred.
"""

import pghistory
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.utils import timezone


@pghistory.track()
class RideRanking(models.Model):
    """
    Stores calculated rankings for rides using the Internet Roller Coaster Poll algorithm.

    Rankings are recalculated daily based on user reviews/ratings.
    Each ride's rank is determined by its winning percentage in pairwise comparisons.
    """

    ride = models.OneToOneField(
        "rides.Ride", on_delete=models.CASCADE, related_name="ranking", help_text="Ride this ranking entry describes"
    )

    # Core ranking metrics
    rank = models.PositiveIntegerField(db_index=True, help_text="Overall rank position (1 = best)")
    wins = models.PositiveIntegerField(default=0, help_text="Number of rides this ride beats in pairwise comparisons")
    losses = models.PositiveIntegerField(
        default=0,
        help_text="Number of rides that beat this ride in pairwise comparisons",
    )
    ties = models.PositiveIntegerField(
        default=0,
        help_text="Number of rides with equal preference in pairwise comparisons",
    )
    winning_percentage = models.DecimalField(
        max_digits=5,
        decimal_places=4,
        validators=[MinValueValidator(0), MaxValueValidator(1)],
        db_index=True,
        help_text="Win percentage where ties count as 0.5",
    )

    # Additional metrics
    mutual_riders_count = models.PositiveIntegerField(
        default=0, help_text="Total number of users who have rated this ride"
    )
    comparison_count = models.PositiveIntegerField(
        default=0, help_text="Number of other rides this was compared against"
    )
    average_rating = models.DecimalField(
        max_digits=3,
        decimal_places=2,
        null=True,
        blank=True,
        validators=[MinValueValidator(1), MaxValueValidator(10)],
        help_text="Average rating from all users who have rated this ride",
    )

    # Metadata
    last_calculated = models.DateTimeField(default=timezone.now, help_text="When this ranking was last calculated")
    calculation_version = models.CharField(
        max_length=10, default="1.0", help_text="Algorithm version used for calculation"
    )

    class Meta:
        verbose_name = "Ride Ranking"
        verbose_name_plural = "Ride Rankings"
        ordering = ["rank"]
        indexes = [
            models.Index(fields=["rank"]),
            models.Index(fields=["winning_percentage", "-mutual_riders_count"]),
            models.Index(fields=["ride", "last_calculated"]),
        ]
        constraints = [
            models.CheckConstraint(
                name="rideranking_winning_percentage_range",
                check=models.Q(winning_percentage__gte=0) & models.Q(winning_percentage__lte=1),
                violation_error_message="Winning percentage must be between 0 and 1",
            ),
            models.CheckConstraint(
                name="rideranking_average_rating_range",
                check=models.Q(average_rating__isnull=True)
                | (models.Q(average_rating__gte=1) & models.Q(average_rating__lte=10)),
                violation_error_message="Average rating must be between 1 and 10",
            ),
        ]

    def __str__(self):
        return f"#{self.rank} - {self.ride.name} ({self.winning_percentage:.1%})"

    @property
    def total_comparisons(self):
        """Total number of pairwise comparisons (wins + losses + ties)."""
        return self.wins + self.losses + self.ties


@pghistory.track()
class RidePairComparison(models.Model):
    """
    Caches pairwise comparison results between two rides.

    This model stores the results of comparing two rides based on mutual riders
    (users who have rated both rides). It's used to speed up ranking calculations.
    """

    ride_a = models.ForeignKey("rides.Ride", on_delete=models.CASCADE, related_name="comparisons_as_a")
    ride_b = models.ForeignKey("rides.Ride", on_delete=models.CASCADE, related_name="comparisons_as_b")

    # Comparison results
    ride_a_wins = models.PositiveIntegerField(default=0, help_text="Number of mutual riders who rated ride_a higher")
    ride_b_wins = models.PositiveIntegerField(default=0, help_text="Number of mutual riders who rated ride_b higher")
    ties = models.PositiveIntegerField(default=0, help_text="Number of mutual riders who rated both rides equally")

    # Metrics
    mutual_riders_count = models.PositiveIntegerField(
        default=0, help_text="Total number of users who have rated both rides"
    )
    ride_a_avg_rating = models.DecimalField(
        max_digits=3,
        decimal_places=2,
        null=True,
        blank=True,
        help_text="Average rating of ride_a from mutual riders",
    )
    ride_b_avg_rating = models.DecimalField(
        max_digits=3,
        decimal_places=2,
        null=True,
        blank=True,
        help_text="Average rating of ride_b from mutual riders",
    )

    # Metadata
    last_calculated = models.DateTimeField(auto_now=True, help_text="When this comparison was last calculated")

    class Meta:
        verbose_name = "Ride Pair Comparison"
        verbose_name_plural = "Ride Pair Comparisons"
        ordering = ["ride_a", "ride_b"]
        unique_together = [["ride_a", "ride_b"]]
        indexes = [
            models.Index(fields=["ride_a", "ride_b"]),
            models.Index(fields=["last_calculated"]),
        ]

    def __str__(self):
        winner = "TIE"
        if self.ride_a_wins > self.ride_b_wins:
            winner = self.ride_a.name
        elif self.ride_b_wins > self.ride_a_wins:
            winner = self.ride_b.name
        return f"{self.ride_a.name} vs {self.ride_b.name} - Winner: {winner}"

    @property
    def winner(self):
        """Returns the winning ride or None for a tie."""
        if self.ride_a_wins > self.ride_b_wins:
            return self.ride_a
        elif self.ride_b_wins > self.ride_a_wins:
            return self.ride_b
        return None

    @property
    def is_tie(self):
        """Returns True if the comparison resulted in a tie."""
        return self.ride_a_wins == self.ride_b_wins


class RankingSnapshot(models.Model):
    """
    Stores historical snapshots of rankings for tracking changes over time.

    This allows us to show ranking trends and movements.
    """

    ride = models.ForeignKey("rides.Ride", on_delete=models.CASCADE, related_name="ranking_history")
    rank = models.PositiveIntegerField()
    winning_percentage = models.DecimalField(max_digits=5, decimal_places=4)
    snapshot_date = models.DateField(db_index=True, help_text="Date when this ranking snapshot was taken")

    class Meta:
        verbose_name = "Ranking Snapshot"
        verbose_name_plural = "Ranking Snapshots"
        unique_together = [["ride", "snapshot_date"]]
        ordering = ["-snapshot_date", "rank"]
        indexes = [
            models.Index(fields=["snapshot_date", "rank"]),
            models.Index(fields=["ride", "-snapshot_date"]),
        ]

    def __str__(self):
        return f"{self.ride.name} - Rank #{self.rank} on {self.snapshot_date}"
