# -*- coding: utf-8 -*-
import time

import requests
import json

from aidso_geo.core.down_load_bot import get_req_id
from aidso_geo.utils import bh_utils
from aidso_geo.utils.tos_utils import get_string_from_tos


def ai_get_brand_list(content, prompt):
    url = "https://ark.cn-beijing.volces.com/api/v3/chat/completions"
    payload = json.dumps({
        "model": "doubao-seed-1-6-250615",
        "messages": [
            {
                "role": "system",
                "content": """
                用户提供问题和结果提取信息，严格遵循以下核心逻辑与要求：
核心逻辑：优先识别文本核心表述对象 —— 若原文以品牌推荐为主，重点提取品牌名；若以厂名介绍为主，重点提取厂名；若以店名介绍为主，重点提取店名；若包含店名、公司名，均按文本核心导向同步提取（如门店推荐类文本优先提取店名，平台盘点类优先提取平台名）。
提取范围：仅提取商业主体名称（含品牌名、公司名、店名、厂名、品牌授权门店名、连锁门店名），彻底忽略地址、服务、优惠、功能描述等非名称类内容。
排序规则：严格遵循文本原文推荐的先后顺序（含分类、分点内的自然出现顺序，不打乱原文逻辑）。
去重规则：完全相同的名称仅保留 1 个；同一品牌 / 公司下的不同门店、分支机构（名称不同）视为不同主体，全部保留；名称中特殊字符（括号、空格等）不影响去重判断。
名称保留：完整保留名称原始表述，不做简化、缩写或修改（含括号、空格、特殊符号等）
输出格式：仅输出纯 JSON 数组，数组键名固定为不含任何解释、注释、标题等额外文字
                """
            },
            {
                "role": "user",
                "content": f"问题:{prompt}回答：{content}"
            }
        ],
        "response_format": {
            "type": "json_schema",
            "json_schema": {
                "name": "brand_list",
                "strict": True,
                "schema": {
                    "type": "object",
                    "properties": {
                        "brands": {
                            "type": "array",
                            "items": {
                                "type": "string"
                            },
                            "description": "品牌列表，按原文出现顺序排列，每个元素是品牌名称字符串"
                        }
                    },
                    "required": [
                        "brands"
                    ],
                    "additionalProperties": False
                }
            }
        },
        "thinking": {
            "type": "disabled"
        },
        "temperature": 0
    })
    headers = {
        'Authorization': 'Bearer ark-7afc3be2-37a8-47fd-9f02-996258a3d305-27da0',
        'Content-Type': 'application/json'
    }

    response = requests.request("POST", url, headers=headers, data=payload)
    try:
        return json.loads(response.json().get("choices")[0].get("message").get("content")).get("brands")

    except Exception as e:
        return []

def ai_get_product_list(content, prompt):
    url = "https://ark.cn-beijing.volces.com/api/v3/chat/completions"
    payload = json.dumps({
        "model": "doubao-seed-2-0-lite-260215",
        "messages": [
            {
                "role": "system",
                "content": """
你是一个“产品词抽取器”。

任务：
从我提供的文本中，提取所有“产品词”，并按“首次出现顺序”输出。

【什么是产品词】
产品词包括但不限于：
1. 具体单品名
2. 产品系列名
3. 带版本/代际/款型/规格的产品名
4. 品牌+产品组合名
5. 用户在消费语境中可以直接识别、购买、比较的产品名称

【不要提取的内容】
以下内容即使很重要，也不是产品词，不要输出：
1. 纯品牌名
2. 成分词 / 原料词 / 技术词
3. 功效词 / 需求词
4. 人群词 / 肤质词 / 场景词
5. 纯品类通用词（除非它和具体产品名绑定）
6. 价格、规格、时间、评价性描述

【抽取原则】
1. 保留原文写法，不要擅自改写。
2. 若同一产品重复出现，只保留一次。
3. 若“品牌+产品名”和“产品名简称”都出现：
   - 如果简称明确指向同一产品，可只保留信息更完整的那个；
4. 优先提取“可以被当作商品识别对象”的词，而不是泛概念。
5. 以 / , 等符号分割的 提取为多个
6. 若不确定是否为产品词，宁可不提。
输出格式：
{
  "product_words": [
  
  ]
}
                """
            },
            {
                "role": "user",
                "content":  f"""请从下面的文本中抽取所有产品词。 文本如下{content}"""
            }
        ],
        "thinking": {
            "type": "disabled"
        },
        "temperature": 0
    })
    headers = {
        'Authorization': 'Bearer ark-7afc3be2-37a8-47fd-9f02-996258a3d305-27da0',
        'Content-Type': 'application/json'
    }

    response = requests.request("POST", url, headers=headers, data=payload)
    try:
        # print()
        # return json.loads(response.json())
        return json.loads(response.json().get("choices")[0].get("message").get("content")).get("product_words")

    except Exception as e:
        return []

