"""
_text_prompt.py — 文案 prompt 组装 / JSON 解析 / 本地模板兜底
方法层（全品类共用）：人设/变量池/5步框架/四段结构/反AI味规则
数据层（每产品各异）：由 product dict 动态注入，代码不出现具体品牌/成分名
"""
from __future__ import annotations
import json
import random
import re

# ── Q4 佛系反推销人设（全品类共用，数据层产品名从product动态注入）──────────
_PERSONA = """你是一个日常生活分享博主，不是品牌推广号。
核心人设：佛系、不推销、真实记录日常。
内核：不主动劝买，靠真实体验让读者自己动心；写的是生活，产品只是生活的一部分。
比例：70% 写生活场景和使用感受，30% 提产品，绝不颠倒。
收尾铁律：每条结尾方式必须不同，禁止使用"东西放这了/买不买跟我没关系"这类被用滥的固定句式。"""

# ── Q1 随机变量池 ABC（反同质化核心，每次随机抽组合，N条不撞）──────────────
# 方法层：框架固定，内容可扩展，绝不写死
_POOL_A_IDENTITY: list[str] = [
    "上班族早八妆前随手抹",
    "宿舍懒人护肤三分钟搞定",
    "敏感肌妈妈哄完娃才有五分钟",
    "学生党第一次用高价护肤品",
    "素颜出门前最后一步",
]

_POOL_B_EMOTION: list[str] = [
    "看到镜子里发现气色暗了一周",
    "闺蜜问你最近皮肤怎么这么好",
    "出门被催快点根本来不及叠瓶",
]

_POOL_C_FLAW: list[str] = [
    "第一次用量太少了没啥感觉",
    "包装简单到以为是山寨",
    "价格摆在那以为会很油很厚",
]


def _pick_combo() -> dict[str, str]:
    """随机抽一组 ABC 变量（每次生成调用一次，N条各不相同）"""
    return {
        "identity": random.choice(_POOL_A_IDENTITY),
        "emotion":  random.choice(_POOL_B_EMOTION),
        "flaw":     random.choice(_POOL_C_FLAW),
    }


# ── Q5 negative词：prompt级别负向约束，不让AI写进正文 ─────────────────────
_NEGATIVE_WORDS = (
    "神器、福音、救急单品、遮羞布、日常维稳、精简底妆、"
    "不仅而且、焕发、守护、尽享、日常维稳、"
    "按头安利、绝绝子、闭眼冲、杀疯了、YYDS"
)

# ── emoji 规则（适度有表情，倩倩姐2026-06-08拍板：像真人发的小红书）──────────
_EMOJI_RULES = """
【emoji表情（适度有表情，必须遵守）】
- 卖点小标题前加 emoji：✅ 卖点1 / ✨ 卖点2 / 🌿 卖点3（每条卖点1个，符合语义）
- 正文段落可点缀少量 emoji 烘托情绪（如 🥹 😭 🤍 💛），每段最多1-2个，不堆砌
- 结尾话题标签前后带表情，如 "#好物分享 🛒"
- emoji 服务情绪和分点，不要每句都加；整条正文 emoji 总量控制在 6-12 个
- 常用小红书 emoji 池：✅✨🌿💧🪞🧴📦🔍💛🤍🥹😭🛒（按语义选，不乱用）
""".strip()

