
#lsx 这个文件里的是博客相关的数据模型，定义了博客系统中所有的数据表结构

from django.db import models
from django.contrib.auth import get_user_model
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.utils import timezone
from markdownx.models import MarkdownxField
from django.utils.functional import cached_property

#lsx 友情链接的展示类型选择，用于控制链接在哪些页面显示
class LinkShowType(models.IntegerChoices):
    I = 1, _('首页')
    L = 2, _('全部页面')
    A = 3, _('文章页面')

#lsx 所有模型的基类，包含通用字段，避免重复代码
class BaseModel(models.Model):
    id = models.AutoField(primary_key=True)  #lsx 主键，自动递增
    creation_time = models.DateTimeField(_('creation time'), default=timezone.now)  #lsx 创建时间
    last_modify_time = models.DateTimeField(_('modify time'), default=timezone.now)  #lsx 最后修改时间

    #lsx 重写保存方法，自动处理slug字段（用于生成友好的URL）
    def save(self, *args, **kwargs):
        #lsx 如果是更新文章浏览量，直接更新数据库，避免触发其他逻辑
        if 'update_fields' in kwargs and kwargs['update_fields'] == {'views'}:
            super().save(update_fields=['views'])
        else:
            #lsx 更新修改时间
            self.last_modify_time = timezone.now()
            #lsx 如果有slug字段，自动从标题或名称生成slug
            if hasattr(self, 'slug') and hasattr(self, 'name') and not self.slug:
                self.slug = self.name.replace(' ', '-')
            if hasattr(self, 'slug') and hasattr(self, 'title') and not self.slug:
                self.slug = self.title.replace(' ', '-')
            super().save(*args, **kwargs)

    #lsx 获取完整的URL地址，包含域名
    def get_full_url(self):
        site = get_current_site().domain
        url = "https://{site}{path}".format(site=site, path=self.get_absolute_url())
        return url

    abstract = True  #lsx 这是一个抽象基类，不会创建数据库表

    #lsx 子类必须实现这个方法，返回对象的URL
    def get_absolute_url(self):
        raise NotImplementedError("该模型未实现get_absolute_url方法")

#lsx 文章模型，博客的核心内容
class Article(BaseModel):
    #lsx 文章状态选择：草稿或已发布
    STATUS = (('d', _('Draft')), ('p', _('Published')))
    #lsx 评论状态选择：开放或关闭
    COMMENT_STATUS = (('o', _('Open')), ('c', _('Close')))
    #lsx 内容类型选择：文章或页面
    TYPE = (('a', _('Article')), ('p', _('Page')))

    title = models.CharField(_('title'), max_length=200, unique=True)  #lsx 文章标题
    body = MarkdownxField(_('body'))  #lsx 文章正文，支持Markdown格式
    pub_time = models.DateTimeField(
        _('publish time'), blank=False, null=False, default=timezone.now)  #lsx 发布时间
    status = models.CharField(
        _('status'), max_length=1, choices=STATUS, default='p')  #lsx 文章状态
    comment_status = models.CharField(
        _('comment status'), max_length=1, choices=COMMENT_STATUS, default='o')  #lsx 评论状态
    type = models.CharField(_('type'), max_length=1, choices=TYPE, default='a')  #lsx 内容类型
    views = models.PositiveIntegerField(_('views'), default=0)  #lsx 浏览次数
    author = models.ForeignKey(
        get_user_model(),
        verbose_name=_('author'),
        blank=False,
        null=False,
        on_delete=models.CASCADE)  #lsx 作者，关联用户表
    article_order = models.IntegerField(
        _('order'), blank=False, null=False, default=0)  #lsx 文章排序
    show_toc = models.BooleanField(_('show toc'), blank=False, null=False, default=False)  #lsx 是否显示目录
    category = models.ForeignKey(
        'Category',
        verbose_name=_('category'),
        on_delete=models.CASCADE,
        blank=False,
        null=False)  #lsx 分类，关联分类表
    tags = models.ManyToManyField('Tag', verbose_name=_('tag'), blank=True)  #lsx 标签，多对多关系

    #lsx 将文章内容转换为字符串
    def __str__(self):
        #lsx 返回文章标题作为对象的字符串表示
        return self.title

    class Meta:
        ordering = ['-article_order', '-pub_time']  #lsx 按排序字段和发布时间倒序排列
        verbose_name = _('article')  #lsx 在管理后台显示的名称
        verbose_name_plural = verbose_name  #lsx 复数形式
        get_latest_by = 'id'  #lsx 获取最新记录的依据

    #lsx 获取文章的URL
    def get_absolute_url(self):
        if self.type == 'a':
            return reverse('blog:article_detail', kwargs={'article_id': self.pk})
        else:
            return reverse('blog:page_detail', kwargs={'page_id': self.pk})

    #lsx 获取分类树，缓存10小时
    @cached_property
    def toc(self):
        return self.markdown.toc

    #lsx 保存文章，更新修改时间
    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)

    #lsx 增加文章浏览次数
    def viewed(self):
        self.views += 1
        self.save(update_fields=['views'])

    #lsx 获取文章评论列表
    @cached_property
    def comment_list(self):
        comment_list = self.comment_set.filter(is_enable=True)
        return comment_list

    #lsx 获取文章在管理后台的URL
    def get_admin_url(self):
        return reverse('admin:blog_article_change', kwargs={'object_id': self.id})

    #lsx 获取下一篇文章，缓存100分钟
    def next_article(self):
        return Article.objects.filter(id__gt=self.id, type='a', status='p').order_by('id').first()

    #lsx 获取上一篇文章，缓存100分钟
    def prev_article(self):
        return Article.objects.filter(id__lt=self.id, type='a', status='p').order_by('-id').first()

    #lsx 获取文章中的第一张图片URL
    def body_to_string(self):
        import re
        from bs4 import BeautifulSoup
        pattern = re.compile(r'<img.*?src=["\'](.*?)["\'].*?>')
        soup = BeautifulSoup(self.body, 'html.parser')
        for img in soup.find_all('img'):
            if img.get('src'):
                return img.get('src')
        return ''