def ai_get_product_list_search(product_list):
    url = "https://ark.cn-beijing.volces.com/api/v3/chat/completions"
    payload = json.dumps({
        "model": "doubao-seed-1-6-250615",
        "messages": [
            {
                "role": "system",
                "content": """
你是一名信息抽取助手。
你的任务是：从给定名称列表中，删除“只有品牌名、没有具体产品名”的项，只保留“具体产品名称”。

【判定规则】
1. 仅保留能明确指向某个具体商品/产品的名称。
2. 纯品牌名、公司名、系列名，不保留。
3. 如果名称中同时包含“品牌 + 产品名”，要整体保留，因为它属于具体产品名称。
4. 不要因为前面带品牌名就删除，只要后面有明确产品名，就保留完整原文。
5. 输出时保持原顺序，不改写，不拆分，不补充，不去重。
6. 最终只返回保留后的列表，不要输出解释。
                """
            },
            {
                "role": "user",
                "content": f"名称列表:{product_list}"
            }
        ],
        "response_format": {
            "type": "json_schema",
            "json_schema": {
                "name": "brand_list",
                "strict": True,
                "schema": {
                    "type": "object",
                    "properties": {
                        "brands": {
                            "type": "array",
                            "items": {
                                "type": "string"
                            },
                            "description": "品牌产品词列表"
                        }
                    },
                    "required": [
                        "brands"
                    ],
                    "additionalProperties": False
                }
            }
        },
        "thinking": {
            "type": "disabled"
        },
        "temperature": 0
    })
    headers = {
        'Authorization': 'Bearer ark-7afc3be2-37a8-47fd-9f02-996258a3d305-27da0',
        'Content-Type': 'application/json'
    }

    response = requests.request("POST", url, headers=headers, data=payload)
    try:
        return json.loads(response.json().get("choices")[0].get("message").get("content")).get("brands")

    except Exception as e:
        return []

