"""
角色差异化分镜模板
扒自：worker/src/image.js buildImageStoryboard storyboardByRole(222-321)

每个分镜角色有【各自不同】的画面/文字/构图策略，这是"小红书风格不雷同"的根因。
之前缩水：6张共用同一个品类 proof 策略 → 图全长一样。
模板里 {占位} 在 storyboard.py 运行时按文案/卖点填充。

字段含义（对齐 promptFromStoryboard 9 字段）：
  goal      本张目标（这张图要让用户产生什么动作/情绪）
  overlay   图上主文字模板（每张不同，不重复封面标题）
  visual    画面主体（构图、景别、道具、光线——这是不雷同的关键）
  basis     文案依据（这张图从文案哪里来，给模型锚点）
  forbidden 本张禁止事项
"""
from __future__ import annotations
import re

# ── sanitize（扒 sanitizeImagePlanText，防违禁视觉词进 prompt）
_SANITIZE_RULES: list[tuple[str, str]] = [
    (r"before\s*&\s*after", "质地与肤感说明"),
    (r"before\s*/?\s*after", "质地与肤感说明"),
    (r"\bbefore\b", "质地状态"),
    (r"\bafter\b", "上脸肤感"),
    (r"使用前后|用前用后|用前后|前后对比|使用前|使用后", "质地/场景/肤感说明"),
    (r"功效对比|效果对比|改善对比", "质地/场景说明对比"),
    (r"肤色变白|皮肤变白|变白|美白", "自然光泽感"),
    (r"瑕疵消失|斑点消失|痘印消失|消除瑕疵|祛斑", "妆感更服帖"),
    (r"治疗前后|治疗后|医美前后|治愈|修复受损", "日常使用场景说明"),
]


def sanitize_text(value: str, max_len: int = 56) -> str:
    s = str(value)
    for pattern, repl in _SANITIZE_RULES:
        s = re.sub(pattern, repl, s, flags=re.IGNORECASE)
    return re.sub(r"\s+", " ", s).strip()[:max_len]


# 北哥6张标准套 + 8张扩展角色，每角色独立画面策略
ROLE_STORYBOARD_TPL: dict[str, dict] = {
    "hook": {
        "goal": "让{audience}因为{pain}停下划走，产生点开欲",
        "overlay": "{hook}",
        "visual": "自然光生活场景，手持产品或产品在桌面前景，真实肤感/手部细节，像iPhone随手实拍的封面，不是海报",
        "basis": "来自选中文案标题、人群{audience}、痛点{pain}",
        "forbidden": "不要价格、不要重复后续卖点、不要App界面、不要广告海报感",
    },
    "product_closeup": {
        "goal": "建立单品记忆锚点，让用户记住是哪个产品",
        "overlay": "{brand}",
        "visual": "单品高清特写居中，干净浅色台面，柔和顶光，瓶身/包装/标签清晰可读，品牌词自然出现在画面或瓶身",
        "basis": "来自产品名和品牌词，第2张和第6张都要带品牌词强化记忆",
        "forbidden": "不要堆多个产品、不要花哨背景抢主体、不要改包装文字",
    },
    "ingredient": {
        "goal": "用成分/配方信息建立信任，但不医疗化",
        "overlay": "看清{point}",
        "visual": "成分卡片式布局，产品+成分图标/短说明，浅色商务美妆风，信息层级清楚",
        "basis": "来自卖点里的成分/功效点，理性表达不夸大",
        "forbidden": "不要治疗/改善疾病承诺、不要医生背书、不要绝对化",
    },
    "texture": {
        "goal": "让用户看到{point}的真实质感证据",
        "overlay": "{point}看得见",
        "visual": "手背或指尖涂抹质地微距，产品放在旁边，自然光，保留真实皮肤纹理，能看清延展和肤感",
        "basis": "来自卖点里的质地/肤感描述",
        "forbidden": "不要生成变白效果、不要医疗化对比、不要和封面同构图",
    },
    "applied_proof": {
        "goal": "用可感知的上脸/使用证据证明{point}",
        "overlay": "{proof_overlay}",
        "visual": "{proof_visual}",
        "basis": "来自核心卖点{point}和用户对效果的关注",
        "forbidden": "{proof_forbidden}",
    },
    "closer": {
        "goal": "用囤货/省钱情报/搜索暗号完成软性转化",
        "overlay": "这波真的会囤 {brand}",
        "visual": "拆箱、囤货角或产品放在日常物品旁，真实分享氛围，轻量搜索/品牌词暗号提示，再带一次品牌词引导成交",
        "basis": "来自价格心智/选择理由，但不做硬广",
        "forbidden": "不要大促价格牌、不要购买按钮、不要红黄电商风",
    },
    # ── 8张扩展角色 ──
    "pain_scene": {
        "goal": "让用户共鸣{pain}",
        "overlay": "{pain}真的懂",
        "visual": "{scene}里的真实困扰场景，产品作为解决方案线索出现，不做使用前后对比",
        "basis": "来自文案痛点和目标人群",
        "forbidden": "不要夸大焦虑、不要before/after",
    },
    "social_proof": {
        "goal": "补足信任背书，让内容不像单方面推销",
        "overlay": "身边人都在问",
        "visual": "产品在包里/桌面/宿舍囤货角，配简短手写感反馈气泡，真实随手拍",
        "basis": "来自评论区语言/选择理由，缺评论时用低调口碑表达",
        "forbidden": "不要假造大量头像评论、不要App评论区截图",
    },
    "scenario": {
        "goal": "展示{scene}以外的多场景使用代入",
        "overlay": "这些场景都能用",
        "visual": "2-3个生活小场景拼贴：宿舍/通勤包/办公桌，产品贯穿其中，统一光线",
        "basis": "来自目标人群的多场景使用需求",
        "forbidden": "不要电商详情页拼贴、不要夸大效果",
    },
    "tutorial": {
        "goal": "降低使用门槛，告诉用户怎么用",
        "overlay": "三步就上手",
        "visual": "三步手势教程：取量、点涂/使用、收尾，干净背景，产品在画面内",
        "basis": "来自文案里的快速/懒人使用场景",
        "forbidden": "不要复杂说明书、不要过多文字",
    },
}


