from decimal import Decimal
from django.utils import timezone
from django.conf import settings
from django.db import models
from django.utils.text import slugify

from io import BytesIO
from PIL import Image

from django.core.exceptions import ValidationError
from django.core.files.base import ContentFile
from django.core.validators import FileExtensionValidator
from common.choices import SRI_LANKA_CITY_CHOICES, SRI_LANKA_DISTRICT_CHOICES

# ============================================================
# Helper: Unique Slug Generator
# ============================================================

def generate_unique_slug(instance, value, slug_field_name='slug'):
    """
    Creates a unique slug for venue/package public URLs.
    """
    base_slug = slugify(value)[:220] or "item"
    slug = base_slug
    counter = 2

    model_class = instance.__class__
    queryset = model_class.objects.all()

    if instance.pk:
        queryset = queryset.exclude(pk=instance.pk)

    while queryset.filter(**{slug_field_name: slug}).exists():
        slug = f"{base_slug}-{counter}"
        counter += 1

    return slug


# ============================================================
# Shared Choices
# ============================================================

class ApprovalStatus(models.TextChoices):
    PENDING = 'pending', 'Pending'
    APPROVED = 'approved', 'Approved'
    REJECTED = 'rejected', 'Rejected'
    SUSPENDED = 'suspended', 'Suspended'


class PublicListingStatus(models.TextChoices):
    DRAFT = 'draft', 'Draft'
    PENDING_REVIEW = 'pending_review', 'Pending Review'
    APPROVED = 'approved', 'Approved'
    REJECTED = 'rejected', 'Rejected'
    HIDDEN = 'hidden', 'Hidden'


class VenueCategory(models.TextChoices):
    RESTAURANT = 'restaurant', 'Restaurant'
    HOTEL = 'hotel', 'Hotel'
    MOTEL_BUDGET_HOTEL = 'motel_budget_hotel', 'Motel / Budget Hotel'
    RESORT = 'resort', 'Resort'
    VILLA_PRIVATE_VILLA = 'villa_private_villa', 'Villa / Private Villa'
    BANQUET_PARTY_HALL = 'banquet_party_hall', 'Banquet Hall / Party Hall'
    CABANA_BEACH_CABANA = 'cabana_beach_cabana', 'Cabana / Beach Cabana'
    BAR_LOUNGE_PUB = 'bar_lounge_pub', 'Bar / Lounge / Pub'
    ROOFTOP_SKY_VENUE = 'rooftop_sky_venue', 'Rooftop / Sky Venue'
    GARDEN_OUTDOOR_VENUE = 'garden_outdoor_venue', 'Garden / Outdoor Venue'
    BEACH_BEACHFRONT = 'beach_beachfront', 'Beach / Beachfront'
    LAKESIDE_RIVERFRONT = 'lakeside_riverfront', 'Lakeside / Riverfront'
    SPA_WELLNESS_CENTER = 'spa_wellness_center', 'Spa / Wellness Center'
    FARMHOUSE_FARM_STAY = 'farmhouse_farm_stay', 'Farmhouse / Farm Stay'
    EVENT_HALL_EVENT_SPACE = 'event_hall_event_space', 'Event Hall / Event Space'
    BOAT_RIVER_CRUISE = 'boat_river_cruise', 'Boat / River Cruise'
    TEMPLE_HERITAGE_VENUE = 'temple_heritage_venue', 'Temple / Heritage Venue'
    BOTANICAL_GARDEN_PARK = 'botanical_garden_park', 'Botanical Garden / Park'
    CASTLE_MANOR = 'castle_manor', 'Castle / Manor'
    HONEYMOON_RESORT = 'honeymoon_resort', 'Honeymoon Resort'
    OTHER = 'other', 'Other'


class EventType(models.TextChoices):
    ROMANTIC_DATE = 'romantic_date', 'Romantic Date'
    PROPOSAL_EVENT = 'proposal_event', 'Proposal Event'
    ANNIVERSARY = 'anniversary', 'Anniversary Celebration'
    BIRTHDAY = 'birthday', 'Birthday Celebration'
    HONEYMOON = 'honeymoon', 'Honeymoon Experience'
    PRIVATE_DINNER = 'private_dinner', 'Private Dinner'
    ENGAGEMENT = 'engagement', 'Engagement Event'
    SMALL_WEDDING = 'small_wedding', 'Small Wedding'
    CUSTOM_CELEBRATION = 'custom_celebration', 'Custom Celebration'


class AddOnPricingType(models.TextChoices):
    FIXED = 'fixed', 'Fixed Price'
    PER_PERSON = 'per_person', 'Per Person'
    STARTING_FROM = 'starting_from', 'Starting From'


class BookingRequestStatus(models.TextChoices):
    SUBMITTED = 'submitted', 'Submitted'
    UNDER_REVIEW = 'under_review', 'Under Review'
    WAITING_HOST_CONFIRMATION = 'waiting_host_confirmation', 'Waiting for Host Confirmation'
    QUOTATION_READY = 'quotation_ready', 'Quotation Ready'
    GUEST_CONFIRMED = 'guest_confirmed', 'Guest Confirmed'
    PAYMENT_DUE = 'payment_due', 'Payment Due'
    PAID = 'paid', 'Paid'
    ARRANGING = 'arranging', 'Arranging'
    COMPLETED = 'completed', 'Completed'
    CANCELLED = 'cancelled', 'Cancelled'
    REJECTED = 'rejected', 'Rejected'


class QuotationStatus(models.TextChoices):
    DRAFT = 'draft', 'Draft'
    SENT_TO_GUEST = 'sent_to_guest', 'Sent to Guest'
    ACCEPTED = 'accepted', 'Accepted'
    EXPIRED = 'expired', 'Expired'
    CANCELLED = 'cancelled', 'Cancelled'

