import json
import os
import random
import re
import urllib

import markdown2
import requests
from apps.core.models.ai_config import AiConfig

# from apps.core.views.txt_logs import createAIlog
from apps.log_service.services import log_service
from django.conf import settings


class AIcreate:

    def __init__(self):
        # 从 AiConfig 中获取配置
        self.ai_config = AiConfig.get_solo()

    def generate_tag_intro(self, obj):
        intro = ""
        print("AIcreate start==>")

        # 指定我们要处理的关键词和百度AI的口令
        prompt = self.ai_config.tag_prompt.replace("keyword", obj.title)

        # 获取百度AI内容
        try:
            intro_content = self._get_ai_content(prompt)
            html_end = (
                self.ai_config.tag_end.replace("keyword", obj.title)
                if self.ai_config.tag_end
                else ""
            )
            obj.intro = intro_content + html_end
            obj.save(
                update_fields=[
                    "intro",
                ]
            )
        except Exception as e:
            print("generate_tag_intro Error==>", e)

        return obj.intro

    def _get_ai_content(self, prompt, **model_kwargs):
        """
        根据配置的API版本获取AI生成的内容。

        参数:
            prompt (str): 必需的提示词。
            **model_kwargs: 可选的关键字参数，用于自定义模型参数。
                支持的自定义参数包括：
                - custom_model: 自定义模型名称
                - custom_temperature: 自定义temperature参数
                - custom_top_p: 自定义top_p参数
                - custom_penalty_score: 自定义penalty_score参数
        使用方法：
            1. 使用默认配置（完全向后兼容）
            content1 = self._get_ai_content("请写一篇文章")

            2. 只自定义模型
            content2 = self._get_ai_content("请写一篇文章", custom_model="ernie-lite-8k")

            3. 自定义多个参数
            content3 = self._get_ai_content("请写一篇文章",
                                        custom_model="ernie-speed-128k",
                                        custom_temperature=0.7)

            4. 使用字典解包（动态参数传递）
            custom_params = {
                'custom_model': 'ernie-tiny-8k',
                'custom_temperature': 0.8,
                'custom_top_p': 0.9
            }
            content4 = self._get_ai_content("请写一篇文章", **custom_params)
        """
        # 根据配置的API版本选择不同的调用方式
        if self.ai_config.api_version == "v1":  # 旧版API
            return self._get_ai_content_v1(prompt)
        else:  # 新版千帆API
            return self._get_ai_content_v2(prompt, **model_kwargs)

    def _get_ai_content_v1(self, prompt):
        """旧版API调用实现"""
        # 获取 access_token
        access_token = self._get_access_token()

        # 构建请求 URL 和 payload
        url = f"https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie-lite-8k?access_token={access_token}"
        payload = json.dumps({"messages": [{"role": "user", "content": prompt}]})
        headers = {"Content-Type": "application/json"}

        # 发送请求并获取响应
        try:
            response = requests.request("POST", url, headers=headers, data=payload, timeout=30)
            response.raise_for_status()  # 检查HTTP错误

            result = response.json().get("result")
            if not result:
                raise ValueError("API返回结果为空")

            content = self._is_markdown(result)
            return content

        except requests.exceptions.RequestException as e:
            raise Exception(f"旧版API请求失败: {str(e)}")
        except ValueError as e:
            raise Exception(f"旧版API响应解析错误: {str(e)}")

    def _get_ai_content_v2(self, prompt, **kwargs):
        """
        新版千帆API调用实现，支持通过字典传递自定义参数

        参数:
            prompt (str): 必需的提示词。
            **model_kwargs: 可选的关键字参数，用于自定义模型参数。
                支持的自定义参数包括：
                - custom_model: 自定义模型名称
                - custom_temperature: 自定义temperature参数
                - custom_top_p: 自定义top_p参数
                - custom_penalty_score: 自定义penalty_score参数

        """
        # 构建请求参数
        url = "https://qianfan.baidubce.com/v2/chat/completions"

        # 使用自定义模型或配置的模型，支持多层默认值
        # 从关键字参数中获取自定义值，若未提供则使用配置值
        model = kwargs.get("custom_model") or getattr(
            self.ai_config, "qianfan_model", "ernie-speed-128k"
        )
        # 定义可选的随机模型列表
        # AVAILABLE_MODELS = [
        #     "ernie-speed-128k", "ernie-lite-pro-128k", "ernie-tiny-8k"
        # ]
        AVAILABLE_MODELS = ["ernie-speed-128k", "ernie-lite-8k", "ernie-tiny-8k"]

        # 在构建 request_params 之前添加模型选择逻辑
        if model == "random":
            # 从可用模型中随机选择一个
            selected_model = random.choice(AVAILABLE_MODELS)
        else:
            selected_model = model
        # 构建请求参数，优先使用自定义参数
        print("selected_model===>", selected_model)
        request_params = {
            "model": selected_model,
            "messages": [{"role": "user", "content": prompt}],
            "temperature": kwargs.get(
                "custom_temperature", getattr(self.ai_config, "temperature", 0.95)
            ),
            "top_p": kwargs.get("custom_top_p", getattr(self.ai_config, "top_p", 0.8)),
            "penalty_score": kwargs.get(
                "custom_penalty_score", getattr(self.ai_config, "penalty_score", 1.0)
            ),
        }

        # 移除None值参数，避免API调用错误
        # 移除None值参数，避免API调用错误
        request_params = {k: v for k, v in request_params.items() if v is not None}

        payload = json.dumps(request_params, ensure_ascii=False)

        # 构建请求头
        authorization_key = getattr(self.ai_config, "authorization_key", "")
        if not authorization_key:
            raise ValueError("新版API需要配置Authorization密钥")

        qianfan_appid = getattr(self.ai_config, "qianfan_appid", "")

        headers = {
            "Content-Type": "application/json",
            "Authorization": f"Bearer {authorization_key}",
        }

        # 如果配置了appid，添加到请求头
        if qianfan_appid:
            headers["appid"] = qianfan_appid

        # 发送请求并获取响应
        try:
            response = requests.request(
                "POST", url, headers=headers, data=payload.encode("utf-8"), timeout=30
            )
            response.raise_for_status()  # 检查HTTP错误

            result = response.json()

            # 新版API返回格式处理
            if "choices" in result and len(result["choices"]) > 0:
                content = result["choices"][0]["message"]["content"]
            else:
                raise ValueError("API返回格式异常，未找到choices字段")

            content = self._is_markdown(content)
            return content

        except requests.exceptions.RequestException as e:
            raise Exception(f"新版API请求失败: {str(e)}")
        except (KeyError, IndexError, ValueError) as e:
            raise Exception(f"新版API响应解析错误: {str(e)}")

    def _get_access_token(self):
        """获取旧版API的access_token"""
        # 构建获取 access_token 的 URL
        url = f"https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id={self.ai_config.api_key}&client_secret={self.ai_config.secret_key}"
        payload = json.dumps("")
        headers = {"Content-Type": "application/json", "Accept": "application/json"}

        # 发送请求并获取响应
        response = requests.request("POST", url, headers=headers, data=payload)
        return response.json().get("access_token")

    def _is_markdown(self, content):
        """判断是不是markdown,然后顺便清理一下格式"""
        try:
            content = markdown2.markdown(content).replace("\n", "<br>")
            # 清理含空格的连续换行符
            content = re.sub(r"<br>\s+", "<br>", content)

            # 合并连续换行符
            content = re.sub(r"(<br\s*/?>\s*){2,}", "<br>", content)
        except Exception as e:
            print("is_markdown Error:我们直接处理div方法")
            content = content.replace("\n", "<br>")
            # 清理含空格的连续换行符
            content = re.sub(r"<br>\s+", "<br>", content)

            # 合并连续换行符
            content = re.sub(r"(<br\s*/?>\s*){2,}", "<br>", content)
        return content

    def _extract_custom_params(self, node_prompt):
        """
        从prompt配置中提取自定义模型参数

        参数:
            node_prompt: prompt节点配置字典

        返回:
            包含自定义参数的字典，如不存在则返回空字典
        """
        custom_params = {}

        # 定义可能的自定义参数字段映射
        param_mapping = {
            "custom_model": "custom_model",
            "custom_temperature": "custom_temperature",
            "custom_top_p": "custom_top_p",
            "custom_penalty_score": "custom_penalty_score",
            "model": "custom_model",  # 兼容简写形式
            "temperature": "custom_temperature",
            "top_p": "custom_top_p",
            "penalty_score": "custom_penalty_score",
        }

        # 检查并提取自定义参数
        for prompt_key, param_key in param_mapping.items():
            if prompt_key in node_prompt:
                custom_params[param_key] = node_prompt[prompt_key]

        return custom_params

    def ai_article(self):
        """主入口方法，根据配置类型路由到不同的处理逻辑"""
        try:
            config_type = getattr(self.ai_config, "get_type", {})
            if isinstance(config_type, dict):
                config_type_value = config_type.get("type", "static_template")
            else:
                config_type_value = "static_template"

            if config_type_value == "dynamic_builder":
                res = self.dynamic_builder_ai_article()
                print("dynamic_builder_ai_article===>res===>", res)
                return res
            else:
                res = self.static_template_ai_article()
                print("static_template_ai_article===>res===>", res)
                return res

        except Exception as e:
            print(f"AI文章生成错误: {e}")
            log_service.write_log("ai_create", f"生成错误: {str(e)}", log_type="persistent")

    def static_template_ai_article(self):
        message = ""
        try:
            prompt = self.ai_config.get_type
            # 获取AI配置根据配置中的内容promptjson来看
            try:
                prompt = self.ai_config.get_type
            except Exception as e:
                print("ai_article get json prompt Error:", e)
                return
            node = random.choice(list(prompt.keys()))
            node_prompt = prompt[node]
            # 提取可能存在的自定义模型参数
            custom_params = self._extract_custom_params(node_prompt)
            # 通过node的指定,我们来找文件,文件存于shared\aiwords\1.txt
            file_path = os.path.join(settings.STATICFILES_DIRS[0], f"aiwords/{int(node)}.txt")
            # 找到文件,取1词
            keyword = ""

            # 查看是什么办法生成AI,目前包括,单轮,多轮,一词多文
            # 单轮,一词一文,词-->下拉词,拼接成标题,有下拉词则执行,无下拉词则跳过,这里我们可以指定10词中的任意1个,如果index失败,我们则用0位词,将词和下拉词进入prompt生成口令成文
            # 多轮,多词一文,词-->下拉词,拼接成标题,标题方法同上,有几个词我们将生成几个新的小标题和prompt,最后将这些标题+content拼接成新文
            # 一词多文,一词-->下拉词,每文词与下拉词成新标题,将词与下拉词生成新的prompt生成新文章

            # 关键词:如果拼文词少于3,我们可以选择下拉词补位,还可以考虑直接将下拉直接加入tag中当,以突显tag能力
            # 锁定表达式,有[0]键位的需要指定第几条,无键位的就是为候选词的第1个词

            # 先得下拉词
            for i in range(2):
                with open(file_path, "r", encoding="utf-8") as file:
                    lines = file.readlines()
                    if len(lines) == 0:
                        message = "在{}中我们没有找到关键词".format(file_path)
                        log_service.write_log("ai_create", message, log_type="persistent")
                        # createAIlog(message)
                        return
                    keyword = lines[0].strip()
                down_words = self.get_down_words(keyword)
                if not down_words:
                    lines = ""
                    with open(file_path, "r", encoding="utf-8") as file:
                        lines = file.readlines()
                    # 移除第一行
                    lines.pop(0)
                    with open(file_path, "w", encoding="utf-8") as file:
                        file.writelines(lines)
                else:
                    break
            if not down_words:
                return
            title = ""
            content_top = ""
            content = ""
            # 通过 node_prompt,我们得到标题
            if node_prompt["get_type"] == "单轮":
                # "title_type": "keyword（downword）|keyword：downword|keyword VS downword|keyword——downword|keyword，downword|keyword：downword？"
                #'title_type':'keyword（laWords[0]）|keyword：laWords[0]|keyword VS laWords[0]|keyword——laWords[0]|keyword，laWords[0]|keyword：laWords[0]？'
                node_title_prompt = random.choices(node_prompt["title_type"].split("|"))[0]
                # max_num = int(node_prompt['num'])
                # down_words = down_words[:max_num]
                down_words_end, title = self._get_title(keyword, down_words, node_title_prompt)
                # 'prompt':'以一个网站编辑的文笔，以“downword”为中心思想，用场景化解决问题思维写文章，不要以回答我的问题的方式写，但不要指出场景一二三等，直接输出一个成熟的文章|最近很多网友问有关“downword”相关的问题，请以网站编辑的口吻给大家解释一下。'
                node_content_prompt = (
                    random.choices(node_prompt["prompt"].split("|"))[0]
                    .replace("keyword", keyword)
                    .replace("downword", down_words_end)
                )
                content = self._get_ai_content(node_content_prompt, **custom_params)
                tags_num = self.ai_config.keywords_num
                tags_words = [keyword] + down_words[: tags_num - 1]
                keywords = ",".join(tags_words)
            elif node_prompt["get_type"] == "多轮":
                # 这里是多轮，我们将多次得到内容进行拼接
                times = int(node_prompt["num"])
                node_title_prompt = random.choices(node_prompt["title_type"].split("|"))[0]
                node_title_prompt = re.sub(r"$\d+$", "", node_title_prompt).replace(
                    "downwords", "downword"
                )
                title = node_title_prompt.replace("keyword", keyword).replace(
                    "downword", down_words[0]
                )
                # 小标题该如果控制？以下拉词为

                node_h2_prompt = node_prompt["h2_type"].split("|")
                if len(down_words) < times:
                    times = len(down_words)

                content_top += "<!--Article by DjaCore AI--><p><strong>本文目录：</strong></p>"
                content = ""
                for i in range(times):
                    # 得到h2标题
                    if node_h2_prompt:
                        i_title = (
                            random.choices(node_h2_prompt)[0]
                            .replace("keyword", keyword)
                            .replace("downword", down_words[i])
                        )
                    else:
                        i_title = down_words[i]

                    node_content_prompt = (
                        random.choices(node_prompt["prompt"].split("|"))[0]
                        .replace("keyword", keyword)
                        .replace("downword", down_words[i])
                    )
                    i_content = self._get_ai_content(node_content_prompt, **custom_params)
                    if i_title and i_content:
                        content_top += f'<a href="#djacore{i}">{down_words[i]}</a><br>'
                        content += f'<h2 style="font-size: 110%;font-weight: bold;"><a id="djacore{i}" name="djacore{i}">{i_title}</a></h2>'
                        content += i_content
                tags_num = self.ai_config.keywords_num
                tags_words = [keyword] + down_words
                tags_words = list(dict.fromkeys(tags_words))
                keywords = ",".join(tags_words[:tags_num])
            elif node_prompt["get_type"] == "一词多文":
                times = int(node_prompt["num"])

                # 小标题该如果控制？以下拉词为

                node_h2_prompt = node_prompt["h2_type"].split("|")
                if len(down_words) < times:
                    times = len(down_words)
                for i in range(times):
                    node_title_prompt = random.choices(node_prompt["title_type"].split("|"))[0]
                    title = node_title_prompt.replace("keyword", keyword).replace(
                        "downword", down_words[i]
                    )

                    node_content_prompt = (
                        random.choices(node_prompt["prompt"].split("|"))[0]
                        .replace("keyword", keyword)
                        .replace("downword", down_words[i])
                    )
                    content = self._get_ai_content(node_content_prompt, **custom_params)
                    tags_num = self.ai_config.keywords_num
                    tags_words = [keyword, down_words[i]] + down_words
                    tags_words = list(dict.fromkeys(tags_words))
                    keywords = ",".join(tags_words[:tags_num])
                    data = {
                        "title": title,
                        "content": content,
                        "meta_keywords": keywords,
                        "category_id": int(node),  # 必须字段
                        "editor_id": self.ai_config.editor_id,  # 必须字段
                        "status": 99,
                    }
                    up_res = self.update_ai_aritcle(data)
                    if "成功" in up_res:
                        lines = ""
                        with open(file_path, "r", encoding="utf-8") as file:
                            lines = file.readlines()
                        # 移除第一行
                        lines.pop(0)
                        with open(file_path, "w", encoding="utf-8") as file:
                            file.writelines(lines)
                    else:
                        print("AI生成文章结果未知。")
                return

            else:
                print("我们没有找到合适的生成方式，请修改调用模式")
                message = "我们没有找到合适的生成方式，请修改调用模式"
                log_service.write_log("ai_create", message, log_type="persistent")
                return

            content = content_top + "<br/>" + content
            data = {
                "title": title,
                "content": content,
                "meta_keywords": keywords,
                "category_id": int(node),  # 必须字段
                "editor_id": self.ai_config.editor_id,  # 必须字段
                "status": 99,
            }
            up_res = self.update_ai_aritcle(data)
            if "成功" in up_res:
                lines = ""
                with open(file_path, "r", encoding="utf-8") as file:
                    lines = file.readlines()
                # 移除第一行
                lines.pop(0)
                with open(file_path, "w", encoding="utf-8") as file:
                    file.writelines(lines)
            else:
                print("AI生成文章结果未知。")
        except Exception as e:
            print("static_template_ai_article Error:", e)
        return message

    def update_ai_aritcle(self, data):
        # 注意要包含所有必填字段（category_id/editor_id 等）
        try:
            from apps.content.views.article import ArticleValidator

            validator = ArticleValidator(data)
            if validator.is_valid():
                article = validator.save()
                print("AI生成成功：", article)
                message = f"AI生成成功：{article}"
                log_service.write_log("ai_create", message, log_type="persistent")
                return article
            else:
                print(f"AI生成失败：{validator.errors}")
                message = f"AI生成失败：{validator.errors}"
                log_service.write_log("ai_create", message, log_type="persistent")
                return f"AI生成失败：{validator.errors}"
        except Exception as e:
            print("updata_ai_aritcle ERROR：", e)
            message = f"AI生成失败，错误位置：updata_ai_aritcle ：{e}"
            log_service.write_log("ai_create", message, log_type="persistent")
            return f"AI生成失败，错误位置：pdata_ai_aritcle ：{e}"

    def _get_title(self, keyword, down_words, node_title_prompt):
        """keyword:关键词
        down_words:为list，代价可以选的词,
        node_title_prompt:title_type的值
        """
        match = re.search(r"\[(\d+)\]", node_title_prompt)  # 匹配中括号内的数字
        down_words_end = ""
        if match:
            index_num = int(match.group(1))
            try:
                down_words_end = down_words[index_num]
            except Exception as e:
                print("单轮获取下拉词为标题时发生错误,我们取第一个词进入标题拼接")
                down_words_end = down_words[0]
            # 将'dwon_words[index_num]'替换为download
            node_title_prompt = node_title_prompt.replace(f"downwords[{index_num}]", "downword")
        else:
            down_words_end = down_words[0]
        return down_words_end, node_title_prompt.replace("keyword", keyword).replace(
            "downword", down_words_end
        )

    def get_down_words(self, word):
        for i in range(2):
            try:
                url = (
                    "https://www.baidu.com/sugrec?pre=1&p=3&ie=utf-8&json=1&prod=pc&from=pc_web&wd="
                    + urllib.parse.quote(word)
                )
                timeout = 5
                headers = {
                    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36",
                    "Referer": url,
                }
                response = requests.get(url, headers=headers, timeout=timeout)
                return [i["q"] for i in response.json()["g"]]
            except requests.exceptions.Timeout:
                pass
            except Exception as e:
                print("get_down_words Error:", e)
                return False

    # 将来可以添加生成文章等方法
    # def generate_article(self, ...):
    #     pass

    def dynamic_builder_ai_article(self):
        """新的动态构建法处理逻辑-优化版"""
        try:
            config_data = self.ai_config.get_type
            if (
                not config_data
                or not isinstance(config_data, dict)
                or config_data.get("type") != "dynamic_builder"
            ):
                return "配置类型非dynamic_builder，将使用静态模板法。"

            # 从config_data.channels的KEY中随机选择一个channel_id
            available_channels = list(config_data.get("channels", {}).keys())
            if not available_channels:
                return "配置中未找到任何有效的channels"

            channel_id = random.choice(available_channels)
            channel_config = config_data["channels"][channel_id]
            print(f"随机选择的channel_id: {channel_id}")

            # 初始化动态提取器
            self.content_extractor = DynamicContentExtractor(channel_config)

            # 获取关键词
            keyword = self._get_keyword_from_file(channel_id)
            if not keyword:
                return "无法获取关键词"

            print(f"动态构建法 - 使用关键词: {keyword}")

            # 执行动态工作流程
            final_result = self.execute_dynamic_workflow(keyword, channel_config)
            print("final_result=========>", final_result)
            # return
            # 保存结果
            if final_result and final_result.get("content"):
                save_res = self._save_ai_article_result(final_result, channel_id)
                return f"动态构建法执行完毕。{save_res}"
            else:
                return "内容生成失败，未获取到有效内容。"

        except Exception as e:
            error_msg = f"动态构建法执行失败: {str(e)}"
            print(error_msg)
            log_service.write_log("ai_create", error_msg, log_type="persistent")
            return error_msg

    def execute_dynamic_workflow(self, keyword, channel_config):
        """执行动态工作流程-完全基于配置"""
        fields_config = channel_config.get("prompt", {}).get("fields", {})
        custom_params = self._extract_custom_params(channel_config)

        # 构建综合提示词
        comprehensive_prompt = self.build_comprehensive_prompt(keyword, fields_config)

        # 调用AI生成内容
        ai_response = self._get_ai_content(comprehensive_prompt, **custom_params)
        print(f"AI原始响应接收成功，开始解析...{ai_response}")

        # return
        # 使用配置的规则提取各个部分
        result = {
            "keyword": keyword,
            "long_tail_words": self.content_extractor.extract_long_tail_words(ai_response),
            "selected_long_tail": self.extract_selected_long_tail(ai_response, fields_config),
            "title": self.extract_title_based_on_config(ai_response, fields_config),
            "content": self.extract_content_based_on_config(ai_response, fields_config),
            "keywords": self.content_extractor.extract_keywords(ai_response, keyword),
        }

        return result

    def extract_content_based_on_config(self, text, fields_config):
        """基于配置的extract_regex提取内容"""
        if not hasattr(self, "content_extractor"):
            return text

        # 直接使用content_extractor的方法，它会自动读取配置中的extract_regex
        content = self.content_extractor.extract_article_content(text, "content")

        # 验证内容有效性
        if not content or len(content.strip()) < 50:
            print("提取的内容过短，返回全文")
            return text

        return content

    def extract_title_based_on_config(self, text, fields_config):
        """基于配置的extract_regex提取标题"""
        if not hasattr(self, "content_extractor"):
            return "自动生成的标题"

        title = self.content_extractor.safe_extract(text, "title", "自动生成的标题")
        return title if title else "自动生成的标题"

    def build_comprehensive_prompt(self, keyword, fields_config):
        """构建综合提示词-支持多模板随机选择"""
        prompt_parts = []

        # 按照字段顺序构建提示词
        for field_type in ["role", "title", "content", "keywords"]:
            if field_type in fields_config:
                field_config = fields_config[field_type]
                prompt_templates = field_config.get("prompt", [])

                if prompt_templates:
                    # 随机选择一个提示词模板
                    if isinstance(prompt_templates, list):
                        selected_prompt = random.choice(prompt_templates)
                    else:
                        selected_prompt = prompt_templates

                    # 替换关键词变量
                    processed_prompt = selected_prompt.replace("{keyword}", keyword)
                    prompt_parts.append(processed_prompt)

        comprehensive_prompt = "\n\n".join(prompt_parts)
        print(f"生成的综合提示词长度: {len(comprehensive_prompt)}====>{comprehensive_prompt}")
        return comprehensive_prompt

    def extract_long_tail_words(self, text):
        """提取长尾词（支持多正则匹配）"""
        if not hasattr(self, "content_extractor"):
            return []

        patterns = self.content_extractor.get_extract_regex("role")
        long_tail_words = []

        for pattern in patterns:
            if not pattern:
                continue
            try:
                matches = re.findall(pattern, text)
                for match in matches:
                    if isinstance(match, tuple):
                        word = next((m for m in match if m), None)
                    else:
                        word = match
                    if word and word not in long_tail_words:
                        long_tail_words.append(word)
                if long_tail_words:  # 找到即返回
                    break
            except Exception as e:
                print(f"长尾词提取错误 {pattern}: {e}")
                continue

        return long_tail_words

    def extract_selected_long_tail(self, text, fields_config):
        """提取选中的长尾词"""
        if not hasattr(self, "content_extractor"):
            return ""

        # 这里可以根据需要实现特定的选中长尾词提取逻辑
        long_tail_words = self.content_extractor.extract_long_tail_words(text)
        return long_tail_words[0] if long_tail_words else ""

    def extract_title(self, text, fields_config):
        """提取标题（支持多正则匹配）"""
        if not hasattr(self, "content_extractor"):
            return "自动生成的文章"

        if "title" in fields_config:
            title_config = fields_config["title"]
            extract_rules = title_config.get("extract_regex", [])

            # 尝试多个正则表达式
            for pattern in extract_rules:
                title = self.content_extractor.safe_extract(text, "title", pattern)
                if title:
                    return title

        # 降级方案
        return self.content_extractor.safe_extract(text, "title", "『(.*?)』", "自动生成的文章")

    def extract_content(self, text, fields_config):
        """提取内容（专门处理HTML标签问题）"""
        if not hasattr(self, "content_extractor"):
            return text

        if "content" in fields_config:
            content_config = fields_config["content"]
            extract_rules = content_config.get("extract_regex", [])

            # 尝试多个正则表达式
            for pattern in extract_rules:
                content = self.content_extractor.safe_extract(text, "content", pattern)
                if content and len(content) > 50:  # 简单验证内容长度
                    return content

        # 使用专门的文章内容提取方法
        content = self.content_extractor.extract_article_content(text)
        return content if content else text

    def extract_keywords(self, text, keyword, fields_config):
        """提取关键词（支持两种方式：下拉词和正则提取）"""
        if not hasattr(self, "content_extractor"):
            return []

        if "keywords" in fields_config:
            return self.content_extractor.extract_keywords(text, keyword)

        return []

    def _get_keyword_from_file(self, channel_id):
        """从文件获取关键词（复用原有逻辑）"""
        file_path = os.path.join(settings.STATICFILES_DIRS[0], f"aiwords/{int(channel_id)}.txt")
        try:
            with open(file_path, "r", encoding="utf-8") as f:
                lines = [line.strip() for line in f if line.strip()]
                return lines[0] if lines else None
        except Exception as e:
            print(f"获取关键词文件错误: {e}")
            return None

    def _save_ai_article_result(self, result, channel_id):
        """保存AI文章结果（复用原有逻辑）"""
        data = {
            "title": result["title"],
            "content": result["content"],
            "meta_keywords": ",".join(result["keywords"]),
            "category_id": int(channel_id),
            "editor_id": self.ai_config.editor_id,
            "status": 99,
        }

        # 调用原有的保存逻辑
        up_res = self.update_ai_aritcle(data)
        if "成功" in up_res:
            # 移除已使用的关键词
            self._remove_used_keyword(channel_id)
            return "生成成功"
        else:
            return f"保存失败: {up_res}"

    def _remove_used_keyword(self, channel_id):
        """移除已使用的关键词（复用原有逻辑）"""
        file_path = os.path.join(settings.STATICFILES_DIRS[0], f"aiwords/{int(channel_id)}.txt")
        try:
            with open(file_path, "r", encoding="utf-8") as file:
                lines = file.readlines()
            if lines:
                lines.pop(0)
                with open(file_path, "w", encoding="utf-8") as file:
                    file.writelines(lines)
        except Exception as e:
            print(f"移除关键词错误: {e}")

    def _extract_custom_params(self, channel_config):
        """提取自定义模型参数"""
        custom_params = {}
        if "custom_model" in channel_config:
            custom_params["custom_model"] = channel_config["custom_model"]
        if "custom_temperature" in channel_config:
            custom_params["custom_temperature"] = channel_config["custom_temperature"]
        if "custom_top_p" in channel_config:
            custom_params["custom_top_p"] = channel_config["custom_top_p"]
        if "custom_penalty_score" in channel_config:
            custom_params["custom_penalty_score"] = channel_config["custom_penalty_score"]

        return custom_params


