from django.db import models
from django.utils.translation import gettext_lazy as _
from django.core.exceptions import ValidationError

from decimal import Decimal
from datetime import datetime


class PaymentMethods(models.TextChoices):
    CASH = 'CASH', _('Efectivo')
    CONFIAR = 'CONFIAR', _('Confiar')
    BANCOLOMBIA = 'BANCOLOMBIA', _('Bancolombia')


class Customer(models.Model):
    name = models.CharField(max_length=100, default=None, null=False, blank=False)
    address = models.CharField(max_length=100, null=True, blank=True)
    email = models.CharField(max_length=100, null=True, blank=True)
    phone = models.CharField(max_length=100, null=True, blank=True)

    def __str__(self):
        return self.name


class MeasuringUnits(models.TextChoices):
    UNIT = 'UNIT', _('Unit')


class ProductCategory(models.Model):
    name = models.CharField(max_length=100, unique=True)

    def __str__(self):
        return self.name


class Product(models.Model):
    name = models.CharField(max_length=100, unique=True)
    price = models.DecimalField(max_digits=9, decimal_places=2)
    measuring_unit = models.CharField(
        max_length=20,
        choices=MeasuringUnits.choices,
        default=MeasuringUnits.UNIT
    )
    categories = models.ManyToManyField(ProductCategory)

    def __str__(self):
        return self.name

    @classmethod
    def to_list(cls):
        products_list = []
        all_products = cls.objects.all()
        for product in all_products:
            rproduct = {
                "id": product.id,
                "name": product.name,
                "price_list": product.price,
                "uom": product.measuring_unit,
                "categories": [c.name for c in product.categories.all()]
            }
            products_list.append(rproduct)
        return products_list


class ReconciliationJar(models.Model):
    is_valid = models.BooleanField(default=False)
    date_time = models.DateTimeField()
    description = models.CharField(max_length=255, null=True, blank=True)
    reconcilier = models.CharField(max_length=255, null=False, blank=False)
    cash_taken = models.DecimalField(max_digits=9, decimal_places=2)
    cash_discrepancy = models.DecimalField(max_digits=9, decimal_places=2)
    total_cash_purchases = models.DecimalField(max_digits=9, decimal_places=2)

    def clean(self):
        self._validate_taken_ammount()

    def add_payments(self, payments):
        for payment in payments:
            self.payment_set.add(payment)
        self.is_valid = True

    def _validate_taken_ammount(self):
        ammount_cash = self.cash_taken + self.cash_discrepancy
        if not self.total_cash_purchases == ammount_cash:
            raise ValidationError(
                {"cash_taken": _("The taken ammount has discrepancy.")}
            )


class Sale(models.Model):
    customer = models.ForeignKey(Customer, on_delete=models.PROTECT)
    date = models.DateTimeField("Date")
    phone = models.CharField(max_length=13, null=True, blank=True)
    description = models.CharField(max_length=255, null=True, blank=True)
    payment_method = models.CharField(
        max_length=30,
        choices=PaymentMethods.choices,
        default=PaymentMethods.CASH,
        blank=False,
        null=False
    )
    reconciliation = models.ForeignKey(
        ReconciliationJar,
        on_delete=models.RESTRICT,
        related_name='Sales',
        null=True
    )

    def __str__(self):
        return f"{self.date} {self.customer}"

    def get_total(self):
        lines = self.saleline_set.all()
        return sum([l.quantity * l.unit_price for l in lines])

    def clean(self):
        if self.payment_method not in PaymentMethods.values:
            raise ValidationError({'payment_method': "Invalid payment method"})

    @classmethod
    def sale_header_csv(cls):
        sale_header_csv = [field.name for field in cls._meta.fields]

        return sale_header_csv


class SaleLine(models.Model):

    sale = models.ForeignKey(Sale, on_delete=models.CASCADE)
    product = models.ForeignKey(Product, null=False, blank=False, on_delete=models.CASCADE)
    quantity = models.IntegerField(null=True)
    unit_price = models.DecimalField(max_digits=9, decimal_places=2)
    description = models.CharField(max_length=255, null=True, blank=True)

    def __str__(self):
        return f"{self.sale} - {self.product}"


class ReconciliationJarSummary():
    def __init__(self, payments):
        self._validate_payments(payments)
        self._payments = payments

    def _validate_payments(self, payments):
        pass

    @property
    def total(self):
        return sum([p.amount for p in self.payments])

    @property
    def payments(self):
        return self._payments


class Payment(models.Model):
    date_time = models.DateTimeField()
    type_payment = models.CharField(
        max_length=30,
        choices=PaymentMethods.choices,
        default=PaymentMethods.CASH
    )
    amount = models.DecimalField(max_digits=9, decimal_places=2)
    reconciliation_jar = models.ForeignKey(
        ReconciliationJar,
        null=True,
        default=None,
        blank=True,
        on_delete=models.RESTRICT
    )
    description = models.CharField(max_length=255, null=True, blank=True)

    @classmethod
    def get_reconciliation_jar_summary(cls):
        return ReconciliationJarSummary(
            cls.objects.filter(
                type_payment=PaymentMethods.CASH,
                reconciliation_jar=None
            )
        )

    @classmethod
    def total_payment_from_sale(cls, payment_method, sale):
        payment = cls()
        payment.date_time = datetime.today()
        payment.type_payment = payment_method
        payment.amount = sale.get_total()
        payment.clean()
        payment.save()

        payment_sale = PaymentSale()
        payment_sale.payment = payment
        payment_sale.sale = sale
        payment_sale.clean()
        payment_sale.save()


class PaymentSale(models.Model):
    payment = models.ForeignKey(Payment, on_delete=models.CASCADE)
    sale = models.ForeignKey(Sale, on_delete=models.CASCADE)


class AdminCode(models.Model):
    value = models.CharField(max_length=255, null=False, blank=False)