import uuid

from django.conf import settings
from django.db import models
from django.db.models import Sum


class Sale(models.Model):
    class PaymentMethod(models.TextChoices):
        CASH = 'CASH', 'Cash'
        MOBILE = 'MOBILE', 'Mobile Money'
        BANK = 'BANK', 'Bank'
        CREDIT = 'CREDIT', 'Credit'

    invoice_number = models.CharField(max_length=50, unique=True)
    # Unguessable id for the public invoice link/QR code — invoice_number is
    # sequential and would let anyone enumerate every invoice ever issued.
    public_token = models.UUIDField(default=uuid.uuid4, unique=True, editable=False)
    customer_name = models.CharField(max_length=200, blank=True)
    customer_phone = models.CharField(max_length=30, blank=True)
    payment_method = models.CharField(max_length=20, choices=PaymentMethod.choices)
    sold_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.PROTECT,
        related_name='sales',
    )
    sold_at = models.DateTimeField(auto_now_add=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['-sold_at']

    @property
    def total_amount(self):
        return sum((item.subtotal for item in self.items.all()), 0)

    def __str__(self):
        return self.invoice_number


class SaleItem(models.Model):
    class PriceAlert(models.TextChoices):
        BELOW_MINIMUM = 'BELOW_MINIMUM', 'Below minimum'
        ABOVE_MAXIMUM = 'ABOVE_MAXIMUM', 'Above maximum'

    sale = models.ForeignKey(
        Sale,
        on_delete=models.CASCADE,
        related_name='items',
    )
    spare_part = models.ForeignKey(
        'inventory.SparePart',
        on_delete=models.PROTECT,
        related_name='sale_items',
    )
    quantity = models.PositiveIntegerField()
    unit_cost = models.DecimalField(max_digits=15, decimal_places=2)
    unit_price = models.DecimalField(max_digits=15, decimal_places=2)
    price_alert = models.CharField(max_length=20, choices=PriceAlert.choices, blank=True)
    approved_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.PROTECT,
        null=True,
        blank=True,
        related_name='approved_sale_items',
        help_text='Manager who voluntarily approved this line at sale time (optional; not required to sell).',
    )

    @property
    def subtotal(self):
        return self.quantity * self.unit_price

    @property
    def profit(self):
        return self.quantity * (self.unit_price - self.unit_cost)

    @property
    def returned_quantity(self):
        return self.return_items.aggregate(total=Sum('quantity'))['total'] or 0

    def __str__(self):
        return f'{self.spare_part} x{self.quantity}'


class SaleReturn(models.Model):
    """A customer return processed against an existing, immutable Sale."""

    sale = models.ForeignKey(
        Sale,
        on_delete=models.PROTECT,
        related_name='returns',
    )
    processed_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.PROTECT,
        related_name='sale_returns',
    )
    reason = models.CharField(max_length=255, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['-created_at']

    @property
    def total_amount(self):
        return sum((item.subtotal for item in self.items.all()), 0)

    def __str__(self):
        return f'Return for {self.sale.invoice_number}'


class SaleReturnItem(models.Model):
    sale_return = models.ForeignKey(
        SaleReturn,
        on_delete=models.CASCADE,
        related_name='items',
    )
    sale_item = models.ForeignKey(
        SaleItem,
        on_delete=models.PROTECT,
        related_name='return_items',
    )
    quantity = models.PositiveIntegerField()
    # Snapshotted from sale_item at return time, so a later price change on the
    # SparePart never retroactively changes what an old return is reported as.
    unit_price = models.DecimalField(max_digits=15, decimal_places=2)
    unit_cost = models.DecimalField(max_digits=15, decimal_places=2)

    @property
    def subtotal(self):
        return self.quantity * self.unit_price

    @property
    def cost_total(self):
        return self.quantity * self.unit_cost

    def __str__(self):
        return f'{self.sale_item.spare_part} x{self.quantity}'