#lsx 分类模型
class Category(BaseModel):
    name = models.CharField(_('category name'), max_length=30, unique=True)  #lsx 分类名称
    parent_category = models.ForeignKey(
        'self',
        verbose_name=_('parent category'),
        blank=True,
        null=True,
        on_delete=models.CASCADE)  #lsx 父分类，支持多级分类
    slug = models.SlugField(default='no-slug', max_length=60, blank=True)  #lsx URL友好的标识符
    index = models.IntegerField(default=0, verbose_name=_('index'))  #lsx 排序索引

    class Meta:
        ordering = ['-index']  #lsx 按索引倒序排列
        verbose_name = _('category')  #lsx 在管理后台显示的名称
        verbose_name_plural = verbose_name  #lsx 复数形式

    #lsx 获取分类的URL
    def get_absolute_url(self):
        return reverse('blog:category_detail', kwargs={'category_id': self.pk})

    #lsx 返回分类名称作为对象的字符串表示
    def __str__(self):
        return self.name

    #lsx 获取分类树，缓存10小时
    @cached_property
    def get_parent_category(self):
        return self.parent_category

    #lsx 获取子分类列表，缓存10小时
    @cached_property
    def get_sub_category(self):
        return Category.objects.filter(parent_category=self)

#lsx 标签模型
class Tag(BaseModel):
    name = models.CharField(_('tag name'), max_length=30, unique=True)  #lsx 标签名称
    slug = models.SlugField(default='no-slug', max_length=60, blank=True)  #lsx URL友好的标识符

    #lsx 返回标签名称作为对象的字符串表示
    def __str__(self):
        return self.name

    #lsx 获取标签的URL
    def get_absolute_url(self):
        return reverse('blog:tag_detail', kwargs={'tag_id': self.pk})

    #lsx 获取标签下的文章数量，缓存10小时
    @cached_property
    def article_count(self):
        return Article.objects.filter(tags=self, status='p').count()

    class Meta:
        ordering = ['name']  #lsx 按名称排序
        verbose_name = _('tag')  #lsx 在管理后台显示的名称
        verbose_name_plural = verbose_name  #lsx 复数形式

#lsx 友情链接模型
class Links(BaseModel):
    name = models.CharField(_('link name'), max_length=30, unique=True)  #lsx 链接名称
    link = models.URLField(_('link'))  #lsx 链接地址
    sequence = models.IntegerField(_('order'), unique=True)  #lsx 排序序号
    is_enable = models.BooleanField(
        _('is show'), default=True, blank=False, null=False)  #lsx 是否启用
    show_type = models.IntegerField(
        _('show type'), choices=LinkShowType.choices, default=LinkShowType.I)  #lsx 显示类型
    creation_time = models.DateTimeField(_('creation time'), default=timezone.now)  #lsx 创建时间
    last_mod_time = models.DateTimeField(_('modify time'), default=timezone.now)  #lsx 修改时间

    class Meta:
        ordering = ['sequence']  #lsx 按序号排序
        verbose_name = _('link')  #lsx 在管理后台显示的名称
        verbose_name_plural = verbose_name  #lsx 复数形式

    #lsx 返回链接名称作为对象的字符串表示
    def __str__(self):
        return self.name

