from django.contrib.auth.models import AbstractUser
from django.db import models
from django.db.models import Sum, F


class PriceField(models.DecimalField):

    def __init__(self, verbose_name=None, name=None, **kwargs):
        kwargs.update({"max_digits": 6, "decimal_places": 2})
        super().__init__(verbose_name, name, **kwargs)


class User(AbstractUser):

    @property
    def paid_drinks(self):
        return self.consumption_list.filter(for_free=False)

    @property
    def free_drinks(self):
        return self.consumption_list.filter(for_free=True)

    @property
    def consumed_drinks_price(self):
        query = self.paid_drinks \
            .annotate(cost=F("amount") * F("drink__sale_price_per_bottle")) \
            .aggregate(sum=Sum("cost"))
        return query["sum"] or 0


class Donation(models.Model):
    date = models.DateField()
    amount = PriceField()
    note = models.CharField(max_length=64, blank=True, default="")


class Payment(models.Model):
    class Topic(models.IntegerChoices):
        site = 10, "Gelände"
        bands = 11, "Bands"
        supply_purchase = 12, "Getränke-/Essenseinkauf"

    purpose = models.CharField(max_length=64)
    date = models.DateField()
    amount = PriceField()



class Drink(models.Model):
    name = models.CharField(max_length=32, unique=True)
    crates_ordered = models.PositiveSmallIntegerField(
        help_text="just informational to see how good we planned, not the actual consumed/paid drinks"
    )
    crates_purchased = models.PositiveSmallIntegerField()
    crates_returned = models.PositiveSmallIntegerField()
    purchase_price_per_crate = PriceField()
    deposit_per_crate = PriceField()
    bottles_per_crate = models.PositiveSmallIntegerField()
    bottle_size = models.FloatField()
    sale_price_per_bottle = PriceField()

    def __str__(self):
        return self.name

    @property
    def bottles_total(self):
        return self.bottles_per_crate * self.crates_purchased

    @property
    def bottles_returned(self):
        return self.bottles_per_crate * self.crates_returned

    @property
    def amount_per_crate(self):
        return self.bottle_size * self.bottles_per_crate

    @property
    def amount_total(self):
        return self.amount_per_crate * self.crates_purchased

    @property
    def purchase_price_per_bottle(self):
        return self.purchase_price_per_crate / self.bottles_per_crate

    @property
    def purchase_price_total(self):
        return self.purchase_price_per_crate * self.crates_purchased

    @property
    def deposit_total(self):
        return self.deposit_per_crate * self.crates_purchased

    @property
    def deposit_refund(self):
        return self.deposit_per_crate * self.crates_returned

    @property
    def deposit_kept(self):
        return self.deposit_total - self.deposit_refund

    @property
    def bottles_sold(self):
        query = self.consumption_list.filter(for_free=False).aggregate(sum=Sum("amount"))
        return query["sum"] or 0

    @property
    def sales_purchase_value(self):
        return self.bottles_sold * self.purchase_price_per_bottle

    @property
    def sale_price_total(self):
        return self.bottles_sold * self.sale_price_per_bottle

    @property
    def bottles_given_away(self):
        query = self.consumption_list.filter(for_free=True).aggregate(sum=Sum("amount"))
        return query["sum"] or 0

    @property
    def giveaway_purchase_value(self):
        return self.bottles_given_away * self.purchase_price_per_bottle

    @property
    def balance(self):
        return self.sale_price_total - self.sales_purchase_value - self.giveaway_purchase_value


class Consumption(models.Model):
    user = models.ForeignKey(
        to=User, on_delete=models.CASCADE,
        related_name="consumption_list", related_query_name="consumption")
    drink = models.ForeignKey(
        to=Drink, on_delete=models.CASCADE,
        related_name="consumption_list", related_query_name="consumption")
    amount = models.PositiveSmallIntegerField()
    day = models.PositiveSmallIntegerField(choices=[(1, "Do"), (2, "Fr"), (3, "Sa"), (4, "So")])
    for_free = models.BooleanField(default=False)
