from pathlib import PurePosixPath
from urllib.parse import urlsplit

from django.core.exceptions import ValidationError
from django.db import models


class SiteSettings(models.Model):
    """Images and labels shared across the public archive website."""

    home_banner_image = models.ImageField(
        upload_to="site/banners/",
        blank=True,
        null=True,
        help_text="Main banner image used on the homepage.",
    )
    menu_banner_image = models.ImageField(
        upload_to="site/menu/",
        blank=True,
        null=True,
        help_text="Image displayed on the right side of the full-screen menu.",
    )

    class Meta:
        verbose_name = "Site settings"
        verbose_name_plural = "Site settings"

    def __str__(self):
        return "Archive site settings"

    def save(self, *args, **kwargs):
        # This table is intentionally a singleton.
        self.pk = 1
        super().save(*args, **kwargs)


class ArchiveCategory(models.Model):
    """Top-level archive section shown on the homepage."""

    TYPE_TIMELINE = "timeline"
    TYPE_3D_MODELS = "3d_models"
    TYPE_CHOICES = (
        (TYPE_TIMELINE, "Timeline grouped by date"),
        (TYPE_3D_MODELS, "3D models - direct item listing"),
    )

    name = models.CharField(max_length=120, unique=True)
    slug = models.SlugField(max_length=140, unique=True)
    category_type = models.CharField(
        max_length=20,
        choices=TYPE_CHOICES,
        default=TYPE_TIMELINE,
        help_text=(
            "Standard categories open a chronological timeline. The 3D models "
            "option opens the items directly and does not display dates."
        ),
    )

    banner_image = models.ImageField(
        upload_to="archive_categories/", blank=True, null=True
    )
    timeline_banner_image = models.ImageField(
        upload_to="archive_categories/timeline_banners/",
        blank=True,
        null=True,
        help_text="Hero/banner image used on the category timeline page.",
    )
    decade_banner_image = models.ImageField(
        upload_to="archive_categories/decade_banners/",
        blank=True,
        null=True,
        help_text="Hero/banner image used on the category decade items page.",
    )

    description = models.TextField(blank=True)

    class Meta:
        ordering = ["name"]

    def __str__(self):
        return self.name

    @property
    def is_3d_models(self):
        return self.category_type == self.TYPE_3D_MODELS


class PortfolioItem(models.Model):
    """Main archive item."""

    IMAGE_VIEWER_EXTENSIONS = {".avif", ".gif", ".jpeg", ".jpg", ".png", ".webp"}
    MODEL_VIEWER_EXTENSIONS = {".glb", ".gltf"}

    category = models.ForeignKey(
        ArchiveCategory, on_delete=models.CASCADE, related_name="items"
    )

    inventory_id = models.CharField(max_length=80, unique=True, blank=True)
    title = models.CharField(max_length=300)

    year = models.PositiveIntegerField(blank=True, null=True)
    date_text = models.CharField(max_length=120, blank=True)
    place_created = models.CharField(max_length=200, blank=True)

    detail_banner_image = models.ImageField(
        upload_to="portfolio/detail_banners/",
        blank=True,
        null=True,
        help_text="Optional banner image used on this item detail page.",
    )
    model_viewer_url = models.URLField(
        max_length=1000,
        blank=True,
        help_text=(
            "Required for items in the 3D Models category. A direct image URL "
            "opens in the built-in zoom viewer; GLB/GLTF files use the interactive "
            "3D viewer; other URLs are embedded as an external viewer."
        ),
    )

    description = models.TextField(blank=True)
    physical_description = models.TextField(blank=True)
    materials_and_techniques = models.TextField(blank=True)
    dimensions = models.CharField(max_length=200, blank=True)
    signature = models.TextField(blank=True)
    marks_and_inscriptions = models.TextField(blank=True)
    bibliographic_reference = models.TextField(blank=True)

    is_featured = models.BooleanField(default=False)

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["year", "title"]

    def __str__(self):
        return self.title

    def clean(self):
        super().clean()
        if self.category_id and self.category.is_3d_models and not self.model_viewer_url:
            raise ValidationError(
                {"model_viewer_url": "A viewer URL is required for 3D model items."}
            )

    @property
    def decade(self):
        if self.year:
            return (self.year // 10) * 10
        return None

    @property
    def is_3d_model_item(self):
        return bool(self.category_id and self.category.is_3d_models)

    @property
    def model_viewer_type(self):
        """Return image, model, or embed according to the configured URL."""
        if not self.model_viewer_url:
            return ""

        suffix = PurePosixPath(urlsplit(self.model_viewer_url).path).suffix.lower()
        if suffix in self.IMAGE_VIEWER_EXTENSIONS:
            return "image"
        if suffix in self.MODEL_VIEWER_EXTENSIONS:
            return "model"
        return "embed"


class PortfolioMedia(models.Model):
    item = models.ForeignKey(
        "PortfolioItem", on_delete=models.CASCADE, related_name="media_items"
    )
    file = models.ImageField(upload_to="portfolio/media/")
    title = models.CharField(max_length=255, blank=True)
    is_primary = models.BooleanField(default=False)

    def __str__(self):
        return self.title or f"Media for {self.item.title}"


class PortfolioMediaAnnotation(models.Model):
    media = models.ForeignKey(
        PortfolioMedia, on_delete=models.CASCADE, related_name="annotations"
    )
    title = models.CharField(max_length=255)
    description = models.TextField(blank=True)

    x_percent = models.DecimalField(max_digits=5, decimal_places=2)
    y_percent = models.DecimalField(max_digits=5, decimal_places=2)

    media_file = models.FileField(
        upload_to="portfolio/annotation_media/", blank=True, null=True
    )
    link = models.URLField(blank=True)

    sort_order = models.PositiveIntegerField(default=0)
    is_active = models.BooleanField(default=True)

    def __str__(self):
        return f"{self.title} ({self.media})"