#lsx 侧边栏模型
class SideBar(BaseModel):
    name = models.CharField(_('title'), max_length=100)  #lsx 侧边栏标题
    content = models.TextField(_('content'))  #lsx 侧边栏内容（支持HTML）
    sequence = models.IntegerField(_('order'), unique=True)  #lsx 排序序号
    is_enable = models.BooleanField(_('is enable'), default=True)  #lsx 是否启用
    creation_time = models.DateTimeField(_('creation time'), default=timezone.now)  #lsx 创建时间
    last_mod_time = models.DateTimeField(_('modify time'), default=timezone.now)  #lsx 修改时间

    class Meta:
        ordering = ['sequence']  #lsx 按序号排序
        verbose_name = _('sidebar')  #lsx 在管理后台显示的名称
        verbose_name_plural = verbose_name  #lsx 复数形式

    #lsx 返回侧边栏标题作为对象的字符串表示
    def __str__(self):
        return self.name

#lsx 网站设置模型
class BlogSettings(BaseModel):
    site_name = models.CharField(
        _('site name'), max_length=100, null=False, blank=False, default='')  #lsx 网站名称
    site_description = models.CharField(
        _('site description'), max_length=1000, null=False, blank=False, default='')  #lsx 网站描述
    site_seo_description = models.CharField(
        _('site seo description'), max_length=1000, null=False, blank=False, default='')  #lsx SEO描述
    site_keywords = models.CharField(
        _('site keywords'), max_length=1000, null=False, blank=False, default='')  #lsx 网站关键词
    article_sub_length = models.IntegerField(_('article sub length'), default=300)  #lsx 文章摘要长度
    sidebar_article_count = models.IntegerField(_('sidebar article count'), default=10)  #lsx 侧边栏文章数量
    sidebar_comment_count = models.IntegerField(_('sidebar comment count'), default=5)  #lsx 侧边栏评论数量
    article_comment_count = models.IntegerField(_('article comment count'), default=5)  #lsx 文章页评论数量
    show_google_adsense = models.BooleanField(_('show adsense'), default=False)  #lsx 是否显示广告
    google_adsense_codes = models.TextField(
        _('adsense code'), max_length=2000, null=True, blank=True, default='')  #lsx 广告代码
    open_site_comment = models.BooleanField(_('open site comment'), default=True)  #lsx 是否开放评论
    comment_need_review = models.BooleanField(
        '评论是否需要审核', default=False, null=False)  #lsx 评论是否需要审核
    global_header = models.TextField("公共头部", null=True, blank=True, default='')  #lsx 公共头部HTML
    global_footer = models.TextField("公共尾部", null=True, blank=True, default='')  #lsx 公共尾部HTML
    icp_number = models.CharField(
        _('ICP license number'), max_length=100, blank=True, default='')  #lsx ICP备案号
    show_icp_number = models.BooleanField(
        '是否显示公安备案号', default=False, null=False)  #lsx 是否显示公安备案号
    security_icp_number = models.CharField(
        _('security ICP license number'), max_length=100, blank=True, default='')  #lsx 公安备案号
    analytics_code = models.TextField(
        _('analytics code'), null=True, blank=True, default='')  #lsx 网站统计代码（如百度统计、Google Analytics等）

    class Meta:
        verbose_name = _('Website configuration')  #lsx 在管理后台显示的名称
        verbose_name_plural = verbose_name  #lsx 复数形式

    #lsx 返回网站名称作为对象的字符串表示
    def __str__(self):
        return self.site_name

    #lsx 验证模型数据
    def clean(self):
        super().clean()

    #lsx 保存设置，更新修改时间
    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)
        from djangoblog.utils import cache
        cache.delete('site_config')

    class Meta:
        verbose_name = _('Website configuration')  #lsx 在管理后台显示的名称
        verbose_name_plural = verbose_name  #lsx 复数形式

# 需要导入get_current_site
from django.contrib.sites.shortcuts import get_current_site
