import barcode
from io import BytesIO
from decimal import Decimal

from PIL import Image, ImageOps
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django.db import models
from django.dispatch import receiver
from django.utils.text import slugify
from barcode.writer import ImageWriter
from django.contrib.auth import get_user_model    
from django.db.models.signals import post_save

from django.core.files.base import ContentFile
from users.models import ShippingAddress
from django_ckeditor_5.fields import CKEditor5Field

User = get_user_model()
class Category(models.Model):
    name = models.CharField(max_length=100, unique=True)
    slug = models.SlugField(unique=True, blank=True)
    description = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    class Meta:
        verbose_name_plural = "Categories"
        ordering = ['name']

    def __str__(self):
        return self.name

    def slugify(self):
        from django.utils.text import slugify
        self.slug = slugify(self.name)

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slugify()
        super().save(*args, **kwargs)


class Product(models.Model):
    category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='products')
    
    name = models.CharField(_("Name"), max_length=200)
    slug = models.SlugField(_("Slug"), unique=True, blank=True)
    
    description = CKEditor5Field(_("Description"), config_name='extends')
    price = models.DecimalField(max_digits=12, decimal_places=2, help_text=_("Price in dollars"))
    
    # Inventory
    stock = models.PositiveIntegerField(default=10)
    quantity_sold = models.PositiveIntegerField(default=0)

    # Engagement
    likes = models.PositiveIntegerField(default=0)
    view_count = models.PositiveIntegerField(default=0)
    
    # Rating System
    average_rating = models.DecimalField(max_digits=3, decimal_places=1, default=0.0)
    rating_count = models.PositiveIntegerField(default=0)

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    barcode = models.CharField(max_length=50, blank=True, null=True)
    qr_code = models.ImageField(upload_to='barcodes/', blank=True, null=True)
    free_delivery = models.BooleanField(default=False, help_text=_('Delivery is free to your door step'))
    
    allow_customer_offer = models.BooleanField(default=False, help_text=_("Let customers submit their own offer for this product"))
    min_offer_price = models.DecimalField(max_digits=12, decimal_places=2, blank=True, null=True, help_text=_('Optional minimum offer price customers must meet'))

    def generate_barcode(self):
        if not self.barcode:
            self.barcode = f"NX{self.id:06d}"
        
        code = barcode.get('code128', self.barcode, writer=ImageWriter())
        buffer = BytesIO()
        code.write(buffer)
        self.qr_code.save(f'barcode_{self.id}.png', ContentFile(buffer.getvalue()), save=False)

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = slugify(self.name)

        original_slug = self.slug
        counter = 1
        while Product.objects.filter(slug=self.slug).exclude(pk=self.pk).exists():
            self.slug = f"{original_slug}-{counter}"
            counter += 1

        super().save(*args, **kwargs)

    @property
    def wishlist_count(self):
        return self.wishlist_set.count()

    def update_rating(self):
        reviews = self.reviews.all()
        if reviews.exists():
            total = sum(review.rating for review in reviews)
            self.average_rating = Decimal(total) / reviews.count()
            self.rating_count = reviews.count()
            self.save(update_fields=['average_rating', 'rating_count'])
        else:
            self.average_rating = Decimal('0.0')
            self.rating_count = 0
            self.save(update_fields=['average_rating', 'rating_count'])

    @property
    def satisfaction_rate(self):
        """Return the customer satisfaction percentage based on 4- and 5-star reviews."""
        total_reviews = self.reviews.count()
        if total_reviews == 0:
            return 0

        satisfied_reviews = self.reviews.filter(rating__gte=4).count()
        return int(round((satisfied_reviews / total_reviews) * 100))
    
    def get_active_discount(self):
        from django.utils import timezone
        now = timezone.now()
        
        discounts = self.discounts.filter(
            is_active=True,
            start_date__lte=now,
            end_date__gte=now
        )
        category_discounts = self.category.discounts.filter(
            is_active=True,
            start_date__lte=now,
            end_date__gte=now
        )
        all_discounts = list(discounts) + list(category_discounts)
        if not all_discounts:
            return None
        return max(all_discounts, key=lambda d: d.calculate_discount_amount(self.price))
    
    def get_discounted_price(self):
        discount = self.get_active_discount()
        if discount:
            return discount.calculate_discounted_price(self.price)
        return self.price
    
    def get_discount_amount(self):
        discount = self.get_active_discount()
        if discount:
            return discount.calculate_discount_amount(self.price)
        return Decimal('0')

    def track_view(self, *, user=None, session_key=None, ip_address=None, referer=None, user_agent=None):
        ProductViewLog.objects.create(
            product=self,
            user=user,
            session_key=session_key,
            ip_address=ip_address,
            referer=referer,
            user_agent=user_agent,
        )

    @property
    def formatted_views(self):
        return f"{self.view_count:,}"

    class Meta:
        ordering = ['-created_at']
    
    def __str__(self):
        return self.name