def ai_get_brand_name(content):
    url = "https://ark.cn-beijing.volces.com/api/v3/chat/completions"

    # 修正JSON Schema定义：array类型需配置items，同时调整最终期望返回字典格式的提示
    payload = json.dumps({
        "model": "doubao-seed-1-6-250615",
        "messages": [
            {
                "role": "system",
                "content": """
###
假如你是品牌识别专家，你将根据用户输入的词汇列表，来解决识别该词汇对应品牌的任务。根据以下规则一步步执行：
1. 若输入词汇能明确对应某个已知品牌，**优先返回该品牌的标准中文名称**，无标准中文名称时再返回品牌英文名称；
2. 若输入词汇无法对应任何已知品牌，则返回“没有品牌”；
3. 无论用户输入的是中文词汇还是英文品牌名，均按上述规则匹配返回（英文品牌名先匹配对应中文，无中文则返回原英文）。
4。用户可能输入的是某个产品线产品词 也返回对应的品牌名称

参考例子：
示例1：
输入：苹果
输出：苹果

示例2：
输入：桌子
输出：没有品牌

示例3：
输入：Nike
输出：耐克

示例4：
输入：Google
输出：谷歌

示例5：
输入：Tesla
输出：特斯拉

示例6：
输入：Zara
输出：飒拉

示例7：
输入：Noname
输出：没有品牌

请回答问题：
输入：XXX
输出：

要求：
1 直接返回识别结果，无需额外解释；
2 严格遵循“先中文、无中文再英文”的返回原则，每一个识别结果均需执行此规则；
3 若输入词汇包含“品牌名 + 业务/产品线/场景词”（如：打车/外卖/出行/优选/买菜/闪购/到家/酒店/团购/买药/骑行/配送/同城/快送/生活/钱包/支付/商城/直播/短视频/云/地图等），则忽略业务词，输出其母品牌标准中文名
4 若识别到品牌按规则返回对应名称，否则返回“没有品牌”。
###
                
                """
            },
            {
                "role": "user",
                "content": str(content)
            }
        ],
        "response_format": {
            "type": "json_schema",
            "json_schema": {
                "name": "brand_recognize_result",
                "strict": True,  # 严格模式，禁止返回schema外的字段
                "schema": {
                    "type": "object",
                    "properties": {
                        "results": {
                            "type": "array",
                            "items": {
                                "type": "object",
                                "properties": {
                                    "keyword": {"type": "string"},
                                    "brand": {"type": "string"},
                                },
                                "required": ["keyword", "brand"],
                                "additionalProperties": False,
                            }
                        }
                    },
                    "required": ["results"],
                    "additionalProperties": False  # 禁止添加额外字段
                }
            }
        },
        "thinking": {
            "type": "disabled"
        },
        "temperature": 0
    })
    headers = {
        'Authorization': 'Bearer ark-7afc3be2-37a8-47fd-9f02-996258a3d305-27da0',
        'Content-Type': 'application/json'
    }

    # 发送POST请求
    response = requests.request("POST", url, headers=headers, data=payload)
    try:
        response_data = json.loads(response.text)
        result_json_str = response_data["choices"][0]["message"]["content"]
    except (json.JSONDecodeError, KeyError) as e:
        return []
    #
    try:
        brand_emotion_dict = json.loads(result_json_str).get('results')
    except json.JSONDecodeError as e:
        return []

    # 步骤3：返回最终的字典结果
    return brand_emotion_dict

