import uuid
from decimal import Decimal

from django.db import models
from django.db.models import Q

from .constants import (
    BASIC,
    NUTRITION_LEVELS,
    NUTRITION_POLICY_KEY,
    NUTRITION_POLICY_VERSION,
    PREMIUM,
    PROFESSIONAL,
    STANDARD,
)
from .exceptions import NutritionHistoryImmutableError


class NutritionLevel(models.TextChoices):
    BASIC = BASIC, "Basic"
    STANDARD = STANDARD, "Standard"
    PROFESSIONAL = PROFESSIONAL, "Professional"
    PREMIUM = PREMIUM, "Premium"


class PlayerNutritionProfile(models.Model):
    """Authoritative current nutrition state for a Player.

    No profile means the default BASIC policy.  Explicit selections create this
    row through NutritionProfileService and increment `revision` monotonically.
    """

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    player = models.OneToOneField(
        "players.Player",
        on_delete=models.PROTECT,
        related_name="nutrition_profile",
    )
    level = models.CharField(max_length=20, choices=NutritionLevel.choices)
    revision = models.PositiveIntegerField(default=1)
    policy_key = models.CharField(max_length=100, default=NUTRITION_POLICY_KEY)
    policy_version = models.CharField(max_length=30, default=NUTRITION_POLICY_VERSION)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["player_id"]
        constraints = [
            models.CheckConstraint(
                condition=Q(level__in=NUTRITION_LEVELS),
                name="nutrition_profile_valid_level",
            ),
            models.CheckConstraint(
                condition=Q(revision__gte=1),
                name="nutrition_profile_revision_gte_1",
            ),
            models.CheckConstraint(
                condition=~Q(policy_key=""),
                name="nutrition_profile_policy_key_nonempty",
            ),
            models.CheckConstraint(
                condition=~Q(policy_version=""),
                name="nutrition_profile_policy_ver_nonempty",
            ),
        ]

    def __str__(self):
        return f"{self.player_id}: {self.level} r{self.revision}"


class _ImmutableNutritionHistoryQuerySet(models.QuerySet):
    def update(self, **kwargs):
        raise NutritionHistoryImmutableError("Nutrition history is immutable.")

    def delete(self):
        raise NutritionHistoryImmutableError("Nutrition history is immutable.")


class NutritionSelectionEvent(models.Model):
    """Immutable audit event for an explicit player nutrition selection."""

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    player = models.ForeignKey(
        "players.Player",
        on_delete=models.PROTECT,
        related_name="nutrition_selection_events",
    )
    revision = models.PositiveIntegerField()
    previous_level = models.CharField(
        max_length=20,
        choices=NutritionLevel.choices,
        null=True,
        blank=True,
    )
    new_level = models.CharField(max_length=20, choices=NutritionLevel.choices)
    multiplier_snapshot = models.DecimalField(max_digits=8, decimal_places=6)
    policy_key = models.CharField(max_length=100)
    policy_version = models.CharField(max_length=30)
    created_at = models.DateTimeField(auto_now_add=True)

    objects = _ImmutableNutritionHistoryQuerySet.as_manager()

    class Meta:
        ordering = ["player_id", "revision"]
        constraints = [
            models.UniqueConstraint(
                fields=["player", "revision"],
                name="uq_nutrition_player_revision",
            ),
            models.CheckConstraint(
                condition=Q(revision__gte=1),
                name="nutrition_event_revision_gte_1",
            ),
            models.CheckConstraint(
                condition=Q(previous_level__isnull=True) | Q(previous_level__in=NUTRITION_LEVELS),
                name="nutrition_event_valid_previous",
            ),
            models.CheckConstraint(
                condition=Q(new_level__in=NUTRITION_LEVELS),
                name="nutrition_event_valid_new",
            ),
            models.CheckConstraint(
                condition=(
                    Q(new_level=BASIC, multiplier_snapshot=Decimal("1.000000"))
                    | Q(new_level=STANDARD, multiplier_snapshot=Decimal("1.050000"))
                    | Q(new_level=PROFESSIONAL, multiplier_snapshot=Decimal("1.100000"))
                    | Q(new_level=PREMIUM, multiplier_snapshot=Decimal("1.150000"))
                ),
                name="nutrition_event_multiplier_matches",
            ),
            models.CheckConstraint(
                condition=~Q(policy_key=""),
                name="nutrition_event_policy_key_nonempty",
            ),
            models.CheckConstraint(
                condition=~Q(policy_version=""),
                name="nutrition_event_policy_ver_nonempty",
            ),
        ]
        indexes = [
            models.Index(fields=["player", "-created_at"], name="nutrition_player_event_idx"),
        ]

    def save(self, *args, **kwargs):
        if not self._state.adding:
            raise NutritionHistoryImmutableError("Nutrition history is immutable.")
        return super().save(*args, **kwargs)

    def delete(self, *args, **kwargs):
        raise NutritionHistoryImmutableError("Nutrition history is immutable.")