def role_template(role: str) -> dict:
    """取角色模板，未知角色用 applied_proof 兜底（和源头一致）"""
    return ROLE_STORYBOARD_TPL.get(role, ROLE_STORYBOARD_TPL["applied_proof"])


# ── proofStrategy（按品类定 applied_proof 证明页策略，扒 image.js:163-208）
# 品类来自 product.category，不硬编码枚举；无匹配走"通用好物"兜底
PROOF_STRATEGIES: dict[str, dict] = {
    "个护护理": {
        "overlay_tpl": "{point}看得见",
        "visual": "手部/身体局部使用证明：少量点涂、推开后吸收状态、真实纹理和自然光；产品只做辅助露出",
        "asset_use": "优先使用实拍/参考图中的手部、干纹、涂抹、随身场景；产品图保证包装准确",
        "forbidden": "不要变白、祛斑、医学效果、before/after字样；不要和封面同构图",
    },
    "美妆护肤": {
        "overlay_tpl": "{point}看得见",
        "visual": "肤感/质地证明：手背、脸颊局部或质地微距，展示推开前后真实状态，保留皮肤纹理和自然光",
        "asset_use": "优先使用实拍/参考图中的手背、上脸、质地素材；产品图辅助露出",
        "forbidden": "不要变白、祛斑、医学效果、before/after字样；不要和封面同构图",
    },
    "食品饮品": {
        "overlay_tpl": "{point}一眼懂",
        "visual": "冲泡/开袋/入口证明：展示包装、杯中状态、质地颜色或一口口感，真实桌面光线",
        "asset_use": "产品图保证包装准确，参考图用于杯子、开袋、冲泡、办公室/居家场景",
        "forbidden": "不要涂抹、不要护肤肤感、不要医疗健康承诺",
    },
    "营养健康": {
        "overlay_tpl": "看清{point}",
        "visual": "理性证明页：包装、成分表、使用场景和每日习惯卡片，信息清晰但不做治疗承诺",
        "asset_use": "产品图和说明图用于成分/包装准确，参考图用于日常使用场景",
        "forbidden": "不要治疗、改善疾病、速效、医生背书、前后对比",
    },
    "家居生活": {
        "overlay_tpl": "{point}真省事",
        "visual": "使用过程证明：展示痛点场景、产品介入和使用过程细节，强调顺手/收纳/效率",
        "asset_use": "参考图用于真实家居环境，产品图保证外观准确",
        "forbidden": "不要护肤涂抹，不要虚假夸大结果",
    },
    "服饰穿搭": {
        "overlay_tpl": "{point}有细节",
        "visual": "上身/材质证明：展示面料纹理、版型细节或普通身材上身局部，真实自然",
        "asset_use": "参考图用于上身/搭配/材质，产品图保证款式颜色准确",
        "forbidden": "不要护肤涂抹，不要过度精修模特感",
    },
    "通用好物": {
        "overlay_tpl": "{point}清晰可见",
        "visual": "产品使用场景证明：真实道具/场景，展示产品细节和使用过程",
        "asset_use": "产品图保证准确，参考图用于场景辅助",
        "forbidden": "不要夸大效果，不要硬广式价格牌",
    },
}


def proof_strategy(category: str, point: str) -> dict:
    """取品类证明策略，无匹配用通用兜底（扒 proofStrategy）"""
    s = PROOF_STRATEGIES.get(category, PROOF_STRATEGIES["通用好物"]).copy()
    s["overlay"] = s.pop("overlay_tpl", "{point}").format(point=point)
    return s