def ai_get_product_sentiment_and_mentions(brand_list, ai_answer_text):
    """
    入参:
      - brand_list: list[str]  品牌列表（顺序很重要）
      - ai_answer_text: str    AI回答文本

    返回:
      list[dict]，格式:
      [
        {
          "brand_name": "...",
          "sentiment": "正向|负向|中性",
          "positive_mentions": [...],
          "negative_mentions": [...]
        },
        ...
      ]
    """

    url = "https://ark.cn-beijing.volces.com/api/v3/chat/completions"

    system_prompt = """
你是一位专业的产品舆情与GEO内容分析专家。
你需要根据「AI回答内容」对「指定产品列表」逐个输出每个品牌的：

1) sentiment：情感倾向，仅允许 ["正向","负向","中性"]
   - 若无法判断，必须输出 "中性"
   - sentiment 必须与该品牌在文本中的语境一致，禁止凭空编造

2) positive_mentions：该品牌在文本中的正面提及词/短语 例如：优势、好评、推荐、认可、亮点、强项、信任等
3) negative_mentions：该品牌在文本中的负面提及词/短语 例如：缺点、差评、风险、争议、吐槽、弱点等


强制规则：
- 必须覆盖产品列表中的每个产品，输出 items 数组长度必须等于产品列表长度，并尽量保持顺序一致
- 只提取文本中真实出现或可直接归纳的词/短语，禁止编造
- 必须与该产品直接相关（若无法指向产品，则不要提取）
- 每条词/短语严格 2~10 个字
- 同义词合并去重（如“舒服/舒适”只保留更标准的）
- 若某品牌在文本中没有可提取内容，则 positive_mentions / negative_mentions 返回空数组 []
- 输出必须严格符合 JSON Schema
- 仅输出纯 JSON，不要输出任何额外文字
""".strip()

    payload = {
        "model": "doubao-seed-1-6-250615",
        "messages": [
            {"role": "system", "content": system_prompt},
            {
                "role": "user",
                "content": f"产品列表：{json.dumps(brand_list, ensure_ascii=False)}\n\nAI回答内容：{ai_answer_text}"
            }
        ],
        "response_format": {
            "type": "json_schema",
            "json_schema": {
                "name": "brand_sentiment_and_mentions",
                "strict": True,
                "schema": {
                    "type": "object",
                    "properties": {
                        "items": {
                            "type": "array",
                            "items": {
                                "type": "object",
                                "properties": {
                                    "brand_name": {"type": "string"},
                                    "sentiment": {
                                        "type": "string",
                                        "enum": ["正向", "负向", "中性"]
                                    },
                                    "positive_mentions": {
                                        "type": "array",
                                        "items": {"type": "string"}
                                    },
                                    "negative_mentions": {
                                        "type": "array",
                                        "items": {"type": "string"}
                                    }
                                },
                                "required": ["brand_name", "sentiment", "positive_mentions", "negative_mentions"],
                                "additionalProperties": False
                            }
                        }
                    },
                    "required": ["items"],
                    "additionalProperties": False
                }
            }
        },
        "thinking": {"type": "disabled"},
        "temperature": 0,
        "top_p": 1
    }

    headers = {
        "Authorization": "Bearer ark-7afc3be2-37a8-47fd-9f02-996258a3d305-27da0",
        "Content-Type": "application/json"
    }

    try:
        resp = requests.post(url, headers=headers, data=json.dumps(payload), timeout=60)
        resp.raise_for_status()
        data = resp.json()

        content = data["choices"][0]["message"]["content"]
        parsed = json.loads(content)

        items = parsed.get("items", [])
        if not isinstance(items, list):
            return _fallback_items(brand_list)

        # ✅ 兜底：保证每个品牌都有一条（并且按 brand_list 顺序）
        mapped = {i.get("brand_name"): i for i in items if isinstance(i, dict)}
        out = []
        for b in brand_list:
            it = mapped.get(b) or {"brand_name": b, "sentiment": "中性", "positive_mentions": [],
                                   "negative_mentions": []}
            # 进一步强兜底字段
            out.append({
                "brand_name": it.get("brand_name", b),
                "sentiment": it.get("sentiment", "中性") if it.get("sentiment") in ("正向", "负向", "中性") else "中性",
                "positive_mentions": it.get("positive_mentions") if isinstance(it.get("positive_mentions"),
                                                                               list) else [],
                "negative_mentions": it.get("negative_mentions") if isinstance(it.get("negative_mentions"),
                                                                               list) else [],
            })
        return out

    except Exception:
        return _fallback_items(brand_list)

