# apps/articles/models/article.py
import random
import string

from apps.core.models.base import SEOMixin
from apps.core.models.global_dns import GlobalDNSConfig
from apps.core.models.site_settings import SiteSettings
from apps.core.models.url_style import UrlStyle
from apps.core.utils.hash import hash_id
from django.contrib.auth.models import User
from django.db import models
from django.utils.translation import gettext_lazy as _

from .category import Category  # 相对导入当前包的模型
from .common import CreateCommon
from .wiki import Wiki


class Article(SEOMixin, CreateCommon):
    # 路径处理使用配置中的模板目录
    # 动态获取模板目录路径

    subtitle = models.CharField(max_length=200, verbose_name="副标题", blank=True, default="")

    author = models.CharField(max_length=200, verbose_name="作者", blank=True, default="")

    source = models.CharField(
        max_length=200,
        verbose_name="来源",
        default="本站原创",
        help_text="请根据实际情况填写内容来源。",
    )

    category = models.ForeignKey(
        "content.category",
        on_delete=models.CASCADE,
        verbose_name=_("所属分类"),
        limit_choices_to={"content_type__model": "article"},
    )

    related_tags = models.ManyToManyField(  # 修复拼写错误
        "content.tag",
        blank=True,
        related_name="articles",
        verbose_name="tags词",
        help_text="与模型 Keyword 相关联。",
    )
    filtered_tags = models.ManyToManyField(
        "content.Tag",
        blank=True,
        related_name="filtered_articles",
        verbose_name="动态匹配标签",
        help_text="通过标签标题模糊匹配关联的标签",
    )
    # 在模板中生成缩略图URL
    # {{ article.thumb_image.url|replace:"origin","thumbnail"|replace:".", "_336x208." }}
    editor = models.ForeignKey(  # 修复拼写错误
        User,
        on_delete=models.CASCADE,
        verbose_name="内容编辑",
        blank=True,
        related_name="edited_articles",
    )
    related_wikis = models.ManyToManyField(
        Wiki,
        blank=True,
        related_name="articles",
        verbose_name="相关百科",
        help_text="选择相关百科，如果没有可以添加",
    )
    cut_keywords = models.ManyToManyField(
        "content.cutword", related_name="articles", blank=True, verbose_name="标题分词"
    )
    from django.contrib.contenttypes.fields import GenericRelation

    comments = GenericRelation("comments.Comment")

    class Meta:
        verbose_name = "文章"
        verbose_name_plural = "文章列表"
        ordering = ["-update_time", "-id"]
        indexes = [
            # 更新文章发布状态的重要索引
            models.Index(fields=["status", "category_id"], name="article_status_category"),
            models.Index(
                fields=["category_id", "status", "create_time"], name="article_cate_status_ctime"
            ),
            # 复合索引（时间字段统一降序）
            models.Index(
                fields=["category", "status", "-update_time"], name="article_cate_status_utime_desc"
            ),
            models.Index(
                fields=["category", "status", "-create_time"], name="article_cate_status_ctime_desc"
            ),
            # 状态+降序时间组合
            models.Index(fields=["status", "-update_time"], name="article_status_utime_desc"),
            models.Index(fields=["status", "-create_time"], name="article_status_ctime_desc"),
            # 独立时间索引（显式降序）
            models.Index(fields=["-create_time"], name="article_ctime_desc"),
            models.Index(fields=["-update_time"], name="article_utime_desc"),
            # 业务索引（修正字段分隔符）
            models.Index(fields=["hits", "status"], name="article_hits_status"),
            models.Index(fields=["category", "hits", "status"], name="article_cate_hits_status"),
            models.Index(fields=["title"], name="article_title"),
            models.Index(
                fields=["category", "-update_time", "-id"], name="article_cate_utime_id_desc"
            ),
            models.Index(fields=["-update_time", "-id"], name="article_utime_id_desc"),
            models.Index(fields=["category"], name="article_category_idx"),
        ]

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

    def save(self, *args, **kwargs):
        """
        重写save方法，在保存时自动更新分词关联
        """
        if self.pk:
            old_instance = Article.objects.get(pk=self.pk)
            title_changed = old_instance.title != self.title
        else:
            title_changed = True

        # 先保存基础数据
        super().save(*args, **kwargs)

        # # 如果title发生变化或是没有相关分词的时候更新分词
        if title_changed or not self.cut_keywords.exists():
            self.update_cut_keywords()

    def update_cut_keywords(self):
        """
        更新文章的分词关联
        """
        from apps.content.utils.extract_keywords import extract_keywords

        from .cut_words import CutWord

        # 清空旧的分词关联
        self.cut_keywords.clear()

        # 提取标题分词
        keywords = extract_keywords(self.title)
        if not keywords:
            return

        # 创建或获取CutWord记录，并建立关联
        for keyword in keywords:
            cut_word, created = CutWord.objects.get_or_create(title=keyword)
            self.cut_keywords.add(cut_word)

    def get_absolute_url(self):
        """
        基于栏目配置生成文章详情页URL
        支持五类路径风格：model/slug/moreslug/random/custom
        """
        # 获取栏目配置参数（需在Category模型中实现配置存储）
        url_config = UrlStyle.get_solo()
        dns_config = GlobalDNSConfig.get_solo()
        dns_host = ""
        subdomain_flag = Category.check_subdomain_flag()
        # 从配置获取最小长度和最大长度
        min_len, max_len = map(int, url_config.random_length.split("-"))
        # 生成闭区间随机长度（包含两端点）
        random_int = random.randint(min_len, max_len)
        try:
            if (
                dns_config.status == "enabled"
                and not dns_config.primary_domain == ""
                and dns_config.dns_type == "content_to_home"
            ):
                dns_host = f"//{hash_id(self.pk)}.{dns_config.primary_domain}"
                return f"{dns_host}"

        except Exception as e:
            print(f"article.get_absolute_url Error:{e}")
        # 生成包含大小写字母和数字的 6 位随机字符串
        random_str = "".join(
            random.choices(
                string.ascii_lowercase + string.digits, k=random_int  # 62 种字符 (a-z, A-Z, 0-9)
            )
        )
        slug_chain = ""
        if url_config.url_front == "moreslug":
            slug_chain = "/".join([c.slug for c in self.category.get_ancestors(include_self=True)])

        # 根据栏目路径风格生成基础路径
        base_path = self._get_base_path(
            url_config.url_front, slug_chain, url_config.custom_article, random_str
        )
        # 生成文章标识符
        identifier = self._get_article_identifier(url_config.url_mid)
        if dns_config.status == "enabled" and dns_config.dns_type == "global_site":
            dns_random = "".join(
                random.choices(
                    string.ascii_lowercase + string.digits,  # 62 种字符 (a-z, A-Z, 0-9)
                    k=random_int,
                )
            )
            dns_host = f"//{dns_random}.{dns_config.primary_domain}"
        elif self.category.subdomain:
            dns_host = self.category.get_absolute_url()
            return f"{dns_host}/{base_path}{identifier}{url_config.url_suffix}"
        elif subdomain_flag:
            site_config = SiteSettings.get_solo()
            dns_host = "//" + site_config.site_url.split("//")[1]
            return f"{dns_host}/{base_path}{identifier}{url_config.url_suffix}"
        # 拼接完整URL
        return (
            f"{dns_host}/{base_path}{identifier}{url_config.url_suffix}"
            if dns_config.status == "enabled"
            else f"/{base_path}{identifier}{url_config.url_suffix}"
        )

    def get_active(self):
        url_config = UrlStyle.get_solo()
        url_front = url_config.url_front
        try:
            if url_front == "model":
                return "article"
            else:
                return self.category.slug
        except Exception as e:
            print("获得文章模型active状态标识出错：", e)
            return "article"

    def _get_base_path(self, front, slug_chain, custom_article, random_str):
        """根据前端配置生成基础路径"""
        base_map = {
            "model": f"{self._meta.model_name}/",
            "slug": f"{self.category.slug}/",
            "moreslug": f"{slug_chain}/",  # 根据实际情况调整多级slug
            "random": f"{random_str}/",
            "custom": f"{custom_article}/" if custom_article else f"{self.category.slug}/",
        }
        return base_map.get(front, "")

    def _get_article_identifier(self, mid_type):
        """生成文章标识"""
        if mid_type == "id":
            return str(self.pk)
        elif mid_type == "hash":
            return hash_id(self.pk)  # 需实现哈希生成方法
        return ""

    @classmethod
    def get_related_articles_by_tag(cls, tag):
        """
        通过标签获取相关文章（优化版）- 类方法
        articles, source = Article.get_related_articles_by_tag(tag)
        """
        # 检查缓存
        if tag.is_recently_matched("article", days=30):
            related_articles = tag.filtered_articles.all().order_by("?")
            if related_articles.exists():
                return related_articles, "related"

        # 使用分词中间表进行高效查询
        related_articles = (
            cls.objects.filter(
                cut_keywords__in=tag.cut_keywords.all(), status=99  # 关键优化：通过中间表关联
            )
            .select_related("category")
            .prefetch_related("cut_keywords")
            .distinct()
            .order_by("-update_time")[:20]
        )

        if related_articles.exists():
            # 更新缓存
            tag.filtered_articles.set(related_articles)
            tag.update_filtered_time("article")
            return related_articles, "related"

        return cls.objects.none(), "none"