class HostingClientType(models.TextChoices):
    FOREIGNER = 'foreigner', 'Foreigner / Guest'
    HOSTING_CLIENT = 'hosting_client', 'Hosting Client'
    DATING_PARTNER = 'dating_partner', 'Dating Partner'
    RENTAL_OWNER = 'rental_owner', 'Rental Owner'
    SERVICE_PARTNER = 'service_partner', 'Service Partner'
    PROPERTY_CARE_OWNER = 'property_care_owner', 'Property Care Owner'
    AFFILIATE = 'affiliate', 'Affiliate Partner'
    OTHER = 'other', 'Other'


class HostBookingPaymentStatus(models.TextChoices):
    UNPAID = 'unpaid', 'Unpaid'
    DUE = 'due', 'Due'
    PAID = 'paid', 'Paid'
    CANCELLED = 'cancelled', 'Cancelled'
    REFUNDED = 'refunded', 'Refunded'


# ============================================================
# Profile Image Validation Helpers
# ============================================================

def validate_profile_image_size(image):
    """
    Restrict profile image upload size.
    Max: 3 MB
    """
    max_size_mb = 3
    if image.size > max_size_mb * 1024 * 1024:
        raise ValidationError(
            f"Profile image file size must be under {max_size_mb} MB."
        )


def validate_profile_image_content(image):
    """
    Validate that the uploaded file is a real, readable image.
    """
    try:
        img = Image.open(image)
        img.verify()
    except Exception:
        raise ValidationError("Upload a valid image file.")

def compress_profile_image(uploaded_image, max_size=(700, 700), quality=85):
    """
    Resize and compress uploaded profile images while preserving aspect ratio.

    - Max dimensions: 700 x 700
    - JPEG/WebP quality: 85
    - PNG kept as PNG with optimization
    """
    image = Image.open(uploaded_image)

    # Normalize orientation/data if needed
    image = image.copy()

    # Resize proportionally
    image.thumbnail(max_size)

    output = BytesIO()

    image_format = image.format or 'JPEG'
    image_format = image_format.upper()

    if image_format in ['JPG', 'JPEG']:
        if image.mode in ('RGBA', 'LA', 'P'):
            image = image.convert('RGB')

        image.save(
            output,
            format='JPEG',
            quality=quality,
            optimize=True
        )
        extension = 'jpg'

    elif image_format == 'PNG':
        image.save(
            output,
            format='PNG',
            optimize=True
        )
        extension = 'png'

    elif image_format == 'WEBP':
        image.save(
            output,
            format='WEBP',
            quality=quality,
            optimize=True
        )
        extension = 'webp'

    else:
        # Fallback conversion to JPEG
        if image.mode in ('RGBA', 'LA', 'P'):
            image = image.convert('RGB')

        image.save(
            output,
            format='JPEG',
            quality=quality,
            optimize=True
        )
        extension = 'jpg'

    output.seek(0)

    return ContentFile(output.read()), extension


# ============================================================
# 1. Hosting Partner Profile
# ============================================================

class HostProfile(models.Model):
    user = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='host_profile'
    )

    business_name = models.CharField(max_length=255)
    contact_person_name = models.CharField(max_length=255)

    email = models.EmailField()
    phone_number = models.CharField(max_length=50)
    whatsapp_number = models.CharField(max_length=50, blank=True)

    profile_image = models.ImageField(
        upload_to='hosting_app/host_profiles/',
        null=True,
        blank=True,
        validators=[
            FileExtensionValidator(
                allowed_extensions=['jpg', 'jpeg', 'png', 'webp']
            ),
            validate_profile_image_size,
            validate_profile_image_content,
        ],
        help_text="Optional profile image. Allowed: JPG, JPEG, PNG, WEBP. Max size: 3 MB."
    )

    short_introduction = models.TextField(
        blank=True,
        help_text="Brief introduction about the hosting partner or business."
    )

    status = models.CharField(
        max_length=30,
        choices=ApprovalStatus.choices,
        default=ApprovalStatus.PENDING
    )

    is_featured = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)

    admin_review_note = models.TextField(blank=True)

    approved_at = models.DateTimeField(null=True, blank=True)

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['-created_at']

    def save(self, *args, **kwargs):
        """
        Compress profile image only when:
        - A profile image exists, and
        - It is newly uploaded or changed
        """
        if self.profile_image:
            should_compress = False

            if not self.pk:
                should_compress = True
            else:
                try:
                    old_instance = HostProfile.objects.get(pk=self.pk)
                    if old_instance.profile_image != self.profile_image:
                        should_compress = True
                except HostProfile.DoesNotExist:
                    should_compress = True

            if should_compress:
                compressed_file, extension = compress_profile_image(self.profile_image)

                original_name = self.profile_image.name.rsplit('.', 1)[0]
                new_filename = f"{original_name}.{extension}"

                self.profile_image.save(
                    new_filename,
                    compressed_file,
                    save=False
                )

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

    def __str__(self):
        return self.business_name


# ============================================================
# 2. Host Venue
# ============================================================