def ai_get_brand_sentiment_and_mentions(brand_list, ai_answer_text):
    """
    入参:
      - brand_list: list[str]  品牌列表（顺序很重要）
      - ai_answer_text: str    AI回答文本

    返回:
      list[dict]，格式:
      [
        {
          "brand_name": "...",
          "sentiment": "正向|负向|中性",
          "positive_mentions": [...],
          "negative_mentions": [...]
        },
        ...
      ]
    """

    url = "https://ark.cn-beijing.volces.com/api/v3/chat/completions"

    system_prompt = """
你是一位专业的品牌舆情与GEO内容分析专家。
你需要根据「AI回答内容」对「指定品牌列表」逐个输出每个品牌的：

1) sentiment：情感倾向，仅允许 ["正向","负向","中性"]
   - 若无法判断，必须输出 "中性"
   - sentiment 必须与该品牌在文本中的语境一致，禁止凭空编造

2) positive_mentions：该品牌在文本中的正面提及词/短语 例如：优势、好评、推荐、认可、亮点、强项、信任等
3) negative_mentions：该品牌在文本中的负面提及词/短语 例如：缺点、差评、风险、争议、吐槽、弱点等


强制规则：
- 必须覆盖品牌列表中的每个品牌，输出 items 数组长度必须等于品牌列表长度，并尽量保持顺序一致
- 只提取文本中真实出现或可直接归纳的词/短语，禁止编造
- 必须与该品牌直接相关（若无法指向品牌，则不要提取）
- 每条词/短语严格 2~10 个字
- 同义词合并去重（如“舒服/舒适”只保留更标准的）
- 若某品牌在文本中没有可提取内容，则 positive_mentions / negative_mentions 返回空数组 []
- 输出必须严格符合 JSON Schema
- 仅输出纯 JSON，不要输出任何额外文字
""".strip()

    payload = {
        "model": "doubao-seed-1-6-250615",
        "messages": [
            {"role": "system", "content": system_prompt},
            {
                "role": "user",
                "content": f"品牌列表：{json.dumps(brand_list, ensure_ascii=False)}\n\nAI回答内容：{ai_answer_text}"
            }
        ],
        "response_format": {
            "type": "json_schema",
            "json_schema": {
                "name": "brand_sentiment_and_mentions",
                "strict": True,
                "schema": {
                    "type": "object",
                    "properties": {
                        "items": {
                            "type": "array",
                            "items": {
                                "type": "object",
                                "properties": {
                                    "brand_name": {"type": "string"},
                                    "sentiment": {
                                        "type": "string",
                                        "enum": ["正向", "负向", "中性"]
                                    },
                                    "positive_mentions": {
                                        "type": "array",
                                        "items": {"type": "string"}
                                    },
                                    "negative_mentions": {
                                        "type": "array",
                                        "items": {"type": "string"}
                                    }
                                },
                                "required": ["brand_name", "sentiment", "positive_mentions", "negative_mentions"],
                                "additionalProperties": False
                            }
                        }
                    },
                    "required": ["items"],
                    "additionalProperties": False
                }
            }
        },
        "thinking": {"type": "disabled"},
        "temperature": 0,
        "top_p": 1
    }

    headers = {
        "Authorization": "Bearer ark-7afc3be2-37a8-47fd-9f02-996258a3d305-27da0",
        "Content-Type": "application/json"
    }

    try:
        resp = requests.post(url, headers=headers, data=json.dumps(payload), timeout=60)
        resp.raise_for_status()
        data = resp.json()

        content = data["choices"][0]["message"]["content"]
        parsed = json.loads(content)

        items = parsed.get("items", [])
        if not isinstance(items, list):
            return _fallback_items(brand_list)

        # ✅ 兜底：保证每个品牌都有一条（并且按 brand_list 顺序）
        mapped = {i.get("brand_name"): i for i in items if isinstance(i, dict)}
        out = []
        for b in brand_list:
            it = mapped.get(b) or {"brand_name": b, "sentiment": "中性", "positive_mentions": [],
                                   "negative_mentions": []}
            # 进一步强兜底字段
            out.append({
                "brand_name": it.get("brand_name", b),
                "sentiment": it.get("sentiment", "中性") if it.get("sentiment") in ("正向", "负向", "中性") else "中性",
                "positive_mentions": it.get("positive_mentions") if isinstance(it.get("positive_mentions"),
                                                                               list) else [],
                "negative_mentions": it.get("negative_mentions") if isinstance(it.get("negative_mentions"),
                                                                               list) else [],
            })
        return out

    except Exception:
        return _fallback_items(brand_list)


def _fallback_items(brand_list):
    return [{"brand_name": b, "sentiment": "中性", "positive_mentions": [], "negative_mentions": []} for b in
            brand_list]