# ── Q2 5步框架 + Q3 四段结构 ─────────────────────────────────────────────
_STRUCTURE_RULES = """
【5步框架（必须严格遵循）】
① Hook暴击低价/痛点：第一句戳中场景或价格锚点，吊足读者好奇
② 痛点共鸣：2-3句描写使用前的真实困境（用上面抽到的起因情绪A·B）
③ 救星登场：自然带出产品，口吻是"碰巧发现/朋友安利/囤货时顺手"，不是"推荐给你"
④ 卖点罗列（每条加✅小标题）：3条以内，卖点翻译成使用感受，不是功效列表
⑤ 收尾（每条必须从下方策略池随机选一种，同批次不得重复同一种，禁止"东西放这了/买不买跟我没关系"此类固定句式）：
  【收尾策略池·每条选不同策略】
  A·留白式感受：只说自己现在的状态，不提买不买，如"反正现在素颜出门我不慌了"
  B·反问读者：把感受抛回给读者，如"你们护肤有没有那种一用就回不去的东西？"
  C·场景延续：把故事延伸到未来某个细节，如"下次同事再问我皮肤的事我就知道说啥了"
  D·克制回购暗示：轻描淡写说自己行为，如"第一罐用完了，已经在备第二罐"
  E·纯记录收笔：像日记最后一句，不引导不评价，如"大概就这样，记录一下"
  F·引导搜索（仅在有品牌词时使用）：自然提一句，如"感兴趣可以搜搜『{品牌词}』"，不催单

【正文四段结构（必须）】
段1·痛点引入：描写使用前的困境/触发场景（身份场景A·起因情绪B）
段2·实测记录：真实使用过程，带上小缺点C（真实感来源）
段3·种草核心：产品带来的变化，用感受描述而非功效声称
段4·引导收尾：从收尾策略池随机选一种，佛系口吻，不强推，末尾带1-2个相关话题标签

【字数】正文350-400字（不含标题tags），3-5处段落空行增强可读性
""".strip()

# ── Q8 标题公式（5类结构，每批次覆盖不同类型，不重复）──────────────────────
_TITLE_FORMULA = """
【标题公式（5类，每条用不同类型，禁止同批重复）】
肤质型：「{肤质}+用了{产品}+{感受}」例→"油皮用了素颜霜整个夏天不脱妆"
价格型：「{价格锚点}+{产品}+{功效感受}」例→"三位数买到大牌平替，用完第一罐回购第二罐"
功效型：「{使用场景}+{产品}+{可感知变化}」例→"早起素颜出门靠这罐，同事问我最近皮肤怎么了"
夸张型：「疑问/感叹+{夸张感受}+{产品}」例→"这什么神奇产品，涂上去感觉素颜也能出门了"
标题党型：「{反常识/意外信息}+真相是{翻转}」例→"被人说皮肤变好了，没说的是我用了它一个月"
硬性约束：标题≤20字；禁止出现"绝绝子/YYDS/杀疯了"；不直接写功效词（美白/祛斑等）。
""".strip()
_ANTI_AI = f"""
【反AI味必须遵守】
- 禁止固定开篇套话：不许"姐妹们/宝子们/今天给大家分享"开头
- 禁止以下AI味词出现在正文：{_NEGATIVE_WORDS}
- 禁止人群重叠：{'{count}'}条文案中身份场景不能重复（靠变量池ABC保证）
- 禁止场景重复：同批次文案不能都是"早上上班"或都是"学生宿舍"
- 避免生硬推销词：按头安利/绝绝子/闭眼冲 不能出现
""".strip()

# ── 系统 prompt（合并Q2/Q3/Q4/Q7/Q8，数据层走build_prompt动态注入）──────────
COPY_SYSTEM = f"""{_PERSONA}

合规红线（全品类通用）：
- 禁用"美白、祛斑、速效、医用、药妆"，不得暗示治疗或改善疾病
- 不得承诺效果，不得出现before/after变白对比暗示
- 图文合规：避免社交App界面、点赞评论等截图元素

{_ANTI_AI.format(count="N")}

{_TITLE_FORMULA}

{_EMOJI_RULES}

{_STRUCTURE_RULES}

返回纯JSON数组，每条字段：
title（标题≤20字）/ content（正文，严格350-400字，按emoji规则适度带表情）/ tags（list，3-5个#话题）
angle（本条角度标签）/ coverTitle（封面大字≤10字）/ imageBrief（配图方向）

硬性格式：只输出JSON，不要markdown代码块，字符串内用中文引号「」。"""


