import os
from django.db import models
from django.core.exceptions import ValidationError
from decimal import Decimal
from django.utils import timezone
from django.conf import settings
from enum import Enum
from lib.choices import choices_the_options
from lib.sevices import generate_amount_in_words
from crm.models.invoice_generator import InvoiceGenerator

from crm.models import Invoice

class PaymentMethod(Enum):
    CASH = "Cash"
    CHEQUE = "Cheque"
    ONLINE = "Online"
    UPI = "UPI"
    CREDIT_CARD = "Credit Card"
    DEBIT_CARD = "Debit Card"
    BANK = 'Bank'

class Payment(models.Model):
    invoice = models.ForeignKey(Invoice, on_delete=models.CASCADE, related_name='payments')
    amount = models.DecimalField(max_digits=12, decimal_places=2)
    payment_date = models.DateTimeField(default=timezone.now)
    payment_method = models.CharField(max_length=50, choices=choices_the_options(PaymentMethod), default=PaymentMethod.CASH.value, blank=True, null=True)
    transaction_id = models.CharField(max_length=100, blank=True, null=True)
    notes = models.TextField(blank=True, null=True)
    pdf_file = models.FileField(upload_to='media/payments/', null=True, blank=True)
    created_at = models.DateTimeField(default=timezone.now)


    def __str__(self):
        return f"Payment of {self.amount} for Invoice {self.invoice.invoice_number}"

    class Meta:
        ordering = ['-created_at']

    def save(self, *args, **kwargs):
        # Validate against overpayment
        if self.invoice:
            items = self.invoice.items.all()
            invoice_total = sum((item.amount for item in items), Decimal("0"))

            # Total already paid excluding this payment if updating existing record
            if self.pk:
                try:
                    original = Payment.objects.get(pk=self.pk)
                    total_paid_existing = Decimal(str(self.invoice.get_total_paid())) - Decimal(str(original.amount))
                except Payment.DoesNotExist:
                    total_paid_existing = Decimal(str(self.invoice.get_total_paid()))
            else:
                total_paid_existing = Decimal(str(self.invoice.get_total_paid()))

            new_total_paid = total_paid_existing + Decimal(str(self.amount))
            if new_total_paid > invoice_total:
                pending_before = (invoice_total - total_paid_existing).quantize(Decimal("0.01"))
                raise ValidationError({
                    "amount": f"Payment exceeds pending amount. Pending: {pending_before}"
                })

        super().save(*args, **kwargs)
        
        # Update invoice payment status
        if self.invoice:
            items = self.invoice.items.all()
            total_amount = float(sum(item.amount for item in items))
            total_paid = float(self.invoice.get_total_paid())
            pending_amount = max(total_amount - total_paid, 0)
            is_paid = pending_amount == 0
            self.invoice.pending_amount = pending_amount
            self.invoice.is_paid = is_paid
            self.invoice.save(update_fields=["pending_amount", "is_paid"])
            
            # Update quote status if exists
            quote = getattr(self.invoice, 'quote', None)
            if quote and hasattr(quote, 'is_paid'):
                quote.is_paid = is_paid
                quote.save(update_fields=["is_paid"])

        # Generate PDF if it doesn't exist
        if not self.pdf_file:
            self.generate_pdf()

    def get_payment_data_for_generator(self):
        """Convert Django model data to format expected by InvoiceGenerator"""
        # Calculate invoice totals
        invoice_items = self.invoice.items.all()
        invoice_total = float(sum(item.amount for item in invoice_items))
        total_paid = float(self.invoice.get_total_paid())
        pending_amount = max(invoice_total - total_paid, 0)

        # Generate amount in words for this payment
        amount_in_words = generate_amount_in_words(float(self.amount))

        # Get lead data
        lead_data = {}
        if self.invoice.lead:
            lead_data = {
                'company': self.invoice.lead.company or '',
                'first_name': self.invoice.lead.first_name or '',
                'last_name': self.invoice.lead.last_name or '',
                'address': self.invoice.lead.address or '',
                'city': self.invoice.lead.city or '',
                'state': self.invoice.lead.state or '',
                'zip_code': self.invoice.lead.zip_code or '',
                'country': self.invoice.lead.country or '',
            }

        return {
            'payment_date': self.payment_date.strftime('%d %b %Y'),
            'reference_number': f"PAY-{self.id}",
            'payment_mode': self.payment_method or 'Cash',
            'amount_received': float(self.amount),
            'amount_in_words': amount_in_words,
            'received_from': f"{lead_data.get('company', '')} {lead_data.get('first_name', '')} {lead_data.get('last_name', '')}".strip(),
            'payments': [{
                'invoice_number': self.invoice.invoice_number,
                'invoice_date': self.invoice.invoice_date.strftime('%d %b %Y'),
                'invoice_amount': invoice_total,
                'payment_amount': float(self.amount)
            }],
            'total_invoice_amount': invoice_total,
            'total_paid': total_paid,
            'pending_amount': pending_amount,
            'transaction_id': self.transaction_id,
            'notes': self.notes,
            'currency': self.invoice.currency
        }


    def generate_pdf(self):
        """Generate PDF using InvoiceGenerator templates"""
        # Get payment data in the format expected by InvoiceGenerator
        payment_data = self.get_payment_data_for_generator()

        # Create payments directory
        payments_dir = os.path.join(settings.MEDIA_ROOT, "payments")
        os.makedirs(payments_dir, exist_ok=True)

        try:
            # Initialize the InvoiceGenerator with office info if available
            office_info = None
            invoice_office = getattr(self.invoice, 'office', None)
            if invoice_office and hasattr(invoice_office, 'get_company_info'):
                office_info = invoice_office.get_company_info()
            generator = InvoiceGenerator(office_info=office_info)

            # Generate HTML content for payment receipt
            html_content = generator.generate_payment_receipt_html(payment_data)

            # Generate HTML file
            html_path = os.path.join(payments_dir, f"payment_{self.id}.html")
            generator.save_html_file(html_content, html_path)

            # Optionally, generate PDF (if you want PDF output, use the appropriate method)
            # pdf_path = os.path.join(payments_dir, f"payment_{self.id}.pdf")
            # generator.save_html_and_pdf_pdfkit(html_content, html_path, pdf_path)

            # Update the model's pdf_file field to point to the HTML file
            self.pdf_file.name = f"payments/payment_{self.id}.html"
            self.save(update_fields=["pdf_file"])

        except Exception as e:
            print(f"Error generating payment files: {e}")
            raise




def get_total_paid(self):
    return float(sum(p.amount for p in self.payments.all()))

def get_pending_amount(self):
    total = float(sum(item.amount for item in self.items.all()))
    return max(total - self.get_total_paid(), 0)

def get_payments_info(self):
    total_invoice_amount = float(sum(item.amount for item in self.items.all()))
    return {
        "total_invoice_amount": total_invoice_amount,
        "total_paid": self.get_total_paid(),
        "pending_amount": self.get_pending_amount(),
        "payments": list(self.payments.all().values('id', 'amount', 'payment_date', 'payment_method', 'transaction_id', 'pdf_file')),
    }

def get_latest_payment(self):
    return self.payments.order_by('-payment_date').first()

Invoice.add_to_class("get_total_paid", get_total_paid)
Invoice.add_to_class("get_pending_amount", get_pending_amount)
Invoice.add_to_class("get_payments_info", get_payments_info)
Invoice.add_to_class("get_latest_payment", get_latest_payment)