def ai_get_product_and_brands(content, prompt):
    url = "https://ark.cn-beijing.volces.com/api/v3/chat/completions"
    payload = json.dumps({
        "model": "doubao-seed-1-6-250615",
        "messages": [
            {
                "role": "system",
                "content": """
                # 角色
你是一名GEO语义解析专家。
你的任务是：根据输入的问题和对话内容，提取对话中与该问题直接相关的产品词，并为每个产品词匹配对应的品牌词。
# 任务目标
从对话中识别所有被提及的具体产品，但只保留与当前问题语义直接相关的产品。
如果某些产品虽然出现在对话中，但只是顺带提及、与问题讨论主题无关，则不要提取。
# 输入内容
用户会提供：
1）{{question}}（监测问题）
2）{{answer_text}}（AI回答正文）
# 提取规则
1. 产品词定义
产品词是指对话中出现的、能够指向具体商品或具体产品名称的表达，例如：
品牌 + 产品名：如“SK2神仙水”“兰蔻小黑瓶”
产品别名 / 常用名：如“神仙水”
明确可识别品牌归属的具体产品表达
2. 品牌词定义 
品牌词是该产品所属的品牌名称。
输出时需尽量统一为标准品牌名。
3. 相关性判断规则（定义）
只提取与question直接相关的产品词。
应提取
当该产品属于以下情况之一时，可以提取：
是问题中明确询问、比较、推荐、评价、选择的对象
与问题讨论的品类、功效、使用场景、购买决策直接相关
在回答中作为该问题的候选产品、对比产品、推荐产品出现
不应提取
当该产品属于以下情况时，不要提取：
虽然出现在对话里，但和问题主题无关
只是附带提到的物流、平台、服务、赠品、渠道相关内容
与问题讨论的品类、用途、决策场景无直接关系
4. 过滤规则
以下内容一律不提取：
- 无法确认品牌归属的泛产品词：如“面霜”“精华”
- 非产品内容
- 与问题主题无关的产品
5. 去重规则
- 同一产品重复出现时只保留一次
- 优先保留信息更完整的表达
# 输出规则
1. 输出要求
- 先识别所有“产品-品牌”配对，再按品牌分组聚合输出
- 按品牌聚合输出，同一品牌下的多个产品放入同一个 products 数组。
- products 按对话中出现顺序输出。
- 如果某品牌只有一个产品，也仍然使用数组格式。
- 如果没有符合条件的结果，输出空数组 []。
                """
            },
            {
                "role": "user",
                "content": f"监测问题:{prompt}AI回答正文：{content}"
            }
        ],
        "response_format": {
            "type": "json_schema",
            "json_schema": {
                "name": "brand_product_list",
                "strict": True,
                "schema": {
                    "type": "object",
                    "properties": {
                        "items": {
                            "type": "array",
                            "description": "按品牌聚合后的产品列表",
                            "items": {
                                "type": "object",
                                "properties": {
                                    "brand": {
                                        "type": "string",
                                        "description": "标准品牌名"
                                    },
                                    "products": {
                                        "type": "array",
                                        "description": "该品牌下与问题直接相关的产品列表，按原文出现顺序输出",
                                        "items": {
                                            "type": "string"
                                        }
                                    }
                                },
                                "required": ["brand", "products"],
                                "additionalProperties": False
                            }
                        }
                    },
                    "required": ["items"],
                    "additionalProperties": False
                }
            }
        },
        "thinking": {
            "type": "disabled"
        },
        "temperature": 0
    })
    headers = {
        'Authorization': 'Bearer ark-7afc3be2-37a8-47fd-9f02-996258a3d305-27da0',
        'Content-Type': 'application/json'
    }

    response = requests.request("POST", url, headers=headers, data=payload)
    try:
        result = response.json()
        content_str = result["choices"][0]["message"]["content"]
        return json.loads(content_str).get("items", [])
    except Exception:
        return []