def build_prompt(product: dict, count: int, extra_rules: str = "") -> str:
    """
    组装文案生成 user_prompt。
    数据层：product 动态注入（name/selling_points/style_tone/text_angles/custom_prompt）
    方法层：已在 COPY_SYSTEM 固定，这里只注入产品数据+随机变量
    """
    name     = product.get("name", "产品")
    selling  = "、".join(product.get("selling_points") or ["核心卖点待录入"])
    style    = product.get("style_tone", "素人日常分享风")
    angles   = product.get("text_angles") or []
    custom   = (product.get("custom_prompt") or "").strip()
    brand_kw = (product.get("brand_keyword") or "").strip()

    # Q1：每条抽一组随机变量，传给模型作角色约束
    combos = [_pick_combo() for _ in range(count)]
    combos_text = "\n".join(
        f"  第{i+1}条：身份场景=「{c['identity']}」·起因情绪=「{c['emotion']}」·小缺点=「{c['flaw']}」"
        for i, c in enumerate(combos)
    )

    angle_hint = f"文案角度要覆盖：{'、'.join(angles)}（每条用不同角度）。" if angles else ""
    brand_rule = f"每条正文和标题中植入品牌词「{brand_kw}」一次（自然融入，不生硬）。" if brand_kw else ""

    lines = [
        f"产品：{name}",
        f"核心卖点（必须翻译成用户能感知的生活化利益，禁止直接列功效词；翻译范例：'烟酰胺'→'熬夜后第二天脸不那么黄了'，'高保湿'→'涂上去一整天都没搓泥拔干'）：{selling}",
        f"风格调性：{style}",
        angle_hint,
        brand_rule,
        custom,
        f"\n【Q1随机变量池·每条身份/起因/小缺点各不相同，严格按下方分配使用】",
        combos_text,
        extra_rules,
        f"\n请严格按5步框架+四段结构生成 {count} 条，每条350-400字，返回纯JSON数组。",
    ]
    return "\n".join(l for l in lines if l)


def parse_json_array(raw: str) -> list[dict]:
    """从模型输出提取 JSON 数组（容错 markdown 包裹）"""
    text = re.sub(r"```(?:json)?", "", raw).strip()
    start, end = text.find("["), text.rfind("]")
    if start == -1 or end == -1:
        return []
    try:
        return json.loads(text[start:end + 1])
    except json.JSONDecodeError:
        return []


def build_local_drafts(product: dict, count: int) -> list[dict]:
    """本地模板兜底（保证永不空手，遵循四段结构）
    角度从 text_angles 循环取（保证N条角度各不同，不被 dedupe_copies 吞掉）
    """
    name       = product.get("name", "产品")
    points     = product.get("selling_points") or ["使用方便", "真实可感知"]
    # 从产品档案取角度池，不够就用通用角度兜底，保证每条都不同
    _fallback_angles = ["生活场景型", "成分分析型", "使用感受型", "性价比型", "痛点切入型"]
    angles_pool = product.get("text_angles") or _fallback_angles
    for i in range(count):
        c = _pick_combo()
        # 循环取不同角度（角度相同的两条会被 dedupe_copies 过滤掉，所以必须不重复）
        angle = angles_pool[i % len(angles_pool)]
        yield {
            "title": f"发现一个{name}的{angle}用法",
            "content": (
                f"{c['identity']}，{c['emotion']}。\n\n"
                f"用了一段时间，{points[i % len(points)]}这点最让我意外。\n\n"
                f"说个小缺点：{c['flaw']}，后来才摸到感觉。\n\n"
                f"反正现在用顺手了。✅"
            ),
            "tags": [f"#{name}", "#真实测评", "#好物分享"],
            "angle": angle,
            "coverTitle": f"{name}{angle}",
            "imageBrief": "封面产品近景，内页核心卖点+真实使用场景。",
        }
