from django.contrib.auth.hashers import check_password, make_password
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.db.models import Sum, F
from django.utils import timezone


def current_year():
    return timezone.now().year


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):
    pin = models.CharField("PIN", max_length=128, blank=True, default="")

    class Meta:
        verbose_name = "Benutzer"
        verbose_name_plural = "Benutzer"

    def set_pin(self, raw_pin):
        self.pin = make_password(raw_pin)

    def check_pin(self, raw_pin):
        if not self.pin:
            return False
        return check_password(raw_pin, self.pin)

    @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.filter(drink__year=current_year())
            .annotate(cost=F("amount") * F("drink__sale_price_per_bottle"))
            .aggregate(sum=Sum("cost"))
        )
        return query["sum"] or 0

    @property
    def paid_amount(self):
        query = self.user_payments.filter(created_at__year=current_year()).aggregate(sum=Sum("amount"))
        return query["sum"] or 0

    @property
    def open_balance(self):
        return self.consumed_drinks_price - self.paid_amount


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

    class Meta:
        verbose_name = "Spende"
        verbose_name_plural = "Spenden"


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

    class Method(models.TextChoices):
        cash = "cash", "Bar"
        card = "card", "EC-Karte"
        paypal = "paypal", "PayPal"
        bank = "bank", "Überweisung"

    purpose = models.CharField("Zweck", max_length=64)
    date = models.DateField("Datum")
    amount = PriceField("Betrag")
    method = models.CharField("Methode", max_length=16, choices=Method.choices, default=Method.paypal)

    class Meta:
        verbose_name = "Ausgabe"
        verbose_name_plural = "Ausgaben"


class Drink(models.Model):
    class Category(models.TextChoices):
        beer = "beer", "Bier"
        radler = "radler", "Radler"
        sekt = "sekt", "Sekt"
        alc_free_beer = "alc_free_beer", "Bier alkoholfrei"
        alc_free_radler = "alc_free_radler", "Radler alkoholfrei"
        soft = "soft", "Softdrink"
        water = "water", "Wasser"

    name = models.CharField("Name", max_length=32)
    year = models.PositiveSmallIntegerField("Jahr", default=2024)
    category = models.CharField(
        "Kategorie",
        max_length=16,
        choices=Category.choices,
        default=Category.beer,
    )
    crates_ordered = models.PositiveSmallIntegerField(
        "Kästen bestellt",
        help_text="nur zur Info, wie gut wir geplant haben — nicht die tatsächlich konsumierten/bezahlten Flaschen",
    )
    crates_purchased = models.PositiveSmallIntegerField("Kästen gekauft")
    crates_returned = models.PositiveSmallIntegerField("Kästen leer zurück")
    purchase_price_per_crate = PriceField("Einkaufspreis pro Kasten")
    deposit_per_crate = PriceField("Pfand pro Kasten")
    bottles_per_crate = models.PositiveSmallIntegerField("Flaschen pro Kasten")
    bottle_size = models.FloatField("Flaschengröße (l)")
    sale_price_per_bottle = PriceField("Verkaufspreis pro Flasche")

    class Meta:
        unique_together = (("name", "year"),)
        verbose_name = "Getränk"
        verbose_name_plural = "Getränke"

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

    @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

    @property
    def crates_full_returned(self):
        return self.crates_ordered - self.crates_purchased

    @property
    def crates_remaining(self):
        return self.crates_purchased - self.crates_returned

    @property
    def bottles_consumed(self):
        return self.bottles_sold + self.bottles_given_away

    @property
    def bottles_remaining(self):
        return self.bottles_total - self.bottles_consumed

    @property
    def remaining_purchase_value(self):
        return self.bottles_remaining * self.purchase_price_per_bottle


class UserPayment(models.Model):
    class Method(models.TextChoices):
        cash = "cash", "Bar"
        paypal = "paypal", "PayPal"
        bank = "bank", "Überweisung"
        other = "other", "Sonstiges"

    user = models.ForeignKey(
        verbose_name="Benutzer",
        to=User,
        on_delete=models.CASCADE,
        related_name="user_payments",
        related_query_name="user_payment",
    )
    amount = PriceField("Betrag")
    method = models.CharField("Methode", max_length=16, choices=Method.choices)
    note = models.CharField("Notiz", max_length=64, blank=True, default="")
    created_at = models.DateTimeField("Bezahlt am", auto_now_add=True)

    class Meta:
        verbose_name = "Zahlung"
        verbose_name_plural = "Zahlungen"


class Consumption(models.Model):
    user = models.ForeignKey(
        verbose_name="Benutzer",
        to=User,
        on_delete=models.CASCADE,
        related_name="consumption_list",
        related_query_name="consumption",
    )
    drink = models.ForeignKey(
        verbose_name="Getränk",
        to=Drink,
        on_delete=models.CASCADE,
        related_name="consumption_list",
        related_query_name="consumption",
    )
    amount = models.PositiveSmallIntegerField("Anzahl")
    day = models.PositiveSmallIntegerField("Tag", choices=[(1, "Do"), (2, "Fr"), (3, "Sa"), (4, "So")])
    for_free = models.BooleanField("Gratis", default=False)
    created_at = models.DateTimeField("Gebucht am", auto_now_add=True, null=True)

    class Meta:
        verbose_name = "Konsum"
        verbose_name_plural = "Konsum"