class HostVenue(models.Model):
    host_profile = models.ForeignKey(
        HostProfile,
        on_delete=models.CASCADE,
        related_name='venues'
    )

    venue_name = models.CharField(max_length=255)
    slug = models.SlugField(max_length=240, unique=True, blank=True)

    venue_category = models.CharField(
        max_length=50,
        choices=VenueCategory.choices
    )

    short_description = models.CharField(max_length=300)
    full_description = models.TextField()

    district = models.CharField(
        max_length=120,
        choices=SRI_LANKA_DISTRICT_CHOICES
    )

    city = models.CharField(
        max_length=120,
        choices=SRI_LANKA_CITY_CHOICES
    )
    address = models.TextField(blank=True)

    google_map_link = models.URLField(blank=True)

    cover_image = models.ImageField(
        upload_to='hosting_app/venue_covers/',
        null=True,
        blank=True
    )

    max_guest_capacity = models.PositiveIntegerField(
        null=True,
        blank=True,
        help_text="Maximum number of people the venue can usually host for relevant event arrangements."
    )

    # Event suitability
    supports_romantic_dates = models.BooleanField(default=False)
    supports_proposal_events = models.BooleanField(default=False)
    supports_anniversaries = models.BooleanField(default=False)
    supports_birthdays = models.BooleanField(default=False)
    supports_honeymoon_experiences = models.BooleanField(default=False)
    supports_private_dinners = models.BooleanField(default=False)
    supports_engagement_events = models.BooleanField(default=False)
    supports_small_weddings = models.BooleanField(default=False)
    supports_custom_celebrations = models.BooleanField(default=False)

    # Venue facilities / capabilities
    meal_service_available = models.BooleanField(default=False)
    alcohol_available = models.BooleanField(default=False)
    decoration_available = models.BooleanField(default=False)
    photography_allowed = models.BooleanField(default=True)
    music_allowed = models.BooleanField(default=True)

    indoor_available = models.BooleanField(default=False)
    outdoor_available = models.BooleanField(default=False)
    private_area_available = models.BooleanField(default=False)
    parking_available = models.BooleanField(default=False)

    important_notes = models.TextField(
        blank=True,
        help_text="Any rules, restrictions, advance notice requirements, or practical notes."
    )

    listing_status = models.CharField(
        max_length=30,
        choices=PublicListingStatus.choices,
        default=PublicListingStatus.DRAFT
    )

    is_public = models.BooleanField(default=False)
    is_featured = models.BooleanField(default=False)

    admin_review_note = models.TextField(blank=True)
    approved_at = models.DateTimeField(null=True, blank=True)

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['-is_featured', '-created_at']

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

    def __str__(self):
        return self.venue_name


# ============================================================
# 3. Venue Gallery Images
# ============================================================

# ============================================================
# Venue Gallery Image Validation Helpers
# ============================================================

def validate_venue_gallery_image_size(image):
    """
    Restrict venue gallery image upload size.
    Max: 6 MB
    """
    max_size_mb = 6

    if image.size > max_size_mb * 1024 * 1024:
        raise ValidationError(
            f"Venue gallery image file size must be under {max_size_mb} MB."
        )


def validate_venue_gallery_image_content(image):
    """
    Validate that the uploaded file is a real, readable image.
    """
    try:
        img = Image.open(image)
        img.verify()
    except Exception:
        raise ValidationError("Upload a valid venue image file.")
    
def compress_venue_gallery_image(
    uploaded_image,
    max_size=(1600, 1200),
    quality=85
):
    """
    Resize and compress venue gallery images while preserving aspect ratio.

    Recommended for listing/gallery use:
    - Max dimensions: 1600 x 1200
    - JPEG/WebP quality: 85
    - PNG optimized without forced quality loss
    """
    image = Image.open(uploaded_image)

    # Work on a safe in-memory copy
    image = image.copy()

    # Resize proportionally without stretching
    image.thumbnail(max_size)

    output = BytesIO()

    image_format = image.format or 'JPEG'
    image_format = image_format.upper()

    if image_format in ['JPG', 'JPEG']:
        if image.mode in ('RGBA', 'LA', 'P'):
            image = image.convert('RGB')

        image.save(
            output,
            format='JPEG',
            quality=quality,
            optimize=True
        )
        extension = 'jpg'

    elif image_format == 'PNG':
        image.save(
            output,
            format='PNG',
            optimize=True
        )
        extension = 'png'

    elif image_format == 'WEBP':
        image.save(
            output,
            format='WEBP',
            quality=quality,
            optimize=True
        )
        extension = 'webp'

    else:
        # Safe fallback to JPEG
        if image.mode in ('RGBA', 'LA', 'P'):
            image = image.convert('RGB')

        image.save(
            output,
            format='JPEG',
            quality=quality,
            optimize=True
        )
        extension = 'jpg'

    output.seek(0)

    return ContentFile(output.read()), extension

class HostVenueImage(models.Model):
    venue = models.ForeignKey(
        HostVenue,
        on_delete=models.CASCADE,
        related_name='gallery_images'
    )

    image = models.ImageField(
        upload_to='hosting_app/venue_gallery/',
        validators=[
            FileExtensionValidator(
                allowed_extensions=['jpg', 'jpeg', 'png', 'webp']
            ),
            validate_venue_gallery_image_size,
            validate_venue_gallery_image_content,
        ],
        help_text="Allowed: JPG, JPEG, PNG, WEBP. Max size: 6 MB."
    )

    caption = models.CharField(max_length=255, blank=True)
    display_order = models.PositiveIntegerField(default=0)

    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['display_order', 'id']

    def save(self, *args, **kwargs):
        """
        Compress venue image only when:
        - A new image is uploaded, or
        - The existing image is replaced
        """
        if self.image:
            should_compress = False

            if not self.pk:
                should_compress = True
            else:
                try:
                    old_instance = HostVenueImage.objects.get(pk=self.pk)
                    if old_instance.image != self.image:
                        should_compress = True
                except HostVenueImage.DoesNotExist:
                    should_compress = True

            if should_compress:
                compressed_file, extension = compress_venue_gallery_image(
                    self.image
                )

                original_name = self.image.name.rsplit('.', 1)[0]
                new_filename = f"{original_name}.{extension}"

                self.image.save(
                    new_filename,
                    compressed_file,
                    save=False
                )

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

    def __str__(self):
        return f"Image for {self.venue.venue_name}"


# ============================================================
# 4. Event Packages
# ============================================================
# ============================================================
# Event Package Image Validation Helpers
# ============================================================

