import uuid
import os
from datetime import date
from django.conf import settings
from django.db import models
from crm.models import Lead
from lib.sevices import generate_amount_in_words
from utils.currency import CurrencyType
from lib.choices import choices_the_options
from crm.models.invoice_generator import InvoiceGenerator




class Quote(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    quote_number = models.CharField(max_length=20, unique=True)
    quote_date = models.DateField()
    lead = models.ForeignKey(Lead, on_delete=models.CASCADE, related_name="quotes")
    office = models.ForeignKey('crm.Office', on_delete=models.SET_NULL, null=True, blank=True, 
                              help_text="Office location for this quote")
    currency = models.CharField(
        max_length=3,
        choices=choices_the_options(CurrencyType),
        default=CurrencyType.INR.value,
    )
    created_at = models.DateTimeField(auto_now_add=True)
    pdf_file = models.FileField(upload_to="media/quotes/", null=True, blank=True)
    mark_as_sent = models.BooleanField(default=False)
    remarks = models.TextField(blank=True, null=True)
    terms_and_conditions = models.TextField(blank=True, null=True)
    is_invoice_converted = models.BooleanField(default=False)
    total_amount = models.DecimalField(max_digits=10, decimal_places=2, default=0.0)

    def __str__(self):
        return self.quote_number

    class Meta:
        ordering = ["-created_at"]

    @classmethod
    def generate_quote_number(cls):
        last_quote = cls.objects.order_by("-created_at").first()
        if last_quote:
            try:
                last_number = int(last_quote.quote_number.replace("QT-", ""))
                new_number = last_number + 1
            except (ValueError, AttributeError):
                new_number = cls.objects.count() + 1
        else:
            new_number = 1
        return f"QT-{new_number:06d}"

    def save(self, *args, **kwargs):
        if not self.quote_number:
            self.quote_number = self.generate_quote_number()

        # Set default office if none specified
        if not self.office:
            from crm.models.office import Office
            self.office = Office.get_default_office()

        # Calculate total amount from items
        items_total = sum(float(item.amount) for item in self.items.all()) if self.pk else 0.0
        self.total_amount = round(items_total, 2)
        super().save(*args, **kwargs)

        # Generate PDF if not exists and not updating specific fields only
        if not self.pdf_file and not kwargs.get('update_fields'):
            self.generate_pdf()

    def get_quote_data_for_generator(self):
        """
        Convert Django model data to format expected by InvoiceGenerator.
        This is analogous to get_invoice_data_for_generator in Invoice.
        """
        items = self.items.all()

        # Calculate totals
        subtotal = sum(float(item.amount) for item in items)
        total = subtotal

        # Convert items to expected format
        items_data = []
        for item in items:
            items_data.append({
                'description': self._safe_string(item.item_name),
                'notes': self._safe_string(item.description),
                'quantity': float(item.qty),
                'rate': float(item.rate),
                'amount': float(item.amount)
            })

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

        # get Quote remarks and terms_and_conditions
        lead_data['remarks'] = self._safe_string(self.remarks)
        lead_data['terms_and_conditions'] = self._safe_string(self.terms_and_conditions)

        print(lead_data['remarks'])
        print(lead_data['terms_and_conditions'])

        # Generate amount in words
        amount_in_words = generate_amount_in_words(total)

        # Get office information
        office_info = {}
        if self.office:
            office_info = self.office.get_company_info()

        return {
            'quote_number': self._safe_string(self.quote_number),
            'quote_date': self.quote_date.strftime('%d %b %Y'),
            'valid_until': self.quote_date.strftime('%d %b %Y'),  # Using quote date as valid until
            'client_name': self._safe_string(f"{lead_data.get('company', '')}"),
            'client_address': self._safe_string(f"{lead_data.get('address', '')}, {lead_data.get('city', '')}, {lead_data.get('state', '')} {lead_data.get('zip_code', '')}, {lead_data.get('country', '')}".strip(', ')),
            'items': items_data,
            'subtotal': subtotal,
            'total': total,
            'amount_in_words': self._safe_string(amount_in_words),
            'notes': 'Thank you for your business.',
            'currency': self._safe_string(self.currency),
            'office_info': office_info,
            'remarks': self._safe_string(self.remarks),
            'terms_and_conditions': self._safe_string(self.terms_and_conditions)
        }

    def _safe_string(self, value):
        """Convert string to ASCII-safe format to prevent Unicode encoding errors"""
        if value is None:
            return ''
        if isinstance(value, str):
            # Replace any problematic Unicode characters with ASCII equivalents
            return value.encode('ascii', 'replace').decode('ascii')
        return str(value)

    def generate_pdf(self):

        # Get quote data in the format expected by InvoiceGenerator
        quote_data = self.get_quote_data_for_generator()

        # Create quotes directory
        quotes_dir = os.path.join(settings.MEDIA_ROOT, "quotes")
        os.makedirs(quotes_dir, exist_ok=True)
        pdf_path = os.path.join(quotes_dir, f"{self.quote_number}.pdf")

        try:
            # Initialize the InvoiceGenerator with office info
            office_info = quote_data.get('office_info', {})
            generator = InvoiceGenerator(office_info=office_info)

            # Generate HTML content using the generator's quote HTML function
            html_content = generator.generate_quote_html(quote_data)

            # Ensure HTML content is properly encoded as UTF-8
            if isinstance(html_content, str):
                html_content = html_content.encode('utf-8').decode('utf-8')

            print(html_content, "HTML")

            # Generate HTML file and PDF via image conversion
            html_path = os.path.join(quotes_dir, f"{self.quote_number}.html")
            generator.save_html_file(html_content, html_path)

            # Update the model's pdf_file field
            self.pdf_file.name = f"quotes/{self.quote_number}.html"
            self.save(update_fields=["pdf_file"])

        except UnicodeEncodeError as e:
            print(f"Unicode encoding error generating quote files: {e}")
            # Try to clean the data and regenerate
            self._clean_and_regenerate_quote()
        except Exception as e:
            print(f"Error generating quote files: {e}")
            raise e

    def _clean_and_regenerate_quote(self):
        """Clean Unicode characters and regenerate quote"""
        try:
            # Get clean quote data
            quote_data = self.get_quote_data_for_generator()
            
            # Clean any problematic Unicode characters from string fields
            for key, value in quote_data.items():
                if isinstance(value, str):
                    # Replace any problematic Unicode characters with ASCII equivalents
                    quote_data[key] = value.encode('ascii', 'replace').decode('ascii')
                elif isinstance(value, list):
                    for item in value:
                        if isinstance(item, dict):
                            for sub_key, sub_value in item.items():
                                if isinstance(sub_value, str):
                                    item[sub_key] = sub_value.encode('ascii', 'replace').decode('ascii')
            
            # Regenerate with cleaned data
            office_info = quote_data.get('office_info', {})
            generator = InvoiceGenerator(office_info=office_info)
            html_content = generator.generate_quote_html(quote_data)
            
            quotes_dir = os.path.join(settings.MEDIA_ROOT, "quotes")
            html_path = os.path.join(quotes_dir, f"{self.quote_number}.html")
            generator.save_html_file(html_content, html_path)
            
            self.pdf_file.name = f"quotes/{self.quote_number}.html"
            self.save(update_fields=["pdf_file"])
            
        except Exception as e:
            print(f"Error in clean regeneration: {e}")
            raise e

    def convert_to_invoice(self):
        from crm.models import Invoice, InvoiceItem
        from lib.sevices import generate_invoice_number

        # Check if invoice already exists for this quote
        existing_invoice = Invoice.objects.filter(quote=self).first()
        if existing_invoice:
            # Ensure the existing invoice has items from the quote
            if existing_invoice.items.count() == 0:
                for item in self.items.all():
                    InvoiceItem.objects.create(
                        invoice=existing_invoice,
                        item_name=item.item_name,
                        description=item.description,
                        qty=item.qty,
                        rate=item.rate,
                        amount=item.amount,
                    )
                existing_invoice.generate_pdf()
            return existing_invoice

        # Create new invoice
        invoice = Invoice.objects.create(
            quote=self,
            invoice_number=generate_invoice_number(),
            invoice_date=date.today(),
            lead=self.lead,
            office=self.office,  # Copy office from quote
            currency=self.currency,
        )

        # Copy all quote items to the new invoice
        for item in self.items.all():
            InvoiceItem.objects.create(
                invoice=invoice,
                item_name=item.item_name,
                description=item.description,
                qty=item.qty,
                rate=item.rate,
                amount=item.amount,
            )

        # Generate PDF for the new invoice
        invoice.generate_pdf()
        return invoice

    def get_lead_data(self):
        lead = self.lead
        if not lead:
            return {}
        return {
            "lead_id": str(lead.id),
            "company": lead.company,
            "first_name": lead.first_name,
            "last_name": lead.last_name,
            "mobile": lead.mobile,
            "address": lead.address,
            "city": lead.city,
            "state": lead.state,
            "zip_code": lead.zip_code,
            "country": lead.country,
        }

class ItemTemplate(models.Model):
    """Predefined items that can be selected when creating quotes"""
    name = models.CharField(max_length=255, unique=True)
    description = models.TextField(blank=True)
    rate = models.DecimalField(max_digits=10, decimal_places=2)
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['name']

    def __str__(self):
        return self.name


class QuoteItem(models.Model):
    quote = models.ForeignKey(Quote, on_delete=models.CASCADE, related_name="items")
    item_name = models.CharField(max_length=255)
    description = models.TextField(blank=True)
    qty = models.IntegerField()
    rate = models.DecimalField(max_digits=10, decimal_places=2)
    amount = models.DecimalField(max_digits=10, decimal_places=2)

    def __str__(self):
        return self.item_name