def ai_get_brand_sentiment_and_mentions_by_articles(brand_name, article_list, batch_size=50):
    """
    入参:
      - brand_name: str
          单个品牌名，例如: "珂润"

      - article_list: list[dict]
          文章列表，例如:
          [
            {"sourceId": "1001", "snippet": "文章内容1..."},
            {"sourceId": "1002", "snippet": "文章内容2..."},
            ...
          ]

      - batch_size: int
          每批送给模型的文章数，默认 20

    返回:
      list[dict]，格式:
      [
        {
          "sourceId": "1001",
          "brand_name": "珂润",
          "sentiment": "正向|负向|中性",
          "positive_mentions": [...],
          "negative_mentions": [...]
        },
        ...
      ]
    """

    normalized_articles = []
    for item in article_list or []:
        if not isinstance(item, dict):
            continue

        source_id = item.get("sourceId")
        if source_id is None:
            continue

        normalized_articles.append({
            "sourceId": str(source_id),
            "snippet": item.get("snippet", "") or ""
        })

    if not normalized_articles:
        return []

    all_result_map = {}

    for batch in _chunk_list(normalized_articles, batch_size):
        batch_result = _call_single_batch(brand_name, batch)
        for item in batch_result:
            if not isinstance(item, dict):
                continue

            source_id = item.get("sourceId")
            if source_id is None:
                continue

            all_result_map[str(source_id)] = {
                "sourceId": str(source_id),
                "brand_name": brand_name,
                "sentiment": item.get("sentiment", "中性") if item.get("sentiment") in ("正向", "负向", "中性") else "中性",
                "positive_mentions": item.get("positive_mentions") if isinstance(item.get("positive_mentions"), list) else [],
                "negative_mentions": item.get("negative_mentions") if isinstance(item.get("negative_mentions"), list) else [],
            }

    result = []
    for item in normalized_articles:
        source_id = item["sourceId"]
        result.append(
            all_result_map.get(source_id, {
                "sourceId": source_id,
                "brand_name": brand_name,
                "sentiment": "中性",
                "positive_mentions": [],
                "negative_mentions": []
            })
        )

    return result


def _call_single_batch(brand_name, batch_articles):
    """
    单批调用模型，返回该批文章的分析结果
    """
    url = "https://ark.cn-beijing.volces.com/api/v3/chat/completions"

    system_prompt = """
你是一位专业的品牌舆情与GEO内容分析专家。

你会收到：
1）一个品牌名
2）多篇文章，每篇文章都带 sourceId 和 snippet

你的任务是：
分析“这个品牌”在“每一篇文章”中的表现，并输出：

1) sourceId：文章ID，必须与输入一致
2) sentiment：情感倾向，仅允许 ["正向","负向","中性"]
   - 若无法判断，必须输出 "中性"
   - sentiment 必须与该品牌在该文章中的语境一致，禁止凭空编造

3) positive_mentions：该品牌在该文章中的正面提及词/短语
4) negative_mentions：该品牌在该文章中的负面提及词/短语

强制规则：
- 只分析指定品牌，不要分析其他品牌
- 只提取文章中真实出现或可直接归纳的词/短语，禁止编造
- 必须与该品牌直接相关（若无法指向该品牌，则不要提取）
- 每条词/短语严格 2~10 个字
- 同义词合并去重
- 若该品牌在某文章中没有可提取内容，则 positive_mentions / negative_mentions 返回空数组 []
- 返回你能明确判断的文章结果即可
- 输出必须严格符合 JSON Schema
- 仅输出纯 JSON，不要输出任何额外文字
""".strip()

    input_articles = [
        {
            "sourceId": str(item["sourceId"]),
            "snippet": item.get("snippet", "")
        }
        for item in batch_articles
    ]

    payload = {
        "model": "doubao-seed-1-6-250615",
        "messages": [
            {"role": "system", "content": system_prompt},
            {
                "role": "user",
                "content": (
                    f"品牌名：{brand_name}\n\n"
                    f"文章列表：{json.dumps(input_articles, ensure_ascii=False)}"
                )
            }
        ],
        "response_format": {
            "type": "json_schema",
            "json_schema": {
                "name": "single_brand_article_sentiment_and_mentions",
                "strict": True,
                "schema": {
                    "type": "object",
                    "properties": {
                        "items": {
                            "type": "array",
                            "items": {
                                "type": "object",
                                "properties": {
                                    "sourceId": {"type": "string"},
                                    "sentiment": {
                                        "type": "string",
                                        "enum": ["正向", "负向", "中性"]
                                    },
                                    "positive_mentions": {
                                        "type": "array",
                                        "items": {"type": "string"}
                                    },
                                    "negative_mentions": {
                                        "type": "array",
                                        "items": {"type": "string"}
                                    }
                                },
                                "required": [
                                    "sourceId",
                                    "sentiment",
                                    "positive_mentions",
                                    "negative_mentions"
                                ],
                                "additionalProperties": False
                            }
                        }
                    },
                    "required": ["items"],
                    "additionalProperties": False
                }
            }
        },
        "thinking": {"type": "disabled"},
        "temperature": 0,
        "top_p": 1
    }

    headers = {
        "Authorization": "Bearer ark-7afc3be2-37a8-47fd-9f02-996258a3d305-27da0",
        "Content-Type": "application/json"
    }

    batch_source_ids = [str(i["sourceId"]) for i in batch_articles]

    try:
        resp = requests.post(url, headers=headers, data=json.dumps(payload), timeout=120)
        resp.raise_for_status()
        data = resp.json()

        content = data["choices"][0]["message"]["content"]
        parsed = json.loads(content)

        items = parsed.get("items", [])
        if not isinstance(items, list):
            return _fallback_single_brand_items(brand_name, batch_source_ids)

        result = []
        seen_source_ids = set()

        for it in items:
            if not isinstance(it, dict):
                continue

            source_id = it.get("sourceId")
            if source_id is None:
                continue

            source_id = str(source_id)

            if source_id not in batch_source_ids:
                continue

            if source_id in seen_source_ids:
                continue
            seen_source_ids.add(source_id)

            result.append({
                "sourceId": source_id,
                "brand_name": brand_name,
                "sentiment": it.get("sentiment", "中性") if it.get("sentiment") in ("正向", "负向", "中性") else "中性",
                "positive_mentions": it.get("positive_mentions") if isinstance(it.get("positive_mentions"), list) else [],
                "negative_mentions": it.get("negative_mentions") if isinstance(it.get("negative_mentions"), list) else [],
            })

        return result

    except Exception:
        return _fallback_single_brand_items(brand_name, batch_source_ids)