def validate_event_package_image_size(image):
    """
    Restrict event package image upload size.
    Max: 5 MB
    """
    max_size_mb = 5

    if image.size > max_size_mb * 1024 * 1024:
        raise ValidationError(
            f"Event package image file size must be under {max_size_mb} MB."
        )


def validate_event_package_image_content(image):
    """
    Validate that the uploaded file is a real, readable image.
    """
    try:
        img = Image.open(image)
        img.verify()
    except Exception:
        raise ValidationError("Upload a valid event package image file.")
    
def compress_event_package_image(
    uploaded_image,
    max_size=(1400, 900),
    quality=85
):
    """
    Resize and compress event package images while preserving aspect ratio.

    Recommended for package cards and detail hero images:
    - Max dimensions: 1400 x 900
    - JPEG/WebP quality: 85
    - PNG optimized without forced quality loss
    """
    image = Image.open(uploaded_image)

    # Work on a safe copy before thumbnail modification
    original_format = image.format
    image = image.copy()

    # Resize proportionally
    image.thumbnail(max_size)

    output = BytesIO()

    image_format = (original_format or 'JPEG').upper()

    if image_format in ['JPG', 'JPEG']:
        if image.mode in ('RGBA', 'LA', 'P'):
            image = image.convert('RGB')

        image.save(
            output,
            format='JPEG',
            quality=quality,
            optimize=True
        )
        extension = 'jpg'

    elif image_format == 'PNG':
        image.save(
            output,
            format='PNG',
            optimize=True
        )
        extension = 'png'

    elif image_format == 'WEBP':
        image.save(
            output,
            format='WEBP',
            quality=quality,
            optimize=True
        )
        extension = 'webp'

    else:
        # Safe fallback conversion to JPEG
        if image.mode in ('RGBA', 'LA', 'P'):
            image = image.convert('RGB')

        image.save(
            output,
            format='JPEG',
            quality=quality,
            optimize=True
        )
        extension = 'jpg'

    output.seek(0)

    return ContentFile(output.read()), extension


class HostEventPackage(models.Model):
    venue = models.ForeignKey(
        HostVenue,
        on_delete=models.CASCADE,
        related_name='event_packages'
    )

    title = models.CharField(max_length=255)
    slug = models.SlugField(max_length=240, unique=True, blank=True)

    event_type = models.CharField(
        max_length=50,
        choices=EventType.choices
    )

    short_description = models.CharField(max_length=300)
    full_description = models.TextField()

    package_image = models.ImageField(
        upload_to='hosting_app/package_images/',
        null=True,
        blank=True,
        validators=[
            FileExtensionValidator(
                allowed_extensions=['jpg', 'jpeg', 'png', 'webp']
            ),
            validate_event_package_image_size,
            validate_event_package_image_content,
        ],
        help_text="Optional package image. Allowed: JPG, JPEG, PNG, WEBP. Max size: 5 MB."
    )

    duration_hours = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        default=Decimal('2.00'),
        help_text="Example: 2.50 means two and a half hours."
    )

    base_price_usd = models.DecimalField(
        max_digits=12,
        decimal_places=2
    )

    max_people_included = models.PositiveIntegerField(
        default=2
    )

    extra_person_price_usd = models.DecimalField(
        max_digits=12,
        decimal_places=2,
        default=Decimal('0.00')
    )

    includes_meals = models.BooleanField(default=False)
    includes_decoration = models.BooleanField(default=False)
    includes_photography = models.BooleanField(default=False)
    includes_music = models.BooleanField(default=False)

    is_customizable = models.BooleanField(default=True)

    inclusions_summary = models.TextField(
        blank=True,
        help_text="Short package inclusion summary. Detailed inclusions can be added below."
    )

    exclusions_summary = models.TextField(
        blank=True,
        help_text="What is not included in the package."
    )

    pricing_note = models.TextField(
        blank=True,
        help_text="Example: Meals priced separately, subject to availability, seasonal price variation, etc."
    )

    listing_status = models.CharField(
        max_length=30,
        choices=PublicListingStatus.choices,
        default=PublicListingStatus.DRAFT
    )

    is_active = models.BooleanField(default=True)
    is_featured = models.BooleanField(default=False)

    admin_review_note = models.TextField(blank=True)
    approved_at = models.DateTimeField(null=True, blank=True)

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['-is_featured', '-created_at']

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = generate_unique_slug(self, self.title)

        if self.package_image:
            should_compress = False

            if not self.pk:
                should_compress = True
            else:
                try:
                    old_instance = HostEventPackage.objects.get(pk=self.pk)
                    if old_instance.package_image != self.package_image:
                        should_compress = True
                except HostEventPackage.DoesNotExist:
                    should_compress = True

            if should_compress:
                compressed_file, extension = compress_event_package_image(
                    self.package_image
                )

                original_name = self.package_image.name.rsplit('.', 1)[0]
                new_filename = f"{original_name}.{extension}"

                self.package_image.save(
                    new_filename,
                    compressed_file,
                    save=False
                )

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

    def __str__(self):
        return f"{self.title} - {self.venue.venue_name}"


# ============================================================
# 5. Optional Detailed Package Inclusions
# ============================================================

class HostPackageInclusion(models.Model):
    package = models.ForeignKey(
        HostEventPackage,
        on_delete=models.CASCADE,
        related_name='detailed_inclusions'
    )

    inclusion_text = models.CharField(max_length=255)
    display_order = models.PositiveIntegerField(default=0)

    class Meta:
        ordering = ['display_order', 'id']

    def __str__(self):
        return self.inclusion_text


# ============================================================
# 6. Meal Plans
# ============================================================

# ============================================================
# Meal Plan Image Validation Helpers
# ============================================================

def validate_meal_plan_image_size(image):
    """
    Restrict meal plan image upload size.
    Max: 5 MB
    """
    max_size_mb = 5

    if image.size > max_size_mb * 1024 * 1024:
        raise ValidationError(
            f"Meal plan image file size must be under {max_size_mb} MB."
        )