class ProductFeature(models.Model):
    product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='features')
    name = models.CharField(max_length=100)
    value = models.CharField(max_length=255)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['name']
        unique_together = ('product', 'name')

    def __str__(self):
        return f"{self.name}: {self.value}"


class ProductViewLog(models.Model):
    product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='view_logs')
    user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='product_views')
    session_key = models.CharField(max_length=40, blank=True, null=True)
    ip_address = models.GenericIPAddressField(blank=True, null=True)
    referer = models.URLField(blank=True, null=True)
    user_agent = models.CharField(max_length=512, blank=True, null=True)
    viewed_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['-viewed_at']
        indexes = [
            models.Index(fields=['product', 'viewed_at']),
            models.Index(fields=['session_key']),
        ]

    def __str__(self):
        return f"{self.product.name} viewed at {self.viewed_at:%Y-%m-%d %H:%M:%S}"

class InventoryLog(models.Model):
    """Tracks every stock movement (in/out/adjustment)"""
    product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='inventory_logs')
    action = models.CharField(max_length=20, choices=[
        ('restock', 'Restock'),
        ('sale', 'Sale'),
        ('return', 'Return'),
        ('adjustment', 'Adjustment'),
        ('damage', 'Damage/Loss')
    ])
    quantity = models.IntegerField()
    previous_stock = models.IntegerField()
    new_stock = models.IntegerField()
    notes = CKEditor5Field(config_name='extends')
    performed_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"{self.action} - {self.product.name} ({self.quantity})"

    class Meta:
        ordering = ['-created_at']

class StockAdjustment(models.Model):
    product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='stock_adjustments')
    previous_stock = models.PositiveIntegerField()
    new_stock = models.PositiveIntegerField()
    quantity_changed = models.IntegerField()  # positive = added, negative = removed
    reason = models.TextField()
    notes = models.TextField(blank=True)
    performed_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"{self.product.name} - {self.quantity_changed} units"
    
class Review(models.Model):
    product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='reviews')
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='reviews')
    
    rating = models.PositiveSmallIntegerField(choices=[(i, f"{i} Star{'s' if i > 1 else ''}") for i in range(1, 6)])
    comment = models.TextField(blank=True)
    
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        unique_together = ('product', 'user')   # One review per user per product
        ordering = ['-created_at']

    def __str__(self):
        return f"{self.user.username} rated {self.product.name} - {self.rating}★"

    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)
        # Automatically update product's average rating
        self.product.update_rating()

  
class Wishlist(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    added_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        unique_together = ('user', 'product')

    def __str__(self):
        return f"{self.user.username} - {self.product.name}"


class OfferRequest(models.Model):
    STATUS_CHOICES = [
        ('Pending', 'Pending'),
        ('Accepted', 'Accepted'),
        ('Declined', 'Declined'),
    ]

    product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='offer_requests')
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='offer_requests')
    offered_price = models.DecimalField(max_digits=12, decimal_places=2)
    message = models.TextField(blank=True)
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='Pending')
    admin_response = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['-created_at']

    def __str__(self):
        return f"Offer #{self.id} - {self.product.name} - {self.user.username}"


