from django.apps import apps
import random
import string
from datetime import timedelta
from uuid import uuid4
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


# 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):
    # Schema separation provides tenant isolation - no tenant field needed
    name = models.CharField(max_length=255, unique=True)  # Restored global unique - schema isolation provides tenant separation
    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 __str__(self):
        return self.name

    class Meta:
        # Schema separation provides tenant isolation - no tenant constraints needed
        ordering = ['name']


# Product model
class Product(models.Model):
    # Schema separation provides tenant isolation - no tenant field needed
    name = models.CharField(max_length=255)
    barcode = models.CharField(max_length=255, blank=True, null=True, unique=True, db_index=True)  # Restored global unique - schema isolation provides tenant separation
    buying_price = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
    selling_price = models.DecimalField(max_digits=10, decimal_places=2)
    stock_quantity = models.PositiveIntegerField(default=0)
    low_stock_threshold = models.PositiveIntegerField(default=0)
    packaging_type = models.CharField(max_length=50, blank=True, null=True)
    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_active = models.BooleanField(default=True, db_index=True)
    deleted_at = models.DateTimeField(null=True, blank=True)

    # Managers
    objects = ActiveManager()
    all_objects = models.Manager()

    def __str__(self):
        return 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.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 barcode generation.
        """
        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

    def update_pricing_from_batch(self, new_batch_buying_price, new_batch_selling_price=None):
        """
        Update product pricing based on new batch pricing.
        """
        current_buying_price = self.buying_price or 0

        if new_batch_buying_price > current_buying_price:
            self.buying_price = new_batch_buying_price
            if new_batch_selling_price:
                self.selling_price = new_batch_selling_price
            self.save()
            return True, "Price updated immediately (higher cost)"

        elif new_batch_buying_price < current_buying_price:
            if self.stock_quantity <= 0:
                self.buying_price = new_batch_buying_price
                if new_batch_selling_price:
                    self.selling_price = new_batch_selling_price
                self.save()
                return True, "Price updated immediately (no current stock)"
            else:
                return False, f"Price update deferred (current stock: {self.stock_quantity})"

        return False, "No price update needed"

    class Meta:
        ordering = ['name']
        # Schema separation provides tenant isolation - no tenant constraints needed


# Product image model (simplified)
class ProductImage(models.Model):
    # Schema separation provides tenant isolation - no tenant field needed
    product = models.ForeignKey(Product, related_name='images', on_delete=models.CASCADE)
    image = models.ImageField(upload_to='product_images/')
    is_primary = models.BooleanField(default=False)
    alt_text = 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}"


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

    # Schema separation provides tenant isolation - no tenant field needed
    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()
    all_objects = models.Manager()

    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()

        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']