from io import BytesIO
from datetime import timedelta

from django.core.files.base import ContentFile
from django.db import models
from django.db.models import F
from django.db import IntegrityError, transaction
from django.db.models.functions import Coalesce
from django.urls import reverse
from django.utils import timezone
from django.utils.html import strip_tags
from django.utils.text import slugify
from PIL import Image


def optimize_uploaded_image(image_field, max_size=(1600, 1600), quality=82):
    if not image_field or not getattr(image_field, "name", ""):
        return

    original_name = image_field.name.lower()

    try:
        image_field.open()
        with Image.open(image_field) as image:
            image.load()
            if image.mode not in {"RGB", "L"}:
                image = image.convert("RGB")

            image.thumbnail(max_size)
            output = BytesIO()

            if original_name.endswith(".png"):
                image.save(output, format="PNG", optimize=True)
                file_name = f"{image_field.name.rsplit('.', 1)[0]}.png"
            elif original_name.endswith(".webp"):
                image.save(output, format="WEBP", quality=quality, method=6)
                file_name = f"{image_field.name.rsplit('.', 1)[0]}.webp"
            else:
                image.save(output, format="JPEG", quality=quality, optimize=True)
                file_name = f"{image_field.name.rsplit('.', 1)[0]}.jpg"

            output.seek(0)
            image_field.save(file_name, ContentFile(output.read()), save=False)
    except Exception:
        return


class OptimizedImageModel(models.Model):
    optimized_image_fields = ()

    class Meta:
        abstract = True

    def optimize_images(self):
        for field_name in self.optimized_image_fields:
            optimize_uploaded_image(getattr(self, field_name, None))

    def save(self, *args, **kwargs):
        self.optimize_images()
        super().save(*args, **kwargs)


class PublishableModel(models.Model):
    is_active = models.BooleanField(default=True)
    is_featured = models.BooleanField(default=False)
    display_order = models.PositiveIntegerField(default=0)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        abstract = True


def unique_slug_for(instance, value):
    base_slug = slugify(value)[:60] or "item"
    slug = base_slug
    model = instance.__class__
    counter = 2

    while model.objects.filter(slug=slug).exclude(pk=instance.pk).exists():
        suffix = f"-{counter}"
        slug = f"{base_slug[:60 - len(suffix)]}{suffix}"
        counter += 1

    return slug


class HolidayPackage(OptimizedImageModel, PublishableModel):
    optimized_image_fields = ("image",)

    class Status(models.TextChoices):
        DRAFT = "draft", "Draft"
        PUBLISHED = "published", "Published"
        ARCHIVED = "archived", "Archived"

    title = models.CharField(max_length=160)
    slug = models.SlugField(max_length=180, unique=True, blank=True)
    image = models.ImageField(upload_to="packages/", blank=True)
    duration = models.CharField(max_length=80)
    location = models.CharField(max_length=160)
    price_from = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        blank=True,
        null=True,
        help_text="Optional starting price.",
    )
    short_description = models.TextField(max_length=320)
    details = models.TextField(blank=True)
    itinerary = models.JSONField(default=list, blank=True)
    includes = models.JSONField(default=list, blank=True)
    excludes = models.JSONField(default=list, blank=True)
    gallery = models.JSONField(default=list, blank=True)
    seo_title = models.CharField(max_length=180, blank=True)
    seo_description = models.CharField(max_length=320, blank=True)
    seo_keywords = models.CharField(max_length=320, blank=True)
    canonical_url = models.URLField(blank=True)
    content_status = models.CharField(max_length=20, choices=Status.choices, default=Status.PUBLISHED)

    class Meta:
        ordering = ["display_order", "title"]
        verbose_name = "Holiday Package"
        verbose_name_plural = "Holiday Packages"

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

    def get_absolute_url(self):
        return f"{reverse('travel:packages')}#{self.slug}"

    def __str__(self):
        return self.title


class CabService(OptimizedImageModel, PublishableModel):
    optimized_image_fields = ("image",)

    title = models.CharField(max_length=160)
    slug = models.SlugField(max_length=180, unique=True, blank=True)
    image = models.ImageField(upload_to="cab-services/", blank=True)
    vehicle_type = models.CharField(max_length=120, blank=True)
    price_from = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
    availability = models.CharField(max_length=160, blank=True)
    description = models.TextField(max_length=420)
    gallery = models.JSONField(default=list, blank=True)
    seo_title = models.CharField(max_length=180, blank=True)
    seo_description = models.CharField(max_length=320, blank=True)
    seo_keywords = models.CharField(max_length=320, blank=True)
    canonical_url = models.URLField(blank=True)

    class Meta:
        ordering = ["display_order", "title"]
        verbose_name = "Cab Service"
        verbose_name_plural = "Cab Services"

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

    def get_absolute_url(self):
        return f"{reverse('travel:cab_services')}#{self.slug}"

    def __str__(self):
        return self.title


class Destination(OptimizedImageModel, PublishableModel):
    optimized_image_fields = ("image", "hero_banner")

    name = models.CharField(max_length=160)
    slug = models.SlugField(max_length=180, unique=True, blank=True)
    image = models.ImageField(upload_to="destinations/", blank=True)
    hero_banner = models.ImageField(upload_to="destinations/banners/", blank=True)
    description = models.TextField(max_length=420)
    highlights = models.JSONField(default=list, blank=True)
    gallery = models.JSONField(default=list, blank=True)
    seo_title = models.CharField(max_length=180, blank=True)
    seo_description = models.CharField(max_length=320, blank=True)
    seo_keywords = models.CharField(max_length=320, blank=True)
    canonical_url = models.URLField(blank=True)

    class Meta:
        ordering = ["display_order", "name"]
        verbose_name = "Destination"
        verbose_name_plural = "Destinations"

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

    def get_absolute_url(self):
        return f"{reverse('travel:destinations')}#{self.slug}"

    def __str__(self):
        return self.name


class GalleryImage(OptimizedImageModel):
    optimized_image_fields = ("image",)

    title = models.CharField(max_length=160)
    image = models.ImageField(upload_to="gallery/")
    alt_text = models.CharField(max_length=220, blank=True)
    is_active = models.BooleanField(default=True)
    display_order = models.PositiveIntegerField(default=0)
    uploaded_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ["display_order", "-uploaded_at"]
        verbose_name = "Gallery Image"
        verbose_name_plural = "Gallery Images"

    def __str__(self):
        return self.title


class WebsiteSettings(OptimizedImageModel):
    optimized_image_fields = ("logo",)

    logo = models.ImageField(upload_to="branding/", blank=True)
    company_name = models.CharField(max_length=160, default="Aarya Holidays & Cabs")
    email = models.EmailField(blank=True)
    phone_number = models.CharField(max_length=30, blank=True)
    whatsapp_number = models.CharField(max_length=30, blank=True)
    address = models.TextField(blank=True)
    footer_text = models.TextField(blank=True)
    business_hours = models.CharField(max_length=120, blank=True)
    facebook_url = models.URLField(blank=True)
    instagram_url = models.URLField(blank=True)
    youtube_url = models.URLField(blank=True)
    x_url = models.URLField(blank=True)

    class Meta:
        verbose_name = "Website Settings"
        verbose_name_plural = "Website Settings"

    @classmethod
    def load(cls):
        obj, _ = cls.objects.get_or_create(pk=1)
        return obj

    def __str__(self):
        return self.company_name


class SEOSettings(OptimizedImageModel):
    optimized_image_fields = ("open_graph_image",)

    meta_title = models.CharField(max_length=180, blank=True)
    meta_description = models.CharField(max_length=320, blank=True)
    meta_keywords = models.CharField(max_length=320, blank=True)
    canonical_url = models.URLField(blank=True)
    open_graph_image = models.ImageField(upload_to="seo/", blank=True)
    robots_index = models.BooleanField(default=True)
    robots_follow = models.BooleanField(default=True)

    class Meta:
        verbose_name = "SEO Settings"
        verbose_name_plural = "SEO Settings"

    @classmethod
    def load(cls):
        obj, _ = cls.objects.get_or_create(pk=1)
        return obj


class GoogleSettings(models.Model):
    ga4_measurement_id = models.CharField(max_length=120, blank=True)
    gtm_container_id = models.CharField(max_length=120, blank=True)
    search_console_verification = models.CharField(max_length=220, blank=True)
    meta_pixel_id = models.CharField(max_length=120, blank=True)

    class Meta:
        verbose_name = "Google Settings"
        verbose_name_plural = "Google Settings"

    @classmethod
    def load(cls):
        obj, _ = cls.objects.get_or_create(pk=1)
        return obj


class HomePageSettings(models.Model):
    hero_title = models.CharField(max_length=220, blank=True)
    hero_subtitle = models.CharField(max_length=320, blank=True)
    hero_button_text = models.CharField(max_length=80, blank=True)
    hero_button_url = models.URLField(blank=True)
    hero_images = models.JSONField(default=list, blank=True)
    featured_destinations = models.ManyToManyField(Destination, blank=True, related_name="+")
    featured_packages = models.ManyToManyField(HolidayPackage, blank=True, related_name="+")

    class Meta:
        verbose_name = "Home Page Settings"
        verbose_name_plural = "Home Page Settings"

    @classmethod
    def load(cls):
        obj, _ = cls.objects.get_or_create(pk=1)
        return obj


class DashboardSettings(models.Model):
    dashboard_title = models.CharField(max_length=120, default="Aarya Holidays Dashboard")
    dashboard_subtitle = models.CharField(max_length=220, blank=True)

    class Meta:
        verbose_name = "Dashboard Settings"
        verbose_name_plural = "Dashboard Settings"

    @classmethod
    def load(cls):
        obj, _ = cls.objects.get_or_create(pk=1)
        return obj


class VisitorCounter(models.Model):
    total_count = models.PositiveBigIntegerField(default=0)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name = "Visitor Counter"
        verbose_name_plural = "Visitor Counter"

    @classmethod
    def load(cls):
        obj, _ = cls.objects.get_or_create(pk=1)
        return obj

    @classmethod
    def increment_total(cls):
        obj, _ = cls.objects.get_or_create(pk=1)
        cls.objects.filter(pk=obj.pk).update(total_count=F("total_count") + 1)
        return cls.objects.values_list("total_count", flat=True).get(pk=obj.pk)

    @classmethod
    def reset_total(cls):
        obj, _ = cls.objects.get_or_create(pk=1)
        cls.objects.filter(pk=obj.pk).update(total_count=0)
        return 0


class WebsiteLikeCounter(models.Model):
    total_likes = models.PositiveBigIntegerField(default=0)
    like_version = models.PositiveIntegerField(default=1)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name = "Website Like Counter"
        verbose_name_plural = "Website Like Counter"

    @classmethod
    def load(cls):
        obj, _ = cls.objects.get_or_create(pk=1)
        return obj

    @classmethod
    def increment_total(cls):
        obj, _ = cls.objects.get_or_create(pk=1)
        cls.objects.filter(pk=obj.pk).update(total_likes=F("total_likes") + 1)
        return cls.objects.values_list("total_likes", flat=True).get(pk=obj.pk)

    @classmethod
    def register_visitor_like(cls, visitor_token):
        obj, _ = cls.objects.get_or_create(pk=1)
        current_version = obj.like_version

        try:
            with transaction.atomic():
                WebsiteLikeEvent.objects.create(
                    visitor_token=visitor_token,
                    like_version=current_version,
                )
                cls.objects.filter(pk=obj.pk).update(total_likes=F("total_likes") + 1)
        except IntegrityError:
            likes = cls.objects.values_list("total_likes", flat=True).get(pk=obj.pk)
            return False, likes, current_version

        likes = cls.objects.values_list("total_likes", flat=True).get(pk=obj.pk)
        return True, likes, current_version

    @classmethod
    def reset_total(cls):
        obj, _ = cls.objects.get_or_create(pk=1)
        cls.objects.filter(pk=obj.pk).update(
            total_likes=0,
            like_version=F("like_version") + 1,
        )
        obj.refresh_from_db(fields=["total_likes", "like_version"])
        return obj.total_likes, obj.like_version


class WebsiteLikeEvent(models.Model):
    visitor_token = models.CharField(max_length=64)
    like_version = models.PositiveIntegerField()
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name = "Website Like Event"
        verbose_name_plural = "Website Like Events"
        constraints = [
            models.UniqueConstraint(
                fields=["visitor_token", "like_version"],
                name="unique_website_like_per_visitor_version",
            ),
        ]


class CustomerReview(models.Model):
    class Status(models.TextChoices):
        PENDING = "pending", "Pending"
        APPROVED = "approved", "Approved"
        REJECTED = "rejected", "Rejected"

    name = models.CharField(max_length=120)
    city = models.CharField(max_length=120, blank=True)
    email = models.EmailField(blank=True)
    rating = models.PositiveSmallIntegerField()
    review = models.TextField(max_length=1200)
    likes = models.PositiveIntegerField(default=0)
    status = models.CharField(max_length=20, choices=Status.choices, default=Status.PENDING)
    admin_notes = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ["-created_at"]
        verbose_name = "Customer Review"
        verbose_name_plural = "Customer Reviews"

    def save(self, *args, **kwargs):
        self.name = strip_tags(self.name).strip()
        self.city = strip_tags(self.city).strip()
        self.review = strip_tags(self.review).strip()
        self.rating = max(1, min(int(self.rating or 1), 5))
        super().save(*args, **kwargs)

    @classmethod
    def approved(cls):
        return cls.objects.filter(status=cls.Status.APPROVED)

    @classmethod
    def approved_average_rating(cls):
        return cls.approved().aggregate(avg=Coalesce(models.Avg("rating"), 0.0))["avg"]

    @classmethod
    def can_accept_similar_submission(cls, name, email, review_text, window_minutes=10):
        window_start = timezone.now() - timedelta(minutes=window_minutes)
        queryset = cls.objects.filter(
            created_at__gte=window_start,
            name__iexact=(name or "").strip(),
            review__iexact=(review_text or "").strip(),
        )
        if email:
            queryset = queryset.filter(email__iexact=email.strip())
        return not queryset.exists()


class ContactEnquiry(models.Model):
    class Requirement(models.TextChoices):
        HOLIDAY_PACKAGE = "holiday_package", "Holiday Package"
        CAB_SERVICE = "cab_service", "Cab Service"
        DESTINATION = "destination", "Destination"
        CORPORATE_TRAVEL = "corporate_travel", "Corporate Travel"
        OTHER = "other", "Other"

    class Status(models.TextChoices):
        NEW = "new", "New"
        CONTACTED = "contacted", "Contacted"
        QUOTATION_SENT = "quotation_sent", "Quotation Sent"
        CONFIRMED = "confirmed", "Confirmed"
        CLOSED = "closed", "Closed"

    class HotelCategory(models.TextChoices):
        BUDGET = "budget_2_star", "Budget (2 Star)"
        STANDARD = "standard_3_star", "Standard (3 Star)"
        DELUXE = "deluxe_4_star", "Deluxe (4 Star)"
        LUXURY = "luxury_5_star", "Luxury (5 Star)"
        RESORT = "resort", "Resort"
        VILLA = "villa", "Villa"
        NO_PREFERENCE = "no_preference", "No Preference"

    name = models.CharField(max_length=120)
    phone = models.CharField(max_length=30)
    email = models.EmailField(blank=True)
    city = models.CharField(max_length=120, blank=True)
    destination = models.CharField(max_length=120, blank=True)
    date_of_travel = models.DateField(blank=True, null=True)
    adults = models.PositiveIntegerField(blank=True, null=True)
    children = models.PositiveIntegerField(default=0)
    children_ages = models.CharField(max_length=220, blank=True)
    number_of_nights = models.PositiveIntegerField(blank=True, null=True)
    hotel_category = models.CharField(
        max_length=30,
        choices=HotelCategory.choices,
        blank=True,
    )
    special_request = models.TextField(blank=True)
    travel_requirement = models.CharField(
        max_length=40,
        choices=Requirement.choices,
        default=Requirement.HOLIDAY_PACKAGE,
    )
    message = models.TextField()
    status = models.CharField(max_length=20, choices=Status.choices, default=Status.NEW)
    admin_notes = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ["-created_at"]
        verbose_name = "Contact Enquiry"
        verbose_name_plural = "Contact Enquiries"

    def __str__(self):
        return f"{self.name} - {self.get_travel_requirement_display()}"


class CompanyInformation(models.Model):
    company_name = models.CharField(max_length=160, default="Aarya Holidays & Cabs")
    tagline = models.CharField(
        max_length=220,
        default="Curated holidays and dependable cab travel from one trusted team.",
    )
    introduction = models.TextField(
        default="We plan memorable holidays and comfortable cab journeys for families, groups, and businesses."
    )
    story = models.TextField(blank=True)
    mission = models.TextField(blank=True)
    vision = models.TextField(blank=True)
    why_choose_text = models.TextField(blank=True)
    phone_number = models.CharField(max_length=30, blank=True)
    whatsapp_number = models.CharField(max_length=30, blank=True)
    email = models.EmailField(blank=True)
    address = models.TextField(blank=True)
    working_hours = models.CharField(max_length=120, blank=True)
    google_map_embed_url = models.URLField(blank=True)
    facebook_url = models.URLField(blank=True)
    instagram_url = models.URLField(blank=True)
    youtube_url = models.URLField(blank=True)
    x_url = models.URLField("X / Twitter URL", blank=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name = "Company Information"
        verbose_name_plural = "Company Information"

    def __str__(self):
        return self.company_name
