from django.db import models
from authentication.models import User
from django.utils import timezone
# from product.models import Product
from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.db.models import Sum
from decimal import Decimal 
from django.db.models.signals import post_save
from django.db import models, transaction
from authentication.models import ActivityLog
from django.db.models import Q
class BaseModel(models.Model):
    created_by = models.ForeignKey(User, on_delete=models.SET_NULL, related_name='%(class)s_created', null=True, blank=True)
    updated_by = models.ForeignKey(User, on_delete=models.SET_NULL, related_name='%(class)s_updated', null=True, blank=True)
    deleted_by = models.ForeignKey(User, on_delete=models.SET_NULL, related_name='%(class)s_deleted', null=True, blank=True)

    created_at = models.DateTimeField(auto_now_add=True,null=True)
    updated_at = models.DateTimeField(auto_now=True,null=True)
    deleted_at = models.DateTimeField(null=True, blank=True)

    class Meta:
        abstract = True

class Supplier(BaseModel): 

    user=models.ForeignKey(User, on_delete=models.CASCADE,null=True,related_name='supplier_user')
    IS_INDIVIDUAL_CHOICES = (
        (True, 'Individual'),
        (False, 'Company'),
    )
    is_individual = models.BooleanField(choices=IS_INDIVIDUAL_CHOICES)
    myob_uid=models.TextField(null=True)
    myob_location=models.TextField(null=True)
    current_row_version=models.TextField(null=True)
    company_name = models.CharField(max_length=255, blank=True, null=True)
    last_name = models.CharField(max_length=255, blank=True, null=True)
    first_name = models.CharField(max_length=255,blank=True, null=True)
    is_active = models.BooleanField(default=True)
    street = models.CharField(max_length=255)
    city = models.CharField(max_length=255)
    state = models.CharField(max_length=255)
    otherCountry = models.IntegerField(default=0)
    supplier_code = models.CharField(max_length=11)

    post_code = models.CharField(max_length=11)
    country = models.CharField(max_length=255)
    phone1 = models.CharField(max_length=21)
    phone2 = models.CharField(max_length=21, blank=True, null=True)
    phone3 = models.CharField(max_length=21, blank=True, null=True)
    fax = models.CharField(max_length=21, blank=True, null=True)
    email = models.EmailField(max_length=255)
    website = models.URLField(max_length=255, blank=True, null=True)
    contact_name = models.CharField(max_length=25, blank=True, null=True)
    salutation = models.CharField(max_length=255, blank=True, null=True)
    abn=models.CharField(max_length=11,blank=True, null=True)
   
    soft_deleted = models.BooleanField(default=False)


    def total_amount(self):
        bills = self.supplier_bill_set.all()  # Assuming you have a related_name for bills
        total_amount = sum(bill.total for bill in bills)
        return "${:,.2f}".format(total_amount)
    

    def due_amount(self):
        bills = self.supplier_bill_set.all()  
        total_amount = sum(bill.total for bill in bills)
        total_paid_amount =  sum(bill.total_paid_amount or 0 for bill in bills)
        due_amount = total_amount - total_paid_amount
        return "${:,.2f}".format(due_amount)
    
    def total_paid_amount(self):
        bills = self.supplier_bill_set.all()  
        total_paid_amount = sum(bill.total_paid_amount or 0 for bill in bills)
        return "${:,.2f}".format(total_paid_amount)
    
     
    
    def soft_delete(self):
        # Soft delete the supplier
        self.soft_deleted = True
        self.deleted_at = timezone.now()
        self.is_active = False
        self.save()

    def undelete(self):
        # Restore the supplier
        self.soft_deleted = False
        self.deleted_at = None
        self.is_active = True
        self.save()

    def __str__(self):
        return self.company_name or f"{self.first_name} {self.last_name}"
        
    def get_status(self):
        return "Active" if self.is_active else "Inactive"
        
        
    @property           
    def get_history_supplier(self):
        supplier_history=ActivityLog.objects.filter(Q(model_name = 'Supplier', instance_id = self.id)| Q(new_data__0__fields__supplier = self.id )).order_by('-timestamp')
        if supplier_history:
            return supplier_history
        else:
            return None 
            