import random
import re
import urllib.parse

import requests


class DynamicContentExtractor:
    """动态内容提取器-根据配置规则提取"""

    def __init__(self, channel_config):
        self.channel_config = channel_config
        self.pattern_cache = {}

    def extract_article_content(self, text, field_type="content"):
        """
        根据配置的extract_regex规则提取文章内容
        Args:
            text: 待提取的文本
            field_type: 字段类型，默认为'content'
        """
        try:
            if not text:
                return text

            # 1. 从配置获取extract_regex数组
            extract_patterns = self.get_extract_regex(field_type)
            if not extract_patterns:
                print(f"字段 {field_type} 未配置提取规则，返回全文")
                return text

            # 2. 按顺序尝试每个正则表达式
            for pattern in extract_patterns:
                if not pattern:
                    continue

                try:
                    content = self._extract_with_pattern(text, pattern)
                    if content and self._is_valid_content(content):
                        print(f"使用正则模式成功提取内容，长度: {len(content)}")
                        return content
                except Exception as e:
                    print(f"正则模式 {pattern} 提取失败: {e}")
                    continue

            # 3. 所有正则都失败时，返回全文
            print("所有正则模式提取失败，返回全文")
            return text

        except Exception as e:
            print(f"文章内容提取异常: {e}")
            return text

    def _extract_with_pattern(self, text, pattern):
        """使用单个正则模式提取内容"""
        if pattern not in self.pattern_cache:
            self.pattern_cache[pattern] = re.compile(pattern, re.DOTALL)

        regex_obj = self.pattern_cache[pattern]
        match = regex_obj.search(text)

        if match:
            # 处理分组情况
            if match.groups():
                return match.group(1)  # 返回第一个分组
            else:
                return match.group(0)  # 返回整个匹配

        return None

    def _is_valid_content(self, content):
        """验证提取的内容是否有效"""
        if not content or len(content.strip()) < 10:
            return False
        return True

    def get_extract_regex(self, field_type):
        """
        从配置获取指定字段的extract_regex数组
        Args:
            field_type: 字段类型 (role/title/content/keywords)
        """
        try:
            fields_config = self.channel_config.get("prompt", {}).get("fields", {})
            if field_type in fields_config:
                extract_regex = fields_config[field_type].get("extract_regex", [])
                # 确保返回的是列表形式
                if isinstance(extract_regex, str):
                    return [extract_regex]
                return extract_regex
            return []
        except Exception as e:
            print(f"获取提取正则配置错误: {e}")
            return []

    def safe_extract(self, text, field_type, default=""):
        """
        安全提取内容（通用方法）
        Args:
            text: 待提取文本
            field_type: 字段类型
            default: 默认值
        """
        try:
            content = self.extract_article_content(text, field_type)
            return content if content else default
        except Exception as e:
            print(f"安全提取错误 {field_type}: {e}")
            return default

    def extract_long_tail_words(self, text):
        """提取长尾词"""
        return self._extract_multiple_values(text, "role")

    def extract_title(self, text):
        """提取标题"""
        return self.safe_extract(text, "title", "自动生成的标题")

    def extract_keywords(self, text, keyword):
        """提取关键词"""
        try:
            fields_config = self.channel_config.get("prompt", {}).get("fields", {})
            if "keywords" not in fields_config:
                return []

            keywords_config = fields_config["keywords"]
            keyword_type = keywords_config.get("type", "regex")
            max_num = keywords_config.get("num", 3)

            keywords = []

            if keyword_type == "dropdown":
                # 下拉词方式
                dropdown_words = self.get_down_words(keyword)
                if dropdown_words:
                    keywords = [keyword] + dropdown_words[: max_num - 1]
                else:
                    # 降级到正则提取
                    keywords = self._extract_keywords_by_regex(text, max_num)

            elif keyword_type == "regex":
                # 正则表达式方式
                keywords = self._extract_keywords_by_regex(text, max_num)

            # 去重并限制数量
            keywords = list(dict.fromkeys(keywords))
            return keywords[:max_num]

        except Exception as e:
            print(f"关键词提取错误: {e}")
            return []

    def _extract_multiple_values(self, text, field_type):
        """提取多个值（如长尾词）"""
        patterns = self.get_extract_regex(field_type)
        results = []

        for pattern in patterns:
            if not pattern:
                continue
            try:
                if pattern not in self.pattern_cache:
                    self.pattern_cache[pattern] = re.compile(pattern)

                matches = self.pattern_cache[pattern].findall(text)
                for match in matches:
                    if isinstance(match, tuple):
                        word = next((m for m in match if m), None)
                    else:
                        word = match
                    if word and word not in results:
                        results.append(word)
            except Exception as e:
                print(f"多值提取错误 {pattern}: {e}")
                continue

        return results

    def _extract_keywords_by_regex(self, text, max_num):
        """通过正则表达式提取关键词"""
        patterns = self.get_extract_regex("keywords")
        keywords = []

        for pattern in patterns:
            if not pattern:
                continue

            try:
                matches = re.findall(pattern, text)
                for match in matches:
                    if isinstance(match, tuple):
                        keyword = next((m for m in match if m), None)
                    else:
                        keyword = match

                    if keyword and keyword not in keywords:
                        keywords.append(keyword)
                        if len(keywords) >= max_num:
                            return keywords
            except Exception as e:
                print(f"正则提取关键词错误 {pattern}: {e}")
                continue

        return keywords

    def get_down_words(self, word):
        """
        获取百度下拉词
        """
        for i in range(2):
            try:
                url = (
                    "https://www.baidu.com/sugrec?pre=1&p=3&ie=utf-8&json=1&prod=pc&from=pc_web&wd="
                    + urllib.parse.quote(word)
                )
                timeout = 5
                headers = {
                    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
                    "Referer": url,
                }
                response = requests.get(url, headers=headers, timeout=timeout)
                if response.status_code == 200:
                    data = response.json()
                    return [item["q"] for item in data.get("g", [])]
            except Exception as e:
                print(f"获取下拉词错误 第{i+1}次: {e}")
        return []
