from django.db import models
from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import transaction
from decimal import Decimal

from django.utils import timezone
from django.utils.timezone import now
from customers.models import Customer
from inventory.models import Product, Batch
import logging


logger = logging.getLogger(__name__)


class Sale(models.Model):
    PAYMENT_METHOD_CHOICES = [
        ('cash', 'Cash'),
        ('mpesa', 'M-Pesa'),
        ('credit', 'Credit'),
        ('cheque', 'Cheque'),
    ]

    # Core Fields
    customer = models.ForeignKey(Customer, null=True, blank=True, on_delete=models.SET_NULL)
    total_amount = models.DecimalField(max_digits=10, decimal_places=2, default=0)
    payment_method = models.CharField(max_length=10, choices=PAYMENT_METHOD_CHOICES)
    sale_date = models.DateTimeField(default=now, db_index=True)
    is_complete = models.BooleanField(default=False)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

    # Cash/M-Pesa Fields
    cash_given = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
    change_due = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
    mpesa_reference = models.CharField(max_length=255, null=True, blank=True)

    # Credit Fields
    credit_amount = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
    credit_paid_date = models.DateField(null=True, blank=True)

    # Cheque Fields
    cheque_number = models.CharField(max_length=50, null=True, blank=True)
    cheque_date = models.DateField(null=True, blank=True)
    cheque_cleared = models.BooleanField(default=False)

    class Meta:
        ordering = ['-sale_date']

    def __str__(self):
        return f"Sale #{self.id} - {self.total_amount} ({self.payment_method})"

    def clean(self):
        """Validation for payment-specific fields"""
        if self.payment_method == 'cash' and not self.cash_given:
            raise ValidationError("Cash amount given is required for cash payments.")

        if self.payment_method == 'mpesa' and not self.mpesa_reference:
            raise ValidationError("M-Pesa reference is required.")

        if self.payment_method == 'credit':
            if self.credit_amount is None:  # Only raise error if None (not 0)
                raise ValidationError("Credit amount must be specified (use 0 for no deposit).")

        if self.payment_method == 'cheque':
            if not self.cheque_number:
                raise ValidationError("Cheque number is required.")
            if not self.cheque_date:
                raise ValidationError("Cheque date is required.")
            
            # Check for duplicate cheque number for the same customer
            if self.customer and self.cheque_number:
                existing_cheque = Sale.objects.filter(
                    customer=self.customer,
                    cheque_number=self.cheque_number,
                    payment_method='cheque'
                ).exclude(id=self.id if self.id else None)
                
                if existing_cheque.exists():
                    raise ValidationError(f"Cheque number {self.cheque_number} already exists for this customer.")

            # Warning for past dates (instead of blocking)
            if self.cheque_date and self.cheque_date < timezone.now().date():
                logger.warning(
                    f"Past-dated cheque recorded: #{self.cheque_number} "
                    f"(Date: {self.cheque_date}, Amount: {self.total_amount})"
                )

    def save(self, *args, **kwargs):
        """Handle payment completion logic"""
        self.full_clean()  # Enforce validation

        # Auto-calculate change for cash payments
        if self.payment_method == 'cash' and self.cash_given:
            self.change_due = self.cash_given - self.total_amount
            self.is_complete = self.change_due >= 0

        # Mark other payment methods as complete
        elif self.payment_method in ['mpesa', 'cheque']:
            self.is_complete = True

        super().save(*args, **kwargs)

    @property
    def outstanding_credit(self):
        """Calculate remaining credit for partial payments"""
        if self.payment_method == 'credit':
            return max(self.total_amount - (self.credit_amount or 0), Decimal(0))
        return Decimal(0)


class SaleItem(models.Model):
    sale = models.ForeignKey(Sale, related_name='items', on_delete=models.CASCADE)
    product = models.ForeignKey(Product, on_delete=models.PROTECT)
    batch = models.ForeignKey(Batch, on_delete=models.PROTECT, null=True, blank=True)
    quantity = models.PositiveIntegerField()
    selling_price = models.DecimalField(max_digits=10, decimal_places=2)
    subtotal = models.DecimalField(max_digits=10, decimal_places=2, editable=False)

    def clean(self):
        """Validate stock availability"""
        if self.batch and self.quantity > self.batch.quantity:
            raise ValidationError(f"Only {self.batch.quantity} units available in batch {self.batch.batch_code}")
        elif not self.batch and self.quantity > self.product.stock_quantity:
            raise ValidationError(f"Only {self.product.stock_quantity} units available")

    def save(self, *args, **kwargs):
        """Auto-calculate prices and update stock"""
        # Set price from batch or product only if not already set
        if not self.selling_price:
            self.selling_price = self.batch.selling_price if self.batch else self.product.selling_price
        self.subtotal = self.selling_price * self.quantity

        with transaction.atomic():
            super().save(*args, **kwargs)
            self.update_stock()

    def update_stock(self):
        """FIFO stock reduction with thread safety"""
        if self.batch:
            self.batch.quantity -= self.quantity
            self.batch.save()
        self.product.stock_quantity -= self.quantity
        self.product.save()

    def __str__(self):
        return f"{self.quantity}x {self.product.name} @ {self.selling_price}"