from django.db.models import CASCADE, CharField, FileField, ForeignKey, ManyToManyField, BooleanField
from django.utils.translation import gettext_lazy as _
from django_extensions.db.fields import AutoSlugField
from markdown.extensions.toc import TocExtension
from markdown_field import MarkdownField

from core.abstract import NiceModel


class Post(NiceModel):  # type: ignore [django-manager-missing]
    __doc__ = _(
        "Represents a blog post model. "
        "The Post class defines the structure and behavior of a blog post. "
        "It includes attributes for author, title, content, optional file attachment, slug, and associated tags. "
        "The class enforces constraints such as requiring either content or a file attachment but not both simultaneously. "
        "It also supports automatic slug generation based on the title."
    )

    is_publicly_visible = True

    author = ForeignKey(to="vibes_auth.User", on_delete=CASCADE, blank=False, null=False, related_name="posts")
    title = CharField(
        unique=True, max_length=128, blank=False, null=False, help_text=_("post title"), verbose_name=_("title")
    )
    content: MarkdownField = MarkdownField(
        "content",
        extensions=[
            TocExtension(toc_depth=3),
            "pymdownx.arithmatex",
            "pymdownx.b64",
            "pymdownx.betterem",
            "pymdownx.blocks.admonition",
            "pymdownx.blocks.caption",
            "pymdownx.blocks.definition",
            "pymdownx.blocks.details",
            "pymdownx.blocks.html",
            "pymdownx.blocks.tab",
            "pymdownx.caret",
            "pymdownx.critic",
            "pymdownx.emoji",
            "pymdownx.escapeall",
            "pymdownx.extra",
            "pymdownx.fancylists",
            "pymdownx.highlight",
            "pymdownx.inlinehilite",
            "pymdownx.keys",
            "pymdownx.magiclink",
            "pymdownx.mark",
            "pymdownx.pathconverter",
            "pymdownx.progressbar",
            "pymdownx.saneheaders",
            "pymdownx.smartsymbols",
            "pymdownx.snippets",
            "pymdownx.striphtml",
            "pymdownx.superfences",
            "pymdownx.tasklist",
            "pymdownx.tilde",
        ],
        blank=True,
        null=True,
    )
    file = FileField(upload_to="posts/", blank=True, null=True)
    slug = AutoSlugField(populate_from="title", allow_unicode=True, unique=True, editable=False)
    tags = ManyToManyField(to="blog.PostTag", blank=True, related_name="posts")
    meta_description = CharField(max_length=150, blank=True, null=True)
    is_static_page = BooleanField(
        default=False,
        verbose_name=_("is static page"),
        help_text=_("is this a post for a page with static URL (e.g. `/help/delivery`)?"),
    )

    def __str__(self):
        return f"{self.title} | {self.author.first_name} {self.author.last_name}"

    class Meta:
        verbose_name = _("post")
        verbose_name_plural = _("posts")

    def save(self, **kwargs):
        if self.file:
            raise ValueError(_("markdown files are not supported yet - use markdown content instead"))
        if not any([self.file, self.content]) or all([self.file, self.content]):
            raise ValueError(_("a markdown file or markdown content must be provided - mutually exclusive"))
        super().save(**kwargs)


class PostTag(NiceModel):
    """
    Represents a tag associated with a post.

    The PostTag class is used to define and manage tags that can be assigned
    to posts. These tags include an internal identifier and a user-friendly
    display name. The class supports internationalization for both the internal
    identifier and the display name.

    Attributes:
        is_publicly_visible (bool): Determines if the tag is visible publicly.
        tag_name (CharField): An internal tag identifier for the post's tag. It is a required
            field with a maximum length of 255 characters.
        name (CharField): A user-friendly, unique display name for the post's tag
            with a maximum length of 255 characters.

    Meta:
        verbose_name (str): Human-readable singular name of the PostTag model.
        verbose_name_plural (str): Human-readable plural name of the PostTag model.
    """

    is_publicly_visible = True
    posts: "Post"

    tag_name = CharField(
        blank=False,
        null=False,
        max_length=255,
        help_text=_("internal tag identifier for the post tag"),
        verbose_name=_("tag name"),
    )
    name = CharField(
        max_length=255,
        help_text=_("user-friendly name for the post tag"),
        verbose_name=_("tag display name"),
        unique=True,
    )

    def __str__(self):
        return self.tag_name

    class Meta:
        verbose_name = _("post tag")
        verbose_name_plural = _("post tags")