def validate_meal_plan_image_content(image):
    """
    Validate that the uploaded file is a real, readable image.
    """
    try:
        img = Image.open(image)
        img.verify()
    except Exception:
        raise ValidationError("Upload a valid meal plan image file.")
    
def compress_meal_plan_image(
    uploaded_image,
    max_size=(1200, 800),
    quality=85
):
    """
    Resize and compress meal plan images while preserving aspect ratio.

    Recommended for meal plan cards and preview blocks:
    - Max dimensions: 1200 x 800
    - JPEG/WebP quality: 85
    - PNG optimized without forced quality loss
    """
    image = Image.open(uploaded_image)

    original_format = image.format
    image = image.copy()

    image.thumbnail(max_size)

    output = BytesIO()

    image_format = (original_format or 'JPEG').upper()

    if image_format in ['JPG', 'JPEG']:
        if image.mode in ('RGBA', 'LA', 'P'):
            image = image.convert('RGB')

        image.save(
            output,
            format='JPEG',
            quality=quality,
            optimize=True
        )
        extension = 'jpg'

    elif image_format == 'PNG':
        image.save(
            output,
            format='PNG',
            optimize=True
        )
        extension = 'png'

    elif image_format == 'WEBP':
        image.save(
            output,
            format='WEBP',
            quality=quality,
            optimize=True
        )
        extension = 'webp'

    else:
        if image.mode in ('RGBA', 'LA', 'P'):
            image = image.convert('RGB')

        image.save(
            output,
            format='JPEG',
            quality=quality,
            optimize=True
        )
        extension = 'jpg'

    output.seek(0)

    return ContentFile(output.read()), extension

class HostMealPlan(models.Model):
    venue = models.ForeignKey(
        HostVenue,
        on_delete=models.CASCADE,
        related_name='meal_plans'
    )

    title = models.CharField(max_length=255)
    description = models.TextField()

    meal_image = models.ImageField(
        upload_to='hosting_app/meal_plan_images/',
        null=True,
        blank=True,
        validators=[
            FileExtensionValidator(
                allowed_extensions=['jpg', 'jpeg', 'png', 'webp']
            ),
            validate_meal_plan_image_size,
            validate_meal_plan_image_content,
        ],
        help_text="Optional meal plan image. Allowed: JPG, JPEG, PNG, WEBP. Max size: 5 MB."
    )

    price_per_person_usd = models.DecimalField(
        max_digits=12,
        decimal_places=2
    )

    minimum_people = models.PositiveIntegerField(default=2)

    is_active = models.BooleanField(default=True)
    is_featured = models.BooleanField(default=False)

    listing_status = models.CharField(
        max_length=30,
        choices=PublicListingStatus.choices,
        default=PublicListingStatus.DRAFT
    )

    admin_review_note = models.TextField(blank=True)
    approved_at = models.DateTimeField(null=True, blank=True)

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['-is_featured', 'title']

    def save(self, *args, **kwargs):
        """
        Compress meal image only when:
        - A new image is uploaded, or
        - The existing image is replaced
        """
        if self.meal_image:
            should_compress = False

            if not self.pk:
                should_compress = True
            else:
                try:
                    old_instance = HostMealPlan.objects.get(pk=self.pk)
                    if old_instance.meal_image != self.meal_image:
                        should_compress = True
                except HostMealPlan.DoesNotExist:
                    should_compress = True

            if should_compress:
                compressed_file, extension = compress_meal_plan_image(
                    self.meal_image
                )

                original_name = self.meal_image.name.rsplit('.', 1)[0]
                new_filename = f"{original_name}.{extension}"

                self.meal_image.save(
                    new_filename,
                    compressed_file,
                    save=False
                )

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

    def __str__(self):
        return f"{self.title} - {self.venue.venue_name}"


# ============================================================
# 7. Optional Add-On Services
# ============================================================

# ============================================================
# Add-On Service Image Validation Helpers
# ============================================================

def validate_add_on_service_image_size(image):
    """
    Restrict add-on service image upload size.
    Max: 4 MB
    """
    max_size_mb = 4

    if image.size > max_size_mb * 1024 * 1024:
        raise ValidationError(
            f"Add-on service image file size must be under {max_size_mb} MB."
        )


def validate_add_on_service_image_content(image):
    """
    Validate that the uploaded file is a real, readable image.
    """
    try:
        img = Image.open(image)
        img.verify()
    except Exception:
        raise ValidationError("Upload a valid add-on service image file.")
    
def compress_add_on_service_image(
    uploaded_image,
    max_size=(1000, 750),
    quality=85
):
    """
    Resize and compress add-on service images while preserving aspect ratio.

    Recommended for add-on cards and listing previews:
    - Max dimensions: 1000 x 750
    - JPEG/WebP quality: 85
    - PNG optimized without forced quality loss
    """
    image = Image.open(uploaded_image)

    original_format = image.format
    image = image.copy()

    image.thumbnail(max_size)

    output = BytesIO()

    image_format = (original_format or 'JPEG').upper()

    if image_format in ['JPG', 'JPEG']:
        if image.mode in ('RGBA', 'LA', 'P'):
            image = image.convert('RGB')

        image.save(
            output,
            format='JPEG',
            quality=quality,
            optimize=True
        )
        extension = 'jpg'

    elif image_format == 'PNG':
        image.save(
            output,
            format='PNG',
            optimize=True
        )
        extension = 'png'

    elif image_format == 'WEBP':
        image.save(
            output,
            format='WEBP',
            quality=quality,
            optimize=True
        )
        extension = 'webp'

    else:
        if image.mode in ('RGBA', 'LA', 'P'):
            image = image.convert('RGB')

        image.save(
            output,
            format='JPEG',
            quality=quality,
            optimize=True
        )
        extension = 'jpg'

    output.seek(0)

    return ContentFile(output.read()), extension