class Supplier_Bill(BaseModel):
    CREATED = 0
    OPEN=1
    CLOSE=2
    
    STATUS_CHOICES = (
        (CREATED, 'Created'),
        (OPEN, 'Open'),
        (CLOSE, 'Close'),
    )
    uid=models.TextField(null=True)
    supplier=models.ForeignKey(Supplier, on_delete=models.CASCADE)
    invoice_number=models.CharField(max_length=255, blank=True, null=True)
    attachment=models.FileField(upload_to='bill_attachment/', null=True)
    bill_date=models.DateField(null=True)
    due_date=models.DateField(null=True)
    discount_type =  models.CharField(max_length=50,null=True)
    subtotal = models.DecimalField(max_digits=10, decimal_places=2,null=True)
    discount = models.DecimalField(max_digits=10, decimal_places=2,null=True)
    discount_percent=models.IntegerField(default=0,null=True, blank=True)
    gst = models.DecimalField(max_digits=10, decimal_places=2,null=True)
    gst_percent=models.IntegerField(default=0,null=True, blank=True)
    total = models.DecimalField(max_digits=10, decimal_places=2,null=True)
    payable_amount= models.DecimalField(max_digits=10, decimal_places=2,null=True)
    is_active = models.BooleanField(default=True,null=True)
    status =models.PositiveSmallIntegerField(choices=STATUS_CHOICES,default=0,null=True)
    total_paid_amount=models.DecimalField(max_digits=10, decimal_places=2,null=True)
    pdf_file = models.FileField(upload_to='bill_pdfs/', blank=True, null=True)
    mailstatus = models.PositiveSmallIntegerField(choices=STATUS_CHOICES,default=0)
    
    soft_deleted = models.BooleanField(default=False)
    
    def total_amount(self):
        return "${:,.2f}".format(self.total)
    
    def paid_amounts(self):
        return "${:,.2f}".format(self.total_paid_amount)
    
    def due_amount(self):
        return "${:,.2f}".format(self.total - self.total_paid_amount)
    
    def __str__(self):
        return f"Bill {self.invoice_number} for {self.supplier}"
    
    @property           
    def get_bill_payment_records(self):
        # Assuming an invoice can have multiple invoice items
        # and we're getting the supplier from the first invoice item

        bill_payment_records= self.supplier_bill_payment.filter(supplier_bill__id=self.id).order_by('-pay_date')
        if bill_payment_records:
            # dd(invoice_payment_records)
            return bill_payment_records

        else:
            return None
            
            
            
    @property           
    def get_history(self):
        
        # bill_history1=ActivityLog.objects.filter(new_data__0__fields__supplier_bill= self.id )
        # bill_history=list(bill_history1) +  list(ActivityLog.objects.filter(model_name = 'Supplier_Bill', instance_id = self.id))
        
        bill_history=ActivityLog.objects.filter(Q(model_name = 'Supplier_Bill', instance_id = self.id)| Q(new_data__0__fields__supplier_bill = self.id )).order_by('-timestamp')
        
          
        if bill_history:
            return bill_history
        else:
            return None   
    @property
    def custom_invoice(self):
        return Supplier_Custom_Invoice.objects.filter(
            bill_no=self.id
        ).first()
    
    def get_landed_cost_map(self):
    
        invoice = Supplier_Custom_Invoice.objects.filter(
            bill_no=self.id
        ).first()
    
        if not invoice:
            return {}
    
        items = list(self.bill_items.all())  # convert once
        if not items:
            return {}
    
        total_value = sum(item.subtotal for item in items)
        product_count = len(items)
        invoice_amount = Decimal(invoice.amount)
    
        cost_map = {}
    
        for item in items:
            # split invoice across products
            product_share = invoice_amount / Decimal(product_count)
            # then split by quantity
            per_unit_cost = product_share / Decimal(item.qty)
            product_price = Decimal(item.cost_per_unit)

            # final per unit cost
            final_unit_cost = product_price + per_unit_cost
            
            cost_map[item.product_id] = round(final_unit_cost, 2)
        # cost_map = {}
        # for item in items:
        #     if len(items) > 1:
        #         # multiple products → proportional
        #         ratio = Decimal(item.subtotal) / Decimal(total_value)
        #         product_cost = Decimal(invoice.amount) * ratio
        #         per_unit_cost = product_cost / Decimal(item.qty)
        #     else:
        #         # single product → by quantity
        #         per_unit_cost = Decimal(invoice.amount) / Decimal(item.qty)
    
        #     cost_map[item.product_id] = round(per_unit_cost, 2)
    
        return cost_map
            
class Supplier_Bill_Item(BaseModel):
    supplier_bill = models.ForeignKey(Supplier_Bill, on_delete=models.CASCADE, related_name='bill_items' ,null=True)
    product = models.ForeignKey('product.Product', on_delete=models.SET_NULL, null=True)
    cost_per_unit = models.DecimalField(max_digits=10, decimal_places=2)
    qty = models.IntegerField()
    subtotal = models.DecimalField(max_digits=10, decimal_places=2)
    soft_deleted = models.BooleanField(default=False)

   
    def __str__(self):
        return f"Bill Item {self.id}"