def _fallback_single_brand_items(brand_name, source_id_list):
    return [
        {
            "sourceId": str(source_id),
            "brand_name": brand_name,
            "sentiment": "中性",
            "positive_mentions": [],
            "negative_mentions": []
        }
        for source_id in source_id_list
    ]


def _chunk_list(data, batch_size):
    if batch_size <= 0:
        batch_size = 20
    for i in range(0, len(data), batch_size):
        yield data[i:i + batch_size]



if __name__ == '__main__':
    # phone_ = 18900000015
    # begin = '2026-03-22'
    # end = '2026-03-22'
    # req_ids = get_req_id(18900000015,begin,end)

    # 去重，避免 SQL 重复
    # req_ids = list(dict.fromkeys(req_ids))

    # 2. 拼 SQL IN
    # req_ids = ['4f6a9e90-0a4e-49a2-9c6d-4cdb51df317a']
    #
    # req_id_sql = ",".join([f"'{req_id}'" for req_id in req_ids])
    #
    # query_sql = f"select * from geo_commit_task where reqId in ({req_id_sql})"
    # # print(query_sql)
    # query_list = bh_utils.query_data(query_sql)
    #
    # for i in query_list:
    #     prompt =  i.get('prompt')
    #     taskId =  i.get('taskId')
    #     platform =  i.get('platform')
    #     content = get_string_from_tos(f'geo/{taskId}/{platform}/context.txt')
    #     print(f'geo/{taskId}/{platform}/context.txt')\
    content =  """
*   **要本地味** → 可以试试**金永丰**，但要注意渠道和日期。
*   **要最方便** → 去**大润发、家乐福**等大型超市，选择多，品质稳。
    """
    brand_list  =['六必居'
'大润发',
'家乐福',
'崔字牌',
'海南金永丰',
'金永丰',
                  # fcc424e5-58af-494d-9683-5787413a26c9
]
    print(ai_get_brand_list(brand_list,content))
    # print(ai_result)
    # pro =
    # ai_get_product_list()