class HostAddOnService(models.Model):
    venue = models.ForeignKey(
        HostVenue,
        on_delete=models.CASCADE,
        related_name='add_on_services'
    )

    title = models.CharField(max_length=255)
    description = models.TextField()

    add_on_image = models.ImageField(
        upload_to='hosting_app/add_on_service_images/',
        null=True,
        blank=True,
        validators=[
            FileExtensionValidator(
                allowed_extensions=['jpg', 'jpeg', 'png', 'webp']
            ),
            validate_add_on_service_image_size,
            validate_add_on_service_image_content,
        ],
        help_text="Optional add-on image. Allowed: JPG, JPEG, PNG, WEBP. Max size: 4 MB."
    )

    pricing_type = models.CharField(
        max_length=30,
        choices=AddOnPricingType.choices,
        default=AddOnPricingType.FIXED
    )

    price_usd = models.DecimalField(
        max_digits=12,
        decimal_places=2
    )

    is_active = models.BooleanField(default=True)
    is_featured = models.BooleanField(default=False)

    listing_status = models.CharField(
        max_length=30,
        choices=PublicListingStatus.choices,
        default=PublicListingStatus.DRAFT
    )

    admin_review_note = models.TextField(blank=True)
    approved_at = models.DateTimeField(null=True, blank=True)

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['-is_featured', 'title']

    def save(self, *args, **kwargs):
        """
        Compress add-on image only when:
        - A new image is uploaded, or
        - The existing image is replaced
        """
        if self.add_on_image:
            should_compress = False

            if not self.pk:
                should_compress = True
            else:
                try:
                    old_instance = HostAddOnService.objects.get(pk=self.pk)
                    if old_instance.add_on_image != self.add_on_image:
                        should_compress = True
                except HostAddOnService.DoesNotExist:
                    should_compress = True

            if should_compress:
                compressed_file, extension = compress_add_on_service_image(
                    self.add_on_image
                )

                original_name = self.add_on_image.name.rsplit('.', 1)[0]
                new_filename = f"{original_name}.{extension}"

                self.add_on_image.save(
                    new_filename,
                    compressed_file,
                    save=False
                )

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

    def __str__(self):
        return f"{self.title} - {self.venue.venue_name}"

class HostingClientProfile(models.Model):
    user = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='hosting_client_profile'
    )

    client_type = models.CharField(
        max_length=40,
        choices=HostingClientType.choices,
        default=HostingClientType.HOSTING_CLIENT
    )

    full_name = models.CharField(max_length=255)
    email = models.EmailField()
    phone_number = models.CharField(max_length=50)
    whatsapp_number = models.CharField(max_length=50, blank=True)

    profile_image = models.ImageField(
        upload_to='hosting_app/client_profiles/',
        null=True,
        blank=True,
        validators=[
            FileExtensionValidator(
                allowed_extensions=['jpg', 'jpeg', 'png', 'webp']
            ),
            validate_profile_image_size,
            validate_profile_image_content,
        ],
        help_text="Optional profile image. Allowed: JPG, JPEG, PNG, WEBP. Max size: 3 MB."
    )

    country = models.CharField(
        max_length=120,
        blank=True,
        default='Sri Lanka'
    )

    city = models.CharField(max_length=120, blank=True)

    created_from_existing_profile = models.BooleanField(default=False)

    source_profile_type = models.CharField(
        max_length=60,
        choices=HostingClientType.choices,
        blank=True,
        help_text="Example: foreigner, dating_partner, rental_owner, service_partner, property_care_owner, affiliate."
    )

    source_profile_id = models.PositiveIntegerField(
        null=True,
        blank=True,
        help_text="Optional source profile ID if this hosting client was created from another profile."
    )

    is_active = models.BooleanField(default=True)
    admin_note = models.TextField(blank=True)

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name = 'Hosting Client Profile'
        verbose_name_plural = 'Hosting Client Profiles'
        ordering = ['-created_at']

    def save(self, *args, **kwargs):
        """
        Compress profile image only when:
        - A profile image exists, and
        - It is newly uploaded or changed
        """
        if self.profile_image:
            should_compress = False

            if not self.pk:
                should_compress = True
            else:
                try:
                    old_instance = HostingClientProfile.objects.get(pk=self.pk)
                    if old_instance.profile_image != self.profile_image:
                        should_compress = True
                except HostingClientProfile.DoesNotExist:
                    should_compress = True

            if should_compress:
                compressed_file, extension = compress_profile_image(
                    self.profile_image
                )

                original_name = self.profile_image.name.rsplit('.', 1)[0]
                new_filename = f"{original_name}.{extension}"

                self.profile_image.save(
                    new_filename,
                    compressed_file,
                    save=False
                )

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

    def __str__(self):
        return f"{self.full_name} - {self.get_client_type_display()}"

# ============================================================
# 8. Guest Booking Request
# ============================================================

