# views.py
import threading

from apps.content.utils.import_tag import import_tag
from apps.core.cache.conditional_cache_page import (
    conditional_cache_page,
    dynamic_ratelimit,
)
from apps.core.cache.static_cache import StaticCacheManager
from apps.core.decorators import require_security_code
from apps.core.models.ai_config import AiConfig
from apps.core.models.sensitive_config import SensitiveConfig
from apps.core.models.static_settings import StaticSettings
from apps.core.utils.hash import unhash_id
from apps.core.utils.sensitive_filter import SensitiveFilter
from apps.core.views.ai_create import AIcreate
from django.conf import settings
from django.core.exceptions import ValidationError
from django.db.models import Q
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.template.loader import select_template
from django.utils.decorators import method_decorator
from django.views.generic import DetailView
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView

from ..models import Article, Tag
from ..utils.extract_keywords import extract_keywords


@method_decorator(dynamic_ratelimit(settings.RATE_LIMIT_TAG), name="dispatch")
@method_decorator(conditional_cache_page(settings.TAG_TIMEOUT), name="dispatch")
class TagDetailView(DetailView):
    model = Tag
    context_object_name = "tag"
    query_pk_and_slug = True  # 同时支持pk和slug查询
    get_type = 1
    keywords = []

    def get_object(self, **kwargs):
        # 判断路径模式
        if "pk" in self.kwargs:
            try:
                int(self.kwargs["pk"])
                return self._pk()
            except:
                return self._handle_hash()
        return super().get_object()

    def _pk(self):
        return get_object_or_404(Tag, pk=self.kwargs["pk"], status=99)

    def _handle_hash(self):
        return get_object_or_404(Tag, pk=int(unhash_id(self.kwargs["pk"])), status=99)

    def get(self, request, *args, **kwargs):
        filename = request.path_info
        self.static_config = StaticSettings.get_solo()
        manager = StaticCacheManager(self.static_config.high_safe, self.static_config.static_tag)
        self.object = self.get_object()

        # 获取静态内容

        if self.static_config.static_key:
            static_content = manager.get(filename)
            if static_content:
                return HttpResponse(static_content)

            # 执行父类get方法完成常规处理
        response = super().get(request, *args, **kwargs)
        if self.static_config.static_key:
            manager.create(filename, response.render().content.decode("utf-8"))

        return response

    def get_template_names(self):
        # theme = getattr(self.request, 'current_theme', 'default') template
        theme = settings.THEME_NAME
        # 文章个性模板>栏目内容模板>主题模型内容页templates/articles/article_item.html
        # themes/default/templates/articles/article_item.html是最后的容灾收段
        templates = []
        templates.extend(
            [
                f"themes/{theme}/templates/tags/tag_item.html",
                "themes/default/templates/tags/tag_item.html",
            ]
        )
        resolved_template = select_template(
            templates
        ).template.name  # 触发模板加载流程并获取实际路径^5
        self.template_name = resolved_template
        return templates

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        obj = self.object
        from apps.core.models import TagPageConfig  # 注意避免循环导入

        tag_config = TagPageConfig.get_solo()

        if not obj.intro.strip():
            ai_config = AiConfig.get_solo()
            try:
                if ai_config.tag_switch:
                    if ai_config.tag_ai_type == 1:
                        print("网页更新")
                        getintro = AIcreate()
                        obj.intro = getintro.generate_tag_intro(obj)
                        print()
                    else:
                        print("多进程更新")
                        getintro = AIcreate()
                        threading.Thread(
                            target=getintro.generate_tag_intro, args=(obj,), daemon=True
                        ).start()
            except Exception as e:
                print("Tag ai check Error:", e)
        # 敏感词处理
        config = SensitiveConfig.get_solo()
        sensitive_filter = SensitiveFilter(config)
        sensitive_fields = ["title", "intro"]
        if config.filter_switch:
            for field in sensitive_fields:
                # 1. 检查字段是否存在
                if not hasattr(obj, field):
                    continue

                # 2. 获取原始值并处理
                original_value = getattr(obj, field)
                processed_value = sensitive_filter.process_text(original_value)

                setattr(obj, field, processed_value)

        # 获取关联内容
        articles, article_source = self.get_related_articles(obj)

        # 1. 如果 generated_title 为空，则尝试生成
        if not obj.generated_title or not obj.generated_intro:
            obj.save()

        context.update(
            {
                "obj": obj,
                "articles": articles,
                "article_source": article_source,
                "words": self.keywords,
                "tag_config": tag_config,
                # 可扩展其他内容类型（wiki/downloads）
            }
        )
        return context

    def get_related_articles(self, tag):
        """获取与标签相关的文章（三级查询策略）"""
        from apps.core.models import TagPageConfig  # 注意避免循环导入

        tag_config = TagPageConfig.get_solo()
        # 1. 直接关联的文章

        direct_articles = (
            tag.articles.filter(status=99)
            .select_related("category")
            .only(
                "title",
                "meta_description",
                "update_time",
                "pk",
                "hits",
                "category",
                "author",
                "thumb_image",
                "category__name",
                "category__slug",
                "category__id",
            )
            .order_by("-update_time")
        )
        if direct_articles.exists():
            self.keywords.append(tag.title)
            return direct_articles, "direct"
        if tag_config.word_split_enabled:
            # 2. 分词查询
            if not tag.segmented_words:
                self.keywords = extract_keywords(tag.title)
            else:
                self.keywords = tag.segmented_words
                # 检查一个月内是否有缓存结果
                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 = (
                    Article.objects.filter(
                        cut_keywords__in=tag.cut_keywords.all(),  # 关键优化：通过中间表关联
                        status=99,
                    )
                    .select_related("category")
                    .prefetch_related("cut_keywords")
                    .only(
                        "title",
                        "meta_description",
                        "update_time",
                        "pk",
                        "hits",
                        "category",
                        "author",
                        "thumb_image",
                        "category__name",
                        "category__slug",
                        "category__id",
                    )
                    .distinct()
                    .order_by("?")[:20]
                )
                if related_articles.exists():
                    tag.update_filtered_time("article")
                    return related_articles, "related"

            if self.keywords:
                # 如果拆出词了先将这个词保存到tag模型中，以后就不需要再拆了。
                if not tag.segmented_words:
                    tag.segmented_words = self.keywords
                    tag.save()
                # 检查一个月内是否有缓存结果
                if tag.is_recently_matched("article", days=30):
                    related_articles = tag.filtered_articles.all().order_by("?")
                    if related_articles.exists():
                        return related_articles, "related"

                # 如果没有缓存或过期，执行新查询
                q_objects = Q()
                for keyword in self.keywords:
                    q_objects |= Q(title__icontains=keyword)

                related_articles = (
                    Article.objects.filter(q_objects, status=99)
                    .select_related("category")
                    .only(
                        "title",
                        "meta_description",
                        "update_time",
                        "pk",
                        "hits",
                        "category",
                        "author",
                        "cut_keywords",
                        "thumb_image",
                        "category__name",
                        "category__slug",
                        "category__id",
                    )
                    .distinct()
                    .order_by("?")[:20]
                )
                #         related_articles = Article.objects.filter(
                #     cut_keywords__in=tag.cut_keywords.all(),  # 关键优化：通过中间表关联
                #     status=99
                # ).select_related('category').prefetch_related('cut_keywords').only(
                #                 'title', 'meta_description', 'update_time', 'pk', 'hits',
                #                 'category', 'author', 'thumb_image', 'category__name',
                #                 'category__slug',
                #                 'category__id').distinct().order_by('-update_time')[:20]

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

                    # 优化：批量检查并更新缺失分词的文章
                    articles_needing_update = []
                    for article in related_articles:
                        # 检查文章是否有关联的分词。使用 exists() 比检查 .all() 更高效。
                        if not article.cut_keywords.exists():
                            articles_needing_update.append(article.id)

                    # 如果存在需要更新的文章，批量处理
                    if articles_needing_update:
                        # 在事务中批量更新，确保数据一致性[1,2](@ref)
                        from django.db import transaction

                        with transaction.atomic():
                            # 再次查询这些文章，使用 select_for_update 防止并发修改（可选，根据并发需求决定）
                            articles_to_update = Article.objects.filter(
                                id__in=articles_needing_update
                            )
                            for article in articles_to_update:
                                article.update_cut_keywords()

                    return related_articles, "related"
        else:
            return Article.objects.none, "random"
        # if self.keywords:
        #     q_objects = Q()
        #     for keyword in self.keywords:
        #         q_objects |= Q(title__icontains=keyword)

        #     related_articles = Article.objects.filter(
        #         q_objects, status=99).select_related('category').only(
        #             'title', 'meta_description', 'update_time', 'pk', 'hits',
        #             'category', 'author', 'thumb_image', 'category__name',
        #             'category__slug',
        #             'category__id').distinct().order_by('-update_time')[:20]

        #     if related_articles.exists():
        #         return related_articles, 'related'

        # 3. 随机推荐
        # return Article.objects.filter(status=99).order_by('?')[:20], 'random'
        return Article.objects.none(), "random"


@method_decorator(require_security_code, name="get")
class TagImportAPIView(APIView):

    def get(self, request):
        try:
            batch_size = int(request.GET.get("batch_size", 5000))
            if batch_size <= 0 or batch_size > 10000:
                raise ValidationError("batch_size必须在1-10000之间")

            stats = import_tag(batch_size)
            return Response(stats, status=status.HTTP_200_OK)

        except Exception as e:
            error_msg = str(e)
            status_code = (
                status.HTTP_400_BAD_REQUEST
                if isinstance(e, ValidationError)
                else status.HTTP_500_INTERNAL_SERVER_ERROR
            )
            return Response({"error": error_msg}, status=status_code)