class Supplier_Payment(BaseModel):
    uid=models.TextField(null=True)
    supplier_bill = models.ForeignKey(Supplier_Bill, on_delete=models.CASCADE, related_name='supplier_bill_payment')
    description=models.CharField( max_length=255,null=True)
    bill_number=models.CharField( max_length=50)
    pay_date=models.DateField(auto_now_add=True)
    payable_amount= models.DecimalField(max_digits=10, decimal_places=2)
    paid_amount=models.DecimalField(max_digits=10, decimal_places=2,null=True)
    
    due_amount= models.DecimalField(max_digits=10, decimal_places=2,null=True)
    total_amount= models.DecimalField(max_digits=10, decimal_places=2,null=True)
    total_paid_amount=models.DecimalField(max_digits=10, decimal_places=2,null=True)
   
    soft_deleted = models.BooleanField(default=False)
    def __str__(self):
       return f"Amount {self.supplier_bill.total} for Bill {self.supplier_bill}"

    def total_amounts(self):
        return "${:,.2f}".format(self.total_amount)
    
    def paid_amounts(self):
        return "${:,.2f}".format(self.total_paid_amount)
    
    def due_amounts(self):
        return "${:,.2f}".format(self.total_amount - self.total_paid_amount)
    


    def save(self, *args, **kwargs):
        from product.models import SerialNumber
     
        super().save(*args, **kwargs)

        # Check if the paid amount is 20% or more

class Supplier_Custom_Invoice(BaseModel):
    bill_no = models.IntegerField(null=True, blank=True)
    date = models.DateField(null=True, blank=True)
    invoice_number = models.CharField(max_length=255, null=True, blank=True)
    attachment=models.FileField(upload_to='custom_invoice/', null=True)
    amount = models.DecimalField(max_digits=10, decimal_places=2,null=True)
    class Meta:
        db_table = 'supplier_custom_invoice'


@receiver(pre_save, sender=Supplier_Bill)
def update_due_amount(sender, instance, **kwargs):
    from product.models import SerialNumber
    from quotation.models import Order_Item
    if instance.pk is not None:
        # Convert instance.total to Decimal
        instance.total = Decimal(instance.total)

        # Calculate due amount by summing all related payments' paid amounts
        total_paid_amount = instance.supplier_bill_payment.aggregate(Sum('paid_amount'))['paid_amount__sum'] or 0
        due_amount = instance.total - total_paid_amount

        payment = Supplier_Payment.objects.filter(supplier_bill=instance).last()

        # if payment:
        #     payment.due_amount = due_amount
        #     payment.total_amount = instance.total
        #     payment.total_paid_amount = total_paid_amount
        #     payment.save()

        #     if payment.paid_amount and (float(payment.total_paid_amount) / float(payment.total_amount)) >= 0.2:
        for item in instance.bill_items.all():
            existing_serial_numbers = SerialNumber.objects.filter(
                product=item.product,
                supplier=instance.supplier,
                bill=instance,
                is_active=True
            )

            if not existing_serial_numbers.exists():
                bill_identifier = 1
                for _ in range(item.qty):
                    serial_number_obj = SerialNumber.objects.create(
                        serial_number=" ",
                        product=item.product,
                        supplier=instance.supplier,
                        bill=instance,
                        bill_identifier=bill_identifier,
                        is_active=True
                    )
                    bill_identifier += 1
                    
                    # Assign serial number to corresponding Order_Item
                    # order_item = Order_Item.objects.filter(product=item.product, serial_number__isnull=True).first()
                    # if order_item:
                    #     order_item.serial_number = serial_number_obj
                    #     order_item.supplier=serial_number_obj.bill.supplier
                    #     order_item.bill=serial_number_obj.bill

                    #     # order_item.updated_by = request.user
                    #     order_item.save()
                    #     serial_number_obj.status = SerialNumber.ALLOCATED
                    #     serial_number_obj.invoice=order_item.order.invoice
                    #     serial_number_obj.customer=order_item.order.invoice.customer
                        # serial_number.updated_by=request.user
                        # serial_number_obj.save()

            existing_quantity = existing_serial_numbers.count()
            if existing_quantity < item.qty:
                # Add new serial numbers
                bill_identifier = existing_quantity + 1
                for _ in range(item.qty - existing_quantity):
                    serial_number_obj = SerialNumber.objects.create(
                        
                        product=item.product,
                        supplier=instance.supplier,
                        bill=instance,
                        bill_identifier=bill_identifier,
                        is_active=True
                    )
                    bill_identifier += 1
                    
                    # Assign serial number to corresponding Order_Item
                    # order_item = Order_Item.objects.filter(product=item.product, serial_number__isnull=True).first()
                    # if order_item:
                    #     order_item.serial_number = serial_number_obj
                    #     order_item.supplier=serial_number_obj.bill.supplier
                    #     order_item.bill=serial_number_obj.bill
                    #     order_item.save()
                        
                    #     serial_number_obj.status = SerialNumber.ALLOCATED
                    #     serial_number_obj.invoice=order_item.order.invoice
                    #     serial_number_obj.customer=order_item.order.invoice.customer
                    #     # serial_number.updated_by=request.user
                    #     serial_number_obj.save()
                        
            elif existing_quantity > item.qty:
                # Remove excess serial numbers
                serial_numbers_to_deactivate = existing_serial_numbers.order_by('id')[:existing_quantity - item.qty]

                # Create a list of IDs to deactivate
                serial_number_ids_to_deactivate = list(serial_numbers_to_deactivate.values_list('id', flat=True))

                # Update the queryset with a filter based on the list of IDs
                SerialNumber.objects.filter(id__in=serial_number_ids_to_deactivate).delete()



        