class HostBookingRequest(models.Model):
    # ============================================================
    # Client / Booker
    # ============================================================

    guest_profile = models.ForeignKey(
        'expat_life.ForeignerProfile',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='hosting_booking_requests',
        help_text="Used when the booking is made by a Live in Sri Lanka Guest / Foreigner."
    )

    hosting_client_profile = models.ForeignKey(
        HostingClientProfile,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='hosting_booking_requests',
        help_text="Used when the booking is made by a local/direct hosting client."
    )

    booked_by_user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='hosting_booking_requests',
        help_text="The actual user account that created this booking/order."
    )

    client_type = models.CharField(
        max_length=40,
        choices=HostingClientType.choices,
        default=HostingClientType.FOREIGNER
    )

    # ============================================================
    # Venue / Host
    # ============================================================

    venue = models.ForeignKey(
        HostVenue,
        on_delete=models.CASCADE,
        related_name='booking_requests'
    )

    host_profile = models.ForeignKey(
        HostProfile,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='booking_requests',
        help_text="Snapshot link to the hosting partner profile for faster dashboard filtering."
    )

    # ============================================================
    # Selected Products
    # ============================================================

    selected_package = models.ForeignKey(
        HostEventPackage,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='booking_requests'
    )

    selected_meal_plan = models.ForeignKey(
        HostMealPlan,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='booking_requests'
    )

    # ============================================================
    # Snapshot Fields
    # ============================================================

    venue_name_snapshot = models.CharField(max_length=255, blank=True)
    host_business_name_snapshot = models.CharField(max_length=255, blank=True)

    package_title_snapshot = models.CharField(max_length=255, blank=True)
    meal_plan_title_snapshot = models.CharField(max_length=255, blank=True)

    # ============================================================
    # Event Details
    # ============================================================

    event_type = models.CharField(
        max_length=50,
        choices=EventType.choices
    )

    preferred_event_date = models.DateField()
    preferred_start_time = models.TimeField(null=True, blank=True)

    expected_duration_hours = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        null=True,
        blank=True
    )

    number_of_guests = models.PositiveIntegerField(default=2)

    occasion_title = models.CharField(
        max_length=255,
        blank=True,
        help_text="Example: Surprise proposal for my partner, anniversary dinner, birthday celebration."
    )

    occasion_note = models.TextField(
        blank=True,
        help_text="Any personal details that help Ensolanka understand the intended experience."
    )

    special_requests = models.TextField(blank=True)

    # ============================================================
    # Pricing Snapshots
    # ============================================================

    package_amount_snapshot_usd = models.DecimalField(
        max_digits=12,
        decimal_places=2,
        default=Decimal('0.00')
    )

    meal_amount_snapshot_usd = models.DecimalField(
        max_digits=12,
        decimal_places=2,
        default=Decimal('0.00')
    )

    add_on_amount_snapshot_usd = models.DecimalField(
        max_digits=12,
        decimal_places=2,
        default=Decimal('0.00')
    )

    total_amount_usd = models.DecimalField(
        max_digits=12,
        decimal_places=2,
        default=Decimal('0.00')
    )

    commission_rate_percent = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        default=Decimal('10.00')
    )

    ensolanka_commission_usd = models.DecimalField(
        max_digits=12,
        decimal_places=2,
        default=Decimal('0.00')
    )

    host_net_amount_usd = models.DecimalField(
        max_digits=12,
        decimal_places=2,
        default=Decimal('0.00')
    )

    # ============================================================
    # Payment / Payable Link
    # ============================================================

    payable_item = models.OneToOneField(
        'expat_life.UserPayableItem',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='host_booking_request'
    )

    payment_status = models.CharField(
        max_length=30,
        choices=HostBookingPaymentStatus.choices,
        default=HostBookingPaymentStatus.DUE
    )

    payment_reference_code = models.CharField(
        max_length=120,
        blank=True,
        help_text="Example: HOST_BOOKING_12"
    )

    # ============================================================
    # Booking Status / Notes
    # ============================================================

    status = models.CharField(
        max_length=40,
        choices=BookingRequestStatus.choices,
        default=BookingRequestStatus.PAYMENT_DUE
    )

    admin_review_note = models.TextField(blank=True)
    host_coordination_note = models.TextField(blank=True)

    # ============================================================
    # Timestamps
    # ============================================================

    submitted_at = models.DateTimeField(auto_now_add=True)
    guest_confirmed_at = models.DateTimeField(null=True, blank=True)
    paid_at = models.DateTimeField(null=True, blank=True)
    delivered_at = models.DateTimeField(null=True, blank=True)
    completed_at = models.DateTimeField(null=True, blank=True)
    cancelled_at = models.DateTimeField(null=True, blank=True)
    refunded_at = models.DateTimeField(null=True, blank=True)

    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['-submitted_at']
        indexes = [
            models.Index(fields=['booked_by_user', 'payment_status']),
            models.Index(fields=['host_profile', 'payment_status']),
            models.Index(fields=['venue', 'payment_status']),
            models.Index(fields=['status']),
        ]

    def save(self, *args, **kwargs):
        if self.venue:
            self.venue_name_snapshot = self.venue.venue_name or ''

            if self.venue.host_profile:
                self.host_profile = self.venue.host_profile
                self.host_business_name_snapshot = (
                    self.venue.host_profile.business_name or ''
                )

        if self.selected_package:
            self.package_title_snapshot = self.selected_package.title or ''

        if self.selected_meal_plan:
            self.meal_plan_title_snapshot = self.selected_meal_plan.title or ''

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

    def __str__(self):
        if self.guest_profile:
            client_name = str(self.guest_profile)
        elif self.hosting_client_profile:
            client_name = self.hosting_client_profile.full_name
        elif self.booked_by_user:
            client_name = self.booked_by_user.get_username()
        else:
            client_name = 'Unknown Client'

        venue_name = self.venue_name_snapshot

        if not venue_name and self.venue_id:
            venue_name = self.venue.venue_name

        return f"{self.get_event_type_display()} - {client_name} - {venue_name}"

# ============================================================
# 9. Booking Request Add-Ons
# ============================================================

class HostBookingRequestAddOn(models.Model):
    booking_request = models.ForeignKey(
        HostBookingRequest,
        on_delete=models.CASCADE,
        related_name='selected_add_ons'
    )

    add_on_service = models.ForeignKey(
        HostAddOnService,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='booking_add_on_items'
    )

    add_on_title_snapshot = models.CharField(max_length=255)
    pricing_type_snapshot = models.CharField(max_length=50, blank=True)

    quantity = models.PositiveIntegerField(default=1)

    unit_price_snapshot_usd = models.DecimalField(
        max_digits=12,
        decimal_places=2,
        default=Decimal('0.00')
    )

    subtotal_snapshot_usd = models.DecimalField(
        max_digits=12,
        decimal_places=2,
        default=Decimal('0.00')
    )

    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name = 'Host Booking Request Add-on'
        verbose_name_plural = 'Host Booking Request Add-ons'

    def save(self, *args, **kwargs):
        if self.add_on_service:
            self.add_on_title_snapshot = self.add_on_service.title or ''

            if hasattr(self.add_on_service, 'pricing_type'):
                self.pricing_type_snapshot = self.add_on_service.pricing_type or ''

            self.unit_price_snapshot_usd = (
                self.add_on_service.price_usd or Decimal('0.00')
            )

        self.subtotal_snapshot_usd = (
            Decimal(str(self.unit_price_snapshot_usd or 0)) *
            Decimal(str(self.quantity or 1))
        ).quantize(Decimal('0.01'))

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

    def __str__(self):
        return f"{self.add_on_title_snapshot} x {self.quantity}"


# ============================================================
# 10. Staff Reviewed Quotation
# ============================================================

class HostBookingQuotation(models.Model):
    booking_request = models.ForeignKey(
        HostBookingRequest,
        on_delete=models.CASCADE,
        related_name='quotations'
    )

    prepared_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='prepared_hosting_quotations'
    )

    # Pricing snapshots
    package_amount_usd = models.DecimalField(
        max_digits=12,
        decimal_places=2,
        default=Decimal('0.00')
    )

    meal_amount_usd = models.DecimalField(
        max_digits=12,
        decimal_places=2,
        default=Decimal('0.00')
    )

    add_on_amount_usd = models.DecimalField(
        max_digits=12,
        decimal_places=2,
        default=Decimal('0.00')
    )

    manual_adjustment_usd = models.DecimalField(
        max_digits=12,
        decimal_places=2,
        default=Decimal('0.00'),
        help_text="Use positive or negative adjustment where staff finalize a custom agreed amount."
    )

    final_guest_payable_amount_usd = models.DecimalField(
        max_digits=12,
        decimal_places=2,
        default=Decimal('0.00')
    )

    # Commission snapshot
    commission_rate_percent = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        default=Decimal('10.00')
    )

    ensolanka_commission_usd = models.DecimalField(
        max_digits=12,
        decimal_places=2,
        default=Decimal('0.00')
    )

    host_net_amount_usd = models.DecimalField(
        max_digits=12,
        decimal_places=2,
        default=Decimal('0.00')
    )

    payment_due_date = models.DateField(null=True, blank=True)

    quotation_note_to_guest = models.TextField(blank=True)
    internal_note = models.TextField(blank=True)

    status = models.CharField(
        max_length=30,
        choices=QuotationStatus.choices,
        default=QuotationStatus.DRAFT
    )

    sent_to_guest_at = models.DateTimeField(null=True, blank=True)
    accepted_at = models.DateTimeField(null=True, 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"Quotation for Request #{self.booking_request_id}"

# ============================================================
# Host Banking Details
# ============================================================

class HostBankingDetails(models.Model):
    host_profile = models.OneToOneField(
        HostProfile,
        on_delete=models.CASCADE,
        related_name='banking_details'
    )

    account_holder_name = models.CharField(max_length=200)
    bank_name = models.CharField(max_length=200)
    branch_name = models.CharField(max_length=200, blank=True)

    account_number = models.CharField(max_length=100)
    iban = models.CharField(max_length=100, blank=True)
    swift_code = models.CharField(max_length=50, blank=True)

    bank_country = models.CharField(
        max_length=120,
        blank=True,
        default='Sri Lanka'
    )

    bank_address = models.TextField(blank=True)

    payment_notes = models.TextField(
        blank=True,
        help_text="Optional notes for transfers, preferred payout instructions, or additional details."
    )

    is_verified = models.BooleanField(default=False)

    admin_review_note = models.TextField(
        blank=True,
        help_text="Internal admin note about banking verification or payout readiness."
    )

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name = 'Host Banking Details'
        verbose_name_plural = 'Host Banking Details'

    def __str__(self):
        return f"{self.host_profile.business_name} - {self.bank_name}"

class HostingPartnerGuidanceResource(models.Model):
    LANGUAGE_CHOICES = [
        ('en', 'English'),
        ('si', 'Sinhala'),
        ('ta', 'Tamil'),
    ]

    language = models.CharField(
        max_length=2,
        choices=LANGUAGE_CHOICES,
        default='en',
        unique=True
    )

    title = models.CharField(
        max_length=200,
        default="Ensolanka Hosting Partner Guidance Playbook"
    )

    full_playbook_pdf = models.FileField(
        upload_to='hosting_partner_guidance/playbooks/%Y/%m/',
        blank=True,
        null=True,
        help_text="Upload the full hosting partner guidance playbook PDF."
    )

    summary_overview_pdf = models.FileField(
        upload_to='hosting_partner_guidance/summaries/%Y/%m/',
        blank=True,
        null=True,
        help_text="Upload the summary overview or quick guidance PDF."
    )

    audio_guide_file = models.FileField(
        upload_to='hosting_partner_guidance/audio/%Y/%m/',
        blank=True,
        null=True,
        help_text="Upload the audio guidance file, such as MP3."
    )

    youtube_video_link = models.URLField(
        blank=True,
        null=True,
        help_text="Optional YouTube or video guidance link."
    )

    description = models.TextField(
        blank=True,
        help_text="Brief description about this hosting partner guidance resource."
    )

    is_active = models.BooleanField(default=True)

    created_at = models.DateTimeField(default=timezone.now)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['language']
        verbose_name = "Hosting Partner Guidance Resource"
        verbose_name_plural = "Hosting Partner Guidance Resources"

    def __str__(self):
        return f"{self.get_language_display()} - {self.title}"