from django.core.exceptions import ValidationError
from django.db import models
from django.urls import reverse
from django.utils.text import slugify

from apps.core.utils import CONTENT_FORMAT_CHOICES, FORMAT_PLAIN, render_content


INSTALLABLE_PLATFORM_SPECS = (
    ("windows_download_url", "Windows", "images/icon-windows.svg"),
    ("macos_download_url", "macOS", "images/icon-macos.svg"),
    ("linux_download_url", "Linux", "images/icon-linux.svg"),
    ("source_code_url", "Source code", None),
)

DISTRIBUTION_INSTALLABLE = "installable"
DISTRIBUTION_PACKAGE = "package"
DISTRIBUTION_CHOICES = [
    (DISTRIBUTION_INSTALLABLE, "Installable application"),
    (DISTRIBUTION_PACKAGE, "Package (external / non-installable)"),
]


class MainProduct(models.Model):
    name = models.CharField(max_length=200)
    slug = models.SlugField(unique=True, blank=True)
    short_description = models.CharField(max_length=300)
    description = models.TextField()
    description_format = models.CharField(
        max_length=20, choices=CONTENT_FORMAT_CHOICES, default=FORMAT_PLAIN
    )
    image = models.ImageField(upload_to="products/main/", blank=True, null=True)
    distribution = models.CharField(
        max_length=20,
        choices=DISTRIBUTION_CHOICES,
        default=DISTRIBUTION_INSTALLABLE,
        help_text="Only affects how releases appear on the public site.",
    )
    order = models.PositiveIntegerField(default=0)
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["order", "name"]
        verbose_name = "Main Product"
        verbose_name_plural = "Main Products"

    @property
    def rendered_description(self):
        return render_content(self.description, self.description_format)

    def __str__(self):
        return self.name

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

    def get_absolute_url(self):
        return reverse("products:main_product_detail", kwargs={"main_slug": self.slug})

    def get_versions_archive_url(self):
        return reverse(
            "products:main_product_versions",
            kwargs={"main_slug": self.slug},
        )


class SubProduct(models.Model):
    DISTRIBUTION_INSTALLABLE = DISTRIBUTION_INSTALLABLE
    DISTRIBUTION_PACKAGE = DISTRIBUTION_PACKAGE
    DISTRIBUTION_CHOICES = DISTRIBUTION_CHOICES

    main_product = models.ForeignKey(
        MainProduct,
        on_delete=models.CASCADE,
        related_name="sub_products",
    )
    name = models.CharField(max_length=200)
    slug = models.SlugField(blank=True)
    short_description = models.CharField(max_length=300)
    description = models.TextField()
    description_format = models.CharField(
        max_length=20, choices=CONTENT_FORMAT_CHOICES, default=FORMAT_PLAIN
    )
    image = models.ImageField(upload_to="products/sub/", blank=True, null=True)
    logo = models.ImageField(upload_to="products/sub_logos/", blank=True, null=True, help_text="Small logo shown as a corner badge on homepage product cards.")
    order = models.PositiveIntegerField(default=0)
    is_active = models.BooleanField(default=True)
    show_on_homepage = models.BooleanField(default=False, help_text="Display this sub-product in the homepage Products section.")
    homepage_order = models.PositiveIntegerField(default=0, help_text="Order within the homepage Products section.")
    distribution = models.CharField(
        max_length=20,
        choices=DISTRIBUTION_CHOICES,
        default=DISTRIBUTION_INSTALLABLE,
        help_text="Only affects how releases appear on the public site.",
    )
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["order", "name"]
        unique_together = [["main_product", "slug"]]
        verbose_name = "Sub Product"
        verbose_name_plural = "Sub Products"

    @property
    def rendered_description(self):
        return render_content(self.description, self.description_format)

    def __str__(self):
        return f"{self.main_product.name} › {self.name}"

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

    def get_absolute_url(self):
        return reverse(
            "products:sub_product_detail",
            kwargs={
                "main_slug": self.main_product.slug,
                "sub_slug": self.slug,
            },
        )

    def get_versions_archive_url(self):
        return reverse(
            "products:sub_product_versions",
            kwargs={
                "main_slug": self.main_product.slug,
                "sub_slug": self.slug,
            },
        )


class Article(models.Model):
    main_product = models.ForeignKey(
        MainProduct,
        on_delete=models.CASCADE,
        related_name="articles",
        null=True,
        blank=True,
    )
    sub_product = models.ForeignKey(
        SubProduct,
        on_delete=models.CASCADE,
        related_name="articles",
        null=True,
        blank=True,
    )
    badge = models.CharField(
        max_length=100,
        blank=True,
        help_text="Optional label shown above the article title (e.g. 'Overview', 'Note').",
    )
    title = models.CharField(max_length=300)
    description = models.TextField()
    description_format = models.CharField(
        max_length=20, choices=CONTENT_FORMAT_CHOICES, default=FORMAT_PLAIN
    )
    citation_count_display = models.PositiveSmallIntegerField(
        null=True,
        blank=True,
        help_text="Optional number shown in the dashed citation circle badge. Leave blank to show an empty circle (if a label is set) or hide the badge entirely.",
    )
    citation_count_label = models.CharField(
        max_length=50,
        blank=True,
        default="",
        help_text="Optional label shown below the number in the citation circle badge (e.g. 'cited'). Leave blank to show no label.",
    )
    order = models.PositiveIntegerField(default=0)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["order", "title"]
        verbose_name = "Article"
        verbose_name_plural = "Articles"
        constraints = [
            models.CheckConstraint(
                check=(
                    models.Q(sub_product__isnull=False, main_product__isnull=True)
                    | models.Q(sub_product__isnull=True, main_product__isnull=False)
                ),
                name="article_exactly_one_parent",
            ),
        ]

    @property
    def rendered_description(self):
        return render_content(self.description, self.description_format)

    def clean(self):
        super().clean()
        has_main = self.main_product_id is not None
        has_sub = self.sub_product_id is not None
        if has_main == has_sub:
            raise ValidationError(
                "An article must belong to exactly one main product or sub-product."
            )

    def __str__(self):
        return self.title


class SubProductVersion(models.Model):
    main_product = models.ForeignKey(
        MainProduct,
        on_delete=models.CASCADE,
        related_name="versions",
        null=True,
        blank=True,
    )
    sub_product = models.ForeignKey(
        SubProduct,
        on_delete=models.CASCADE,
        related_name="versions",
        null=True,
        blank=True,
    )
    version = models.CharField(max_length=80)
    is_featured_stable = models.BooleanField(
        default=False,
        help_text="Highlighted as the main release on the product page.",
    )
    windows_download_url = models.URLField(blank=True)
    macos_download_url = models.URLField(blank=True)
    linux_download_url = models.URLField(blank=True)
    source_code_url = models.URLField(blank=True)
    package_resource_url = models.URLField(
        blank=True,
        help_text="Shown on the site when set. For Resources it is the main Open link; for Installable it appears under the platform grid.",
    )
    release_notes = models.TextField(blank=True)
    is_active = models.BooleanField(default=True)
    order = models.PositiveIntegerField(default=0)

    class Meta:
        ordering = ["order", "version", "pk"]
        verbose_name = "Release version"
        verbose_name_plural = "Release versions"
        constraints = [
            models.CheckConstraint(
                check=(
                    models.Q(sub_product__isnull=False, main_product__isnull=True)
                    | models.Q(sub_product__isnull=True, main_product__isnull=False)
                ),
                name="release_version_exactly_one_parent",
            ),
            models.UniqueConstraint(
                fields=["sub_product", "version"],
                condition=models.Q(sub_product__isnull=False),
                name="release_version_unique_sub_product_version",
            ),
            models.UniqueConstraint(
                fields=["main_product", "version"],
                condition=models.Q(main_product__isnull=False),
                name="release_version_unique_main_product_version",
            ),
        ]

    def clean(self):
        super().clean()
        has_main = self.main_product_id is not None
        has_sub = self.sub_product_id is not None
        if has_main == has_sub:
            raise ValidationError(
                "A release version must belong to exactly one main product or sub-product."
            )

    def __str__(self):
        parent = self.sub_product or self.main_product
        return f"{parent.name} v{self.version}"

    def install_urls_for_specs(self, specs):
        out = []
        for field_name, label, icon in specs:
            url = getattr(self, field_name, "") or ""
            if url.strip():
                out.append({"field": field_name, "label": label, "icon": icon, "url": url})
        return out

    def has_any_install_asset(self):
        return any(
            (getattr(self, f[0]) or "").strip()
            for f in INSTALLABLE_PLATFORM_SPECS
        )

    def has_package_link(self):
        return bool((self.package_resource_url or "").strip())


class ArticleSection(models.Model):
    article = models.ForeignKey(
        Article,
        on_delete=models.CASCADE,
        related_name="sections",
    )
    title = models.CharField(max_length=200)
    value = models.TextField()
    value_format = models.CharField(
        max_length=20, choices=CONTENT_FORMAT_CHOICES, default=FORMAT_PLAIN
    )
    order = models.PositiveIntegerField(default=0)

    class Meta:
        ordering = ["order"]
        verbose_name = "Article Section"
        verbose_name_plural = "Article Sections"

    @property
    def rendered_value(self):
        return render_content(self.value, self.value_format)

    def __str__(self):
        return f"{self.article.title} › {self.title}"


class ArticleCitation(models.Model):
    article = models.ForeignKey(
        Article,
        on_delete=models.CASCADE,
        related_name="citations",
    )
    text = models.TextField(help_text="Full citation text.")
    url = models.URLField(blank=True, help_text="Optional link to the cited source.")
    order = models.PositiveIntegerField(default=0)

    class Meta:
        ordering = ["order", "pk"]
        verbose_name = "Article Citation"
        verbose_name_plural = "Article Citations"

    def __str__(self):
        return f"{self.article.title} — citation {self.order or self.pk}"
