from django.db import models
from django.conf import settings
from garments.models import Garment
from services.models import Service
from category.models import Category
import random
import string


def _generate_booking_id():
    return f"IR-{random.randint(0, 999999):06d}"


class Booking(models.Model):
    BOOKING_STATUS_CHOICES = [
        ('pending_confirmation', 'Pending Confirmation'),
        ('confirmed', 'Confirmed'),
        ('in_progress', 'In Progress'),
        ('completed', 'Completed'),
        ('cancelled', 'Cancelled'),
        ('back_to_base', 'Back to Base'),
    ]

    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='bookings')
    quick_schedule = models.BooleanField(default=False)
    booking_status = models.CharField(max_length=32, choices=BOOKING_STATUS_CHOICES, default='pending_confirmation')

    PICKUP_TYPE_CHOICES = [
        ('normal', 'Normal'),
        ('express', 'Express'),
    ]
    pickup_type = models.CharField(max_length=16, choices=PICKUP_TYPE_CHOICES, default='normal')
    # Human-friendly unique booking id (e.g. bi-012345)
    booking_id = models.CharField(max_length=10, unique=True, null=True, blank=True)

    schedule_pickup_date = models.DateField(null=True, blank=True)
    schedule_pickup_time = models.CharField(max_length=64, null=True, blank=True)

    contact_name = models.CharField(max_length=255)
    contact_mobile = models.CharField(max_length=32)
    pickup_instructions = models.TextField(null=True, blank=True)

    # Snapshot of pickup address (stored as JSON — not linked to users.Address)
    pickup_address = models.JSONField(default=dict, blank=True)

    # Geo location as an object {"latitude": ..., "longitude": ...}
    geo_location = models.JSONField(default=dict, blank=True)

    # Order details stored as JSON (mirrors the provided payload structure)
    order_details = models.JSONField(default=dict, blank=True)
    premium_details = models.JSONField(default=dict, blank=True)
    luxury_details = models.JSONField(default=dict, blank=True)

    total_quantity = models.IntegerField(default=0)
    total_final_price = models.DecimalField(max_digits=12, decimal_places=2, default=0)
    currency_code = models.CharField(max_length=8, default='INR')

    rider = models.ForeignKey('riders.Rider', on_delete=models.SET_NULL, null=True, blank=True, related_name='bookings')

    trash = models.BooleanField(default=False)
    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"Booking {self.pk} - {self.user} - {self.booking_status}"

    def save(self, *args, **kwargs):
        """Generate a unique booking_id before saving if not provided."""
        if not self.booking_id:
            # try a few times to avoid rare collisions
            for _ in range(10):
                candidate = _generate_booking_id()
                if not self.__class__.objects.filter(booking_id=candidate).exists():
                    self.booking_id = candidate
                    break
            # if still not set (extremely unlikely), set a candidate and let DB enforce uniqueness
            if not self.booking_id:
                self.booking_id = _generate_booking_id()

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


    @staticmethod
    def generate_unique_tag(category_name):
        prefix = category_name[0].upper() if category_name else 'X'
        while True:
            number = ''.join(random.choices(string.digits, k=6))
            tag = f"{prefix}-{number}"
            if not BookingItem.objects.filter(tag_number=tag).exists():
                return tag

    def generate_item_tags(self):
        """
        Iterates over order_details, premium_details, and luxury_details JSON 
        and creates BookingItem records for each quantity unit.
        """
        
        # Define the sources and their corresponding item_type
        sources = [
            (self.order_details, 'standard'),
            (self.premium_details, 'premium'),
            (self.luxury_details, 'luxury')
        ]

        # Track active garment IDs per type to identify removed rows
        active_garment_ids_by_type = {
            'standard': set(),
            'premium': set(),
            'luxury': set()
        }

        for details_dict, item_type in sources:
            if not details_dict:
                continue

            # Structure: { "Service": { "Category": [ { item... } ] } }
            for service_name, categories in details_dict.items():
                # Try to find Service object
                service_obj = Service.objects.filter(service_name=service_name).first()
                
                for category_name, items in categories.items():
                    # Try to find Category object
                    category_obj = Category.objects.filter(name=category_name).first()
                    
                    if isinstance(items, list):
                        for item in items:
                            qty = item.get('item_quantity', 0)
                            garment_id = item.get('item_id')
                            
                            if garment_id:
                                active_garment_ids_by_type[item_type].add(garment_id)

                            garment_obj = Garment.objects.filter(pk=garment_id).first()
                            
                            if not garment_obj:
                                continue

                            # Count existing items for this garment + type in this booking
                            existing_items_count = self.items.filter(
                                garment=garment_obj, 
                                item_type=item_type
                            ).count()
                            
                            if qty > existing_items_count:
                                # Create missing items
                                to_create = qty - existing_items_count
                                for _ in range(to_create):
                                    prefix = category_name[0].upper() if category_name else 'X'
                                    while True:
                                        number = ''.join(random.choices(string.digits, k=6))
                                        tag = f"{prefix}-{number}"
                                        if not BookingItem.objects.filter(tag=tag).exists():
                                            break
                                    
                                    BookingItem.objects.create(
                                        booking=self,
                                        garment=garment_obj,
                                        service=service_obj,
                                        category=category_obj,
                                        tag=tag,
                                        item_type=item_type
                                    )
                            # NOTE: We do NOT automatically delete items if qty < existing_items_count
                            # Users must manually delete specific tags.

        # Remove tags for items (garment rows) that are completely removed from the respective details JSON
        for item_type, active_ids in active_garment_ids_by_type.items():
            self.items.filter(item_type=item_type).exclude(garment_id__in=active_ids).delete()

class BookingItem(models.Model):
    ITEM_TYPE_CHOICES = [
        ('standard', 'Standard'),
        ('premium', 'Premium'),
        ('luxury', 'Luxury'),
    ]

    booking = models.ForeignKey(Booking, on_delete=models.CASCADE, related_name='items')
    garment = models.ForeignKey(Garment, on_delete=models.SET_NULL, null=True, blank=True)
    service = models.ForeignKey(Service, on_delete=models.SET_NULL, null=True, blank=True)
    category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, blank=True)
    
    item_type = models.CharField(max_length=20, choices=ITEM_TYPE_CHOICES, default='standard')
    tag = models.CharField(max_length=20, unique=True)
    color = models.CharField(max_length=50, blank=True)
    is_damaged = models.BooleanField(default=False)
    damage_description = models.TextField(blank=True)
    
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"{self.tag} - {self.garment.name if self.garment else 'Unknown'}"
