from django.core.validators import MinValueValidator, MaxValueValidator
from django.db import models
from django.db.models import Avg

from users.models import Player

class ModelWithUpdate(models.Model):
    class Meta:
        abstract = True

    def update(self, commit=False, **kwargs):
        for key, value in kwargs.items():
            setattr(self, key, value)
        if commit:
            self.save()

class Author(ModelWithUpdate):
    name        = models.CharField(max_length=255)
    author_id    = models.IntegerField()

    def __str__(self):
        return f"{self.author_id} - {self.name}"

class Level(models.Model):
    author      = models.ForeignKey(Author, on_delete=models.CASCADE)
    title       = models.CharField(max_length=64)
    level_id     = models.IntegerField()
    darts       = models.SmallIntegerField()
    target      = models.SmallIntegerField()
    plays       = models.IntegerField()
    beats       = models.IntegerField()
    rating      = models.FloatField(null=True, blank=True)
    data        = models.CharField(max_length=288)

    played_by = models.ManyToManyField(Player, blank=True, related_name="bloonsa_levels_played")

    @property
    def play_count(self):
        return self.played_by.count()

    @property
    def win_count(self):
        return LevelScore.objects.filter(level=self, clear=True).count()

    @property
    def dart_glitch_count(self):
        return LevelScore.objects.filter(level=self, dart_glitch_ever=True).count()

    @property
    def stars(self):
        result = list(self.bloonsa_level_ratings.aggregate(Avg("rating")).values())[0]
        return 0 if result is None else round(result)


    def __str__(self):
        return f"{self.level_id}: {self.author.name} - {self.title}"

    def get_flash_vars(self, seperator):
        flashVars = {
            "levelNum": self.level_id,
            "title": self.title,
            "darts": self.darts,
            "target": self.target,
            "data": self.data,
            "authorID": self.author.author_id,
            "rating": self.rating if self.rating else 0.0,
        }
        flashVarsStr = f"{seperator}".join([f"{k}={v}"
                                            for k, v in flashVars.items()])
        return flashVarsStr


class LevelRating(ModelWithUpdate):
    player = models.ForeignKey(Player, on_delete=models.CASCADE, related_name="bloonsa_level_ratings", null=True)
    level = models.ForeignKey(Level, on_delete=models.CASCADE, related_name="bloonsa_level_ratings")
    rating = models.SmallIntegerField(validators=[MinValueValidator(1),
                                                  MaxValueValidator(10)])


    def __str__(self):
        return (f"{self.player.user.username if self.player else ''}"
                f" - [<{int(self.rating) * '★'}> - {self.level.title}]")

# There should only be 1 score per player
# Highest popcount wins
class LevelScore(ModelWithUpdate):
    player = models.ForeignKey(Player, on_delete=models.CASCADE, related_name="bloonsa_level_scores", null=True)
    level = models.ForeignKey(Level, on_delete=models.CASCADE)
    clear = models.BooleanField(default=True)  # This is for if we ever submit scores for failed attempts
    darts_left = models.PositiveSmallIntegerField()
    dart_glitch = models.BooleanField(default=False)  # This is triggered when the player lingers their last dart
    dart_glitch_ever = models.BooleanField(default=False)  # If they ever performed dart glitch in the past
    pops = models.PositiveSmallIntegerField()

    def __str__(self):
        clear_state = "✅" if self.clear else "❌"
        dart_glitch_state = "✏" if self.dart_glitch else "🖋" if self.dart_glitch_ever else ""
        score_view = f"🎈{self.pops} | 🎯{self.darts_left} {dart_glitch_state}"
        return (f"{self.player.user.username if self.player else ''}'s "
                f"{clear_state} @ {self.level}: {score_view}")

class Config(ModelWithUpdate):
    player = models.OneToOneField(Player, on_delete=models.CASCADE, related_name="bloonsa_config")

    # Automatically start ruffle/flash when page loads
    autoplay = models.BooleanField(default=True)