class ProductImage(models.Model):
    product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='images')
    image = models.ImageField(upload_to='products/')
    alt_text = models.CharField(max_length=200, blank=True, help_text="Short description for accessibility")
    is_primary = models.BooleanField(default=False, help_text="Mark as main product image")
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['-is_primary', 'created_at']

    def __str__(self):
        return f"Image for {self.product.name}"

    def save(self, *args, **kwargs):
        # Ensure only one primary image per product
        if self.is_primary:
            ProductImage.objects.filter(product=self.product, is_primary=True).exclude(pk=self.pk).update(is_primary=False)
        super().save(*args, **kwargs)

        if self.image:
            try:
                with Image.open(self.image.path) as img:
                    img = ImageOps.exif_transpose(img)
                    max_width, max_height = 1200, 1200
                    if img.width > max_width or img.height > max_height:
                        if img.mode not in ('RGB', 'RGBA'):
                            img = img.convert('RGB')

                        img.thumbnail((max_width, max_height), Image.LANCZOS)
                        buffer = BytesIO()
                        format = 'PNG' if img.mode == 'RGBA' else 'JPEG'
                        img.save(buffer, format=format, quality=85, optimize=True)
                        self.image.save(self.image.name, ContentFile(buffer.getvalue()), save=False)
                        super().save(update_fields=['image'])
            except Exception:
                pass
        

class ProductVideo(models.Model):
    product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='videos')
    video = models.FileField(upload_to='products/videos/')
    title = models.CharField(max_length=200, blank=True, help_text="Video title or description")
    is_primary = models.BooleanField(default=False, help_text="Mark as main product video")
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['-is_primary', 'created_at']

    def __str__(self):
        return f"Video for {self.product.name}"

    def save(self, *args, **kwargs):
        # Ensure only one primary video per product
        if self.is_primary:
            ProductVideo.objects.filter(product=self.product, is_primary=True).exclude(pk=self.pk).update(is_primary=False)
        super().save(*args, **kwargs)
        
        
class Cart(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    updated_at = models.DateTimeField(auto_now=True)


class CartItem(models.Model):
    cart = models.ForeignKey(Cart, on_delete=models.CASCADE, related_name='items')
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    quantity = models.PositiveIntegerField(default=1)
    
    def subtotal(self):
        return self.product.price * self.quantity
    
    def get_discounted_price(self):
        """Get price after product-level discount"""
        discount = self.product.get_active_discount()
        if discount:
            return discount.calculate_discounted_price(self.product.price)
        return self.product.price
    
    def get_total_with_discount(self):
        """Get line total after discount"""
        return self.get_discounted_price() * self.quantity

class PaymentLog(models.Model):
    order = models.ForeignKey('Order', on_delete=models.CASCADE, related_name='payment_logs')
    flutterwave_reference = models.CharField(max_length=100, blank=True)
    amount = models.DecimalField(max_digits=10, decimal_places=2)
    status = models.CharField(max_length=20, choices=[
        ('success', 'Success'),
        ('failed', 'Failed'),
        ('pending', 'Pending')
    ])
    message = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    
    def __str__(self):
        return f"{self.order} - {self.status} - ${self.amount}"

class Order(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='orders')
    total = models.DecimalField(max_digits=10, decimal_places=2)
    phone = models.CharField(max_length=15)
    shipping_address = models.ForeignKey(ShippingAddress, on_delete=models.SET_NULL, null=True, blank=True, related_name='orders')
    status = models.CharField(max_length=20, default='Pending', choices=[
        ('Pending', 'Pending'),
        ('Paid', 'Paid'),
        ('Processing', 'Processing'),
        ('Shipped', 'Shipped'),
        ('Delivered', 'Delivered'),
        ('Cancelled', 'Cancelled')
    ])
    reference = models.CharField(max_length=200, unique=True)
    fraud_score = models.IntegerField(default=0)   # 0-100
    is_fraud = models.BooleanField(default=False)
    
    # Discount tracking
    discount = models.ForeignKey('Discount', on_delete=models.SET_NULL, null=True, blank=True, related_name='orders')
    subtotal = models.DecimalField(max_digits=10, decimal_places=2, default=0)
    discount_amount = models.DecimalField(max_digits=10, decimal_places=2, default=0)
    
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    payment_type = models.CharField(max_length=20, default='full', choices=[
        ('full', 'Full Payment'),
        ('installment', 'Installment')
    ])
    total_installments = models.PositiveIntegerField(default=1)
    installments_paid = models.PositiveIntegerField(default=0)
    next_due_date = models.DateField(null=True, blank=True)
    flutterwave_token = models.CharField(max_length=255, blank=True, null=True)  
    
    def __str__(self):
        return f"Order #{self.id} - {self.user.username}"
    
    def get_status_timeline(self):
        statuses = [
            ('Pending', 'Order Placed'),
            ('Paid', 'Payment Confirmed'),
            ('Processing', 'Processing Order'),
            ('Shipped', 'Shipped'),
            ('Delivered', 'Delivered'),
        ]
        timeline = []
        for status, label in statuses:
            timeline.append({
                'status': status,
                'label': label,
                'completed': self.status in ['Delivered', 'Shipped', 'Processing', 'Paid'] if status == 'Pending' else
                            self.status in ['Delivered', 'Shipped', 'Processing'] if status == 'Paid' else
                            self.status in ['Delivered', 'Shipped'] if status == 'Processing' else
                            self.status == 'Delivered' if status == 'Shipped' else False,
                'current': self.status == status,
                'date': self.updated_at if self.status == status else None
            })
        return timeline
    
    @staticmethod
    def generate_reference():
        import uuid
        return str(uuid.uuid4()).replace('-', '').upper()[:20]

    def save(self, *args, **kwargs):
        if not self.reference:   # Only generate if not already set
            self.reference = self.generate_reference()
        super().save(*args, **kwargs)


    def calculate_fraud_score(self):
        """Basic rule-based fraud detection"""
        score = 0
        if self.total > 500000:          # Very high value order
            score += 40
        if len(self.shipping_address.full_address) < 20:       # Suspiciously short address
            score += 20
        # Add more rules: rapid successive orders, unusual IP, etc.
        self.fraud_score = score
        self.is_fraud = score > 60
        self.save()
        
        
class OrderItem(models.Model):
    order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name='items')
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    quantity = models.PositiveIntegerField()
    price = models.DecimalField(max_digits=10, decimal_places=2)
    
    def get_subtotal(self):
        """Calculate subtotal (quantity * price)"""
        return self.quantity * self.price
    
  
class ChatMessage(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)
    session_id = models.CharField(max_length=100)  # For anonymous users too
    message = models.TextField()
    is_bot = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"{'Bot' if self.is_bot else 'User'}: {self.message[:50]}"


class BanshixContact(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    phone = models.CharField(max_length=15, blank=True, null=True, help_text="e.g. +2348012345678")
    address = models.TextField(blank=True, null=True)  # Bonus: can pre-fill from orders
    
    # Social Media Links
    facebook = models.URLField(blank=True, null=True, help_text="Facebook profile URL")
    twitter = models.URLField(blank=True, null=True, help_text="Twitter/X profile URL")
    instagram = models.URLField(blank=True, null=True, help_text="Instagram profile URL")
    linkedin = models.URLField(blank=True, null=True, help_text="LinkedIn profile URL")
    youtube = models.URLField(blank=True, null=True, help_text="YouTube profile URL")
    is_active = models.BooleanField(default=False, help_text="Mark this contact record as currently active for the user")
    emails = models.JSONField(encoder=None, decoder=None, blank=True, null=True, help_text="Email addresses")
    whatsapp = models.CharField(max_length=20, blank=True, null=True, help_text="WhatsApp number (e.g. +2348012345678)")

    def save(self, *args, **kwargs):
        if self.is_active:
            BanshixContact.objects.filter(user=self.user, is_active=True).exclude(pk=self.pk).update(is_active=False)
        super().save(*args, **kwargs)

    def __str__(self):
        return f"{self.user.username}'s Contacts"


class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    phone = models.CharField(max_length=15, blank=True, null=True, help_text="e.g. +2348012345678")
    address = CKEditor5Field(config_name='extends')  # Bonus: can pre-fill from orders
    
    # Social Media Links
    facebook = models.URLField(blank=True, null=True, help_text="Facebook profile URL")
    twitter = models.URLField(blank=True, null=True, help_text="Twitter/X profile URL")
    instagram = models.URLField(blank=True, null=True, help_text="Instagram profile URL")
    linkedin = models.URLField(blank=True, null=True, help_text="LinkedIn profile URL")
    whatsapp = models.CharField(max_length=20, blank=True, null=True, help_text="WhatsApp number (e.g. +2348012345678)")

    def __str__(self):
        return f"{self.user.username}'s Profile"

# Auto-create Profile when new user is created
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
    if created:
        Profile.objects.create(user=instance)

@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
    instance.profile.save()
    
    
class Ticket(models.Model):
    STATUS_CHOICES = [
        ('Open', 'Open'),
        ('In Progress', 'In Progress'),
        ('Resolved', 'Resolved'),
        ('Closed', 'Closed'),
    ]

    PRIORITY_CHOICES = [
        ('Low', 'Low'),
        ('Medium', 'Medium'),
        ('High', 'High'),
        ('Urgent', 'Urgent'),
    ]

    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='tickets')
    subject = models.CharField(max_length=200)
    description = CKEditor5Field(config_name='extends')
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='Open')
    priority = models.CharField(max_length=20, choices=PRIORITY_CHOICES, default='Medium')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    ai_chat_session_id = models.CharField(max_length=100, blank=True, null=True)  # Link to escalated chat

    class Meta:
        ordering = ['-created_at']
        
    def __str__(self):
        return f"Ticket #{self.id} - {self.user.username} ({self.status})"
    
    def save(self, *args, **kwargs):
        # Auto-set priority based on highest order value if it's a new escalation ticket
        if not self.pk and self.ai_chat_session_id:  # New ticket from AI escalation
            highest_order = Order.objects.filter(user=self.user).order_by('-total').first()
            if highest_order:
                if highest_order.total >= 500000:      # $500,000+
                    self.priority = 'Urgent'
                elif highest_order.total >= 200000:    # $200,000+
                    self.priority = 'High'
                elif highest_order.total >= 50000:
                    self.priority = 'Medium'
                else:
                    self.priority = 'Low'
        super().save(*args, **kwargs)

class TicketReply(models.Model):
    ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE, related_name='replies')
    user = models.ForeignKey(User, on_delete=models.CASCADE)  # Who replied (admin or staff)
    message = CKEditor5Field(config_name='extends')
    attachment = models.FileField(upload_to='ticket_attachments/', blank=True, null=True)
    is_from_admin = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"Reply on Ticket #{self.ticket.id} by {self.user.username}"

    class Meta:
        ordering = ['created_at']
        

class SiteVisit(models.Model):
    date = models.DateField(default=timezone.now, unique=True)
    daily_visits = models.PositiveIntegerField(default=0)
    monthly_visits = models.PositiveIntegerField(default=0)  # You can compute this
    yearly_visits = models.PositiveIntegerField(default=0)
    
    class Meta:
        ordering = ['-date']
        
class VariantAttribute(models.Model):
    """e.g., Color, Size, Storage, RAM"""
    name = models.CharField(max_length=50, unique=True)  # Color, Size, etc.
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name


class VariantValue(models.Model):
    """e.g., Red, Blue, 128GB, Large"""
    attribute = models.ForeignKey(VariantAttribute, on_delete=models.CASCADE, related_name='values')
    value = models.CharField(max_length=100)

    class Meta:
        unique_together = ('attribute', 'value')

    def __str__(self):
        return f"{self.attribute.name}: {self.value}"


class ProductVariant(models.Model):
    product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='variants')
    sku = models.CharField(max_length=100, unique=True, blank=True)   # Stock Keeping Unit
    price = models.DecimalField(max_digits=10, decimal_places=2)
    stock = models.PositiveIntegerField(default=0)
    
    # Variants combination (e.g., Color: Red + Size: Large)
    attributes = models.ManyToManyField(VariantValue, related_name='product_variants')
    
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return f"{self.product.name} - {self.sku or 'No SKU'}"

    def save(self, *args, **kwargs):
        if not self.sku:
            # Auto-generate SKU if not provided
            self.sku = f"{self.product.slug.upper()}-{self.id or 'NEW'}"
        super().save(*args, **kwargs)


class Discount(models.Model):
    """Product and category discounts with multiple types"""
    DISCOUNT_TYPE_CHOICES = [
        ('percentage', 'Percentage (%)'),
        ('fixed', 'Fixed Amount'),
    ]
    
    name = models.CharField(max_length=200)
    description = CKEditor5Field(config_name='extends')
    code = models.CharField(max_length=50, unique=True, blank=True, help_text="Optional promotional code (e.g., SAVE20)")
    
    discount_type = models.CharField(max_length=20, choices=DISCOUNT_TYPE_CHOICES, default='percentage')
    discount_value = models.DecimalField(max_digits=10, decimal_places=2, help_text="Discount value (% or fixed amount)")
    
    # Applicability
    products = models.ManyToManyField(Product, blank=True, related_name='discounts', help_text="Leave empty to apply to all products")
    categories = models.ManyToManyField(Category, blank=True, related_name='discounts', help_text="Leave empty to apply to all categories")
    min_purchase = models.DecimalField(max_digits=10, decimal_places=2, default=0, help_text="Minimum purchase amount required")
    
    # Availability
    is_active = models.BooleanField(default=True)
    start_date = models.DateTimeField()
    end_date = models.DateTimeField()
    
    # Usage limits
    usage_limit = models.PositiveIntegerField(null=True, blank=True, help_text="Total number of times this discount can be used")
    usage_count = models.PositiveIntegerField(default=0, editable=False)
    usage_limit_per_user = models.PositiveIntegerField(null=True, blank=True, help_text="Max times one user can use this discount")
    
    # Tracking
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    created_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='created_discounts')
    
    class Meta:
        ordering = ['-created_at']
        verbose_name = 'Discount'
        verbose_name_plural = 'Discounts'
    
    def __str__(self):
        return f"{self.name} - {self.discount_value}{('%' if self.discount_type == 'percentage' else '$')}"
    
    def is_valid(self):
        """Check if discount is currently valid"""
        now = timezone.now()
        return (
            self.is_active and
            self.start_date <= now <= self.end_date and
            (self.usage_limit is None or self.usage_count < self.usage_limit)
        )
    
    def can_user_use(self, user):
        """Check if user can still use this discount"""
        if not self.is_valid():
            return False
        
        if self.usage_limit_per_user is None:
            return True
        
        user_usage = Order.objects.filter(
            user=user,
            discount=self
        ).count()
        return user_usage < self.usage_limit_per_user
    
    def is_applicable_to_product(self, product):
        """Check if discount applies to a specific product"""
        if not self.products.exists() and not self.categories.exists():
            return True  # Applies to all
        
        if self.products.filter(id=product.id).exists():
            return True
        
        if self.categories.filter(id=product.category.id).exists():
            return True
        
        return False
    
    def calculate_discount_amount(self, amount):
        """Calculate discount amount for a given price"""
        if self.discount_type == 'percentage':
            return amount * Decimal(self.discount_value) / Decimal('100')
        else:
            return self.discount_value
    
    def calculate_discounted_price(self, original_price):
        """Calculate final price after discount"""
        discount_amount = self.calculate_discount_amount(original_price)
        return max(original_price - discount_amount, Decimal('0'))
    
    def apply(self):
        """Increment usage count when discount is applied"""
        self.usage_count += 1
        self.save(update_fields=['usage_count'])
    
    def get_discounted_price_for_product(self, product):
        """Get discounted price for a specific product"""
        if not self.is_applicable_to_product(product):
            return product.price
        return self.calculate_discounted_price(product.price)
    
    def get_discount_amount_for_product(self, product):
        """Get discount amount for a specific product"""
        if not self.is_applicable_to_product(product):
            return Decimal('0')
        return self.calculate_discount_amount(product.price)


# ============================================================================
# INSTALLMENT & RECURRING PAYMENT MODELS
# ============================================================================

class RecurringPaymentToken(models.Model):
    """Securely store customer payment tokens for recurring charges"""
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='payment_tokens')
    
    # Token from payment gateway
    flutterwave_token = models.CharField(max_length=255, unique=True)
    card_last_four = models.CharField(max_length=4, help_text="Last 4 digits of card")
    card_brand = models.CharField(max_length=50, blank=True)  # e.g., Visa, Mastercard
    
    # Authorization
    authorization_url = models.URLField(blank=True, help_text="URL for customer to authorize recurring charges")
    is_authorized = models.BooleanField(default=False)
    authorized_at = models.DateTimeField(null=True, blank=True)
    
    # Gateway
    gateway = models.CharField(max_length=20, default='flutterwave')
    
    # Metadata
    created_at = models.DateTimeField(auto_now_add=True)
    last_used = models.DateTimeField(null=True, blank=True)
    is_active = models.BooleanField(default=True)
    
    class Meta:
        ordering = ['-created_at']
    
    def __str__(self):
        return f"{self.user.username} - {self.card_brand} ****{self.card_last_four}"


class InstallmentSchedule(models.Model):
    """Tracks the monthly payment schedule for installment orders"""
    
    PAYMENT_STATUS_CHOICES = [
        ('pending', 'Pending'),
        ('processing', 'Processing'),
        ('successful', 'Successful'),
        ('failed', 'Failed'),
        ('skipped', 'Skipped'),
        ('refunded', 'Refunded'),
    ]
    
    order = models.OneToOneField(Order, on_delete=models.CASCADE, related_name='installment_schedule')
    payment_token = models.ForeignKey(RecurringPaymentToken, on_delete=models.SET_NULL, null=True, blank=True, related_name='installment_schedules')
    
    # Schedule details
    installment_amount = models.DecimalField(max_digits=10, decimal_places=2, help_text="Amount to charge each month")
    total_installments = models.PositiveIntegerField(help_text="Total number of monthly payments")
    paid_installments = models.PositiveIntegerField(default=0, help_text="Number of payments completed")
    failed_attempts = models.PositiveIntegerField(default=0, help_text="Number of failed payment attempts")
    
    # Dates
    start_date = models.DateField(auto_now_add=True)
    next_payment_date = models.DateField(help_text="Date of next scheduled payment")
    final_payment_date = models.DateField(help_text="Date when all payments should be complete")
    
    # Status tracking
    status = models.CharField(max_length=20, choices=[
        ('active', 'Active'),
        ('completed', 'Completed'),
        ('suspended', 'Suspended'),
        ('cancelled', 'Cancelled'),
    ], default='active')
    
    auto_retry = models.BooleanField(default=True, help_text="Automatically retry failed payments")
    max_retries = models.PositiveIntegerField(default=3, help_text="Maximum retry attempts per payment")
    
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        ordering = ['-created_at']
    
    def __str__(self):
        return f"Installment Schedule - Order #{self.order.id} ({self.paid_installments}/{self.total_installments})"
    
    @property
    def remaining_installments(self):
        """Calculate remaining payments"""
        return self.total_installments - self.paid_installments
    
    @property
    def is_complete(self):
        """Check if all payments are done"""
        return self.paid_installments >= self.total_installments
    
    def mark_payment_complete(self):
        """Mark one installment as paid"""
        self.paid_installments += 1
        self.failed_attempts = 0  # Reset on successful payment
        
        if self.is_complete:
            self.status = 'completed'
        else:
            # Schedule next payment for same day next month
            from dateutil.relativedelta import relativedelta
            self.next_payment_date = self.next_payment_date + relativedelta(months=1)
        
        self.save()


class ScheduledPayment(models.Model):
    """Individual payment charge in an installment schedule"""
    
    CHARGE_STATUS_CHOICES = [
        ('pending', 'Pending'),
        ('processing', 'Processing'),
        ('successful', 'Successful'),
        ('failed', 'Failed'),
        ('refunded', 'Refunded'),
    ]
    
    installment_schedule = models.ForeignKey(InstallmentSchedule, on_delete=models.CASCADE, related_name='scheduled_payments')
    order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name='scheduled_payments')
    
    # Payment details
    amount = models.DecimalField(max_digits=10, decimal_places=2)
    installment_number = models.PositiveIntegerField(help_text="Which installment is this (1st, 2nd, etc.)")
    
    # Status tracking
    status = models.CharField(max_length=20, choices=CHARGE_STATUS_CHOICES, default='pending')
    
    # Gateway response
    transaction_reference = models.CharField(max_length=200, blank=True, help_text="Payment gateway transaction ID")
    gateway_response = models.JSONField(default=dict, blank=True, help_text="Full API response from payment gateway")
    error_message = models.TextField(blank=True, help_text="Error details if payment failed")
    
    # Retry tracking
    retry_count = models.PositiveIntegerField(default=0)
    last_retry_at = models.DateTimeField(null=True, blank=True)
    
    # Dates
    scheduled_date = models.DateField(help_text="When this payment was scheduled")
    due_date = models.DateField(help_text="When this payment is due")
    processed_at = models.DateTimeField(null=True, blank=True, help_text="When payment was actually processed")
    
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        ordering = ['due_date']
        unique_together = ['installment_schedule', 'installment_number']
    
    def __str__(self):
        return f"Payment #{self.installment_number} - Order #{self.order.id} - {self.status}"
    
    def mark_as_failed(self, error_msg: str = ""):
        """Mark payment as failed"""
        self.status = 'failed'
        self.error_message = error_msg
        self.retry_count += 1
        self.last_retry_at = timezone.now()
        self.save()
    
    def mark_as_successful(self, transaction_ref: str = "", gateway_response: dict = None):
        """Mark payment as successful"""
        self.status = 'successful'
        self.transaction_reference = transaction_ref
        self.processed_at = timezone.now()
        if gateway_response:
            self.gateway_response = gateway_response
        self.save()
        