from django.apps import apps
import random
import string
from datetime import timedelta
from uuid import uuid4
from imagekit.models import ProcessedImageField
from imagekit.processors import ResizeToFill
from django.db import models
from django.core.exceptions import ValidationError
from django.utils.text import slugify
from django.utils.timezone import now
from suppliers.models import Supplier  # Import Supplier from the suppliers app



# Custom manager for handling soft-deleted records
class ActiveManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(deleted_at__isnull=True)


# Category model
class Category(models.Model):
    name = models.CharField(max_length=255, unique=True)
    slug = models.SlugField(unique=True, max_length=255, blank=True, null=True)
    description = models.TextField(blank=True, null=True)
    thumbnail = models.ImageField(upload_to='category_images/', blank=True, null=True)
    parent = models.ForeignKey('self', null=True, blank=True, related_name='subcategories', on_delete=models.CASCADE)
    deleted_at = models.DateTimeField(null=True, blank=True)

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = slugify(self.name)
        super().save(*args, **kwargs)

    def __str__(self):
        return self.name


# Product model
class Product(models.Model):
    name = models.CharField(max_length=255)
    variant = models.CharField(max_length=255, blank=True, null=True)  # For variants like size or color
    slug = models.SlugField(unique=True, max_length=255)  # SEO-friendly unique URL
    barcode = models.CharField(max_length=255, blank=True, null=True, unique=True, db_index=True)
    buying_price = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
    selling_price = models.DecimalField(max_digits=10, decimal_places=2)
    wholesale_price = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
    stock_quantity = models.PositiveIntegerField(default=0)
    low_stock_threshold = models.PositiveIntegerField(default=0)
    packaging_type = models.CharField(max_length=50, blank=True, null=True)  # E.g., "Box", "Bottle"
    expiry_date = models.DateField(blank=True, null=True)
    created_at = models.DateTimeField(auto_now_add=True)
    category = models.ForeignKey('Category', null=True, blank=True, on_delete=models.SET_NULL, db_index=True, related_name='products')
    description = models.TextField(blank=True, null=True)
    image = models.ImageField(upload_to='product_images/', blank=True, null=True)
    is_featured = models.BooleanField(default=False, db_index=True)
    discount_price = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
    is_active = models.BooleanField(default=True, db_index=True)  # Products are active by default
    deleted_at = models.DateTimeField(null=True, blank=True)

    # SEO metadata
    seo_title = models.CharField(max_length=70, blank=True, null=True)
    seo_description = models.TextField(blank=True, null=True)

    # Managers
    objects = ActiveManager()  # Filters out soft-deleted records
    all_objects = models.Manager()  # Includes all records, including soft-deleted ones

    def __str__(self):
        return f"{self.name} ({self.variant})" if self.variant else self.name

    def clean(self):
        """
        Validation logic for product fields.
        """
        if self.buying_price is not None and self.selling_price <= self.buying_price:
            raise ValidationError("Selling price must be greater than buying price.")
        if self.discount_price and self.discount_price >= self.selling_price:
            raise ValidationError("Discount price must be less than the selling price.")
        if self.stock_quantity < 0:
            raise ValidationError("Stock quantity cannot be negative.")
        if self.low_stock_threshold < 0:
            raise ValidationError("Low stock threshold cannot be negative.")

    def save(self, *args, **kwargs):
        """
        Override save method to handle slug and barcode generation.
        """
        if not self.slug:
            base_slug = slugify(f"{self.name}-{self.variant}" if self.variant else self.name)
            unique_slug = base_slug
            counter = 1

            # Ensure unique slug
            while Product.objects.filter(slug=unique_slug).exists():
                unique_slug = f"{base_slug}-{counter}"
                counter += 1

            self.slug = unique_slug

        if not self.barcode:
            self.barcode = self.generate_barcode()

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

    @staticmethod
    def generate_barcode():
        """
        Generates a random 12-digit barcode.
        """
        return ''.join(random.choices(string.digits, k=12))

    def delete(self, *args, **kwargs):
        """
        Soft-delete the product by setting `deleted_at` to the current timestamp.
        """
        self.deleted_at = now()
        self.save()

    def restore(self):
        """
        Restore a soft-deleted product by clearing the `deleted_at` field.
        """
        self.deleted_at = None
        self.save()

    def is_low_stock(self):
        """
        Check if the product stock is below the low stock threshold.
        """
        return self.stock_quantity <= self.low_stock_threshold

    def is_nearing_expiry(self):
        """
        Check if the product is nearing expiry within 30 days.
        """
        if self.expiry_date:
            return now().date() >= (self.expiry_date - timedelta(days=30))
        return False

    def has_expired(self):
        """
        Check if the product has expired.
        """
        if self.expiry_date:
            return now().date() > self.expiry_date
        return False

    class Meta:
        unique_together = ['name', 'variant']  # Ensure unique product-variant combinations


# Product image model
class ProductImage(models.Model):
    product = models.ForeignKey(Product, related_name='images', on_delete=models.CASCADE)
    image = ProcessedImageField(
        upload_to='product_images/',
        processors=[ResizeToFill(800, 800)],
        format='JPEG',
        options={'quality': 85},
    )
    thumbnail = ProcessedImageField(
        upload_to='product_thumbnails/',
        processors=[ResizeToFill(200, 200)],
        format='JPEG',
        options={'quality': 85},
        blank=True,
        null=True,
    )
    is_primary = models.BooleanField(default=False)
    alt_text = models.CharField(max_length=255, blank=True, null=True)
    order = models.PositiveIntegerField(default=0)
    caption = models.CharField(max_length=255, blank=True, null=True)

    def clean(self):
        if self.is_primary and self.product.images.filter(is_primary=True).exists():
            raise ValidationError("Only one primary image is allowed per product.")

    def __str__(self):
        return f"Image for {self.product.name}"

    class Meta:
        ordering = ['order']  # Sort images by order field


# Batch model
class Batch(models.Model):
    STATUS_CHOICES = [
        ('active', 'Active'),
        ('expired', 'Expired'),
        ('sold_out', 'Sold Out'),
    ]

    product = models.ForeignKey('Product', related_name='batches', on_delete=models.CASCADE)
    batch_code = models.CharField(max_length=255, blank=True, null=True)
    quantity = models.PositiveIntegerField()
    buying_price = models.DecimalField(max_digits=10, decimal_places=2)
    selling_price = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
    expiry_date = models.DateField(blank=True, null=True)
    created_at = models.DateTimeField(auto_now_add=True)
    deleted_at = models.DateTimeField(null=True, blank=True)
    received_date = models.DateField(default=now)
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='active')
    notes = models.TextField(blank=True, null=True)
    supplier = models.ForeignKey(Supplier, on_delete=models.SET_NULL, null=True, blank=True)
    objects = ActiveManager()  # Exclude soft-deleted
    all_objects = models.Manager()  # Include all records

    def clean(self):
        """
        Validation logic to ensure data integrity.
        """
        if self.expiry_date and self.expiry_date < now().date():
            raise ValidationError("Expiry date cannot be in the past.")
        if self.quantity < 0:
            raise ValidationError("Batch quantity cannot be negative.")
        if self.selling_price and self.selling_price <= self.buying_price:
            raise ValidationError("Selling price must be greater than buying price.")

    def save(self, *args, **kwargs):
        """
        Override save to generate batch code if not provided.
        """
        if not self.batch_code:
            self.batch_code = self.generate_batch_code()

        # Dynamically load the Supplier model during save
        Supplier = apps.get_model('suppliers', 'Supplier')  # Dynamically load Supplier model

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

    @staticmethod
    def generate_batch_code():
        """
        Generates a unique batch code.
        """
        return f"BATCH-{uuid4().hex[:6].upper()}"

    def __str__(self):
        return f"Batch {self.batch_code} for {self.product.name}"

    class Meta:
        ordering = ['received_date']  # Sort batches by received date