"""
图片 Prompt 增强模块 — 利用旁白语义 + 模版视觉指南生成高质量图片提示词。

纯函数模块，无外部依赖。主要入口:
  - build_enhanced_prompt()  : 根据 purpose/topic/style/narration/styleGuide 构建增强 prompt
  - enrich_asset_payload()   : 一站式入口，自动判断并增强 asset payload
"""

import re
from typing import Optional

# ---------------------------------------------------------------------------
# 常量
# ---------------------------------------------------------------------------

# 口语化填充词 / 无视觉意义的片段
_FILLER_PATTERNS = re.compile(
    r"(那么|其实|所以说|然后呢|接下来|大家好|你好|对吧|对不对|是不是|"
    r"嗯|啊|呢|吧|哦|哈|了|的话|来说|而言|就是说|换句话说|"
    r"如果觉得有用就|点个关注|下期见|别忘了)"
)

# 标点符号归一化
_PUNCT = re.compile("[，。！？、；：\u201c\u201d\u2018\u2019《》【】（）\\s]+")

# 默认按 purpose 的 prompt 模板（无模版 styleGuide 时的兜底）
DEFAULT_PURPOSE_PATTERNS: dict[str, str] = {
    "opening": "{narration_summary}，{topic}主题概念图，视觉冲击力强，{style_suffix}，高清大图",
    "intro": "{narration_summary}，{topic}主题引入场景，{style_suffix}，清晰直观",
    "point": "{narration_summary}，{style_suffix}，聚焦核心概念的场景插画，清晰直观",
    "example": "{narration_summary}，{style_suffix}，具体案例示意图，写实与图解结合",
    "explanation": "{narration_summary}，{style_suffix}，解释性图示，信息图表风格",
    "highlight": "{narration_summary}，{style_suffix}，重点概念高亮，视觉聚焦",
    "transition": "{topic}相关过渡画面，{style_suffix}，简洁流畅",
    "cta": "关注点赞互动图标，{style_suffix}，简洁现代设计，明亮醒目",
    "ending": "感谢观看结尾画面，{style_suffix}，温馨大气",
}

# prompt 长度阈值：超过此字符数的 prompt 视为已精心编写
WELL_CRAFTED_THRESHOLD = 80

# ---------------------------------------------------------------------------
# 工具函数
# ---------------------------------------------------------------------------


def summarize_narration(text: str, max_chars: int = 60) -> str:
    """从旁白文本提取视觉关键信息，去除口语化填充词。

    >>> summarize_narration("那么接下来我们来看一下AI如何帮你高效背单词")
    'AI如何帮你高效背单词'
    """
    if not text:
        return ""
    # 去除填充词
    cleaned = _FILLER_PATTERNS.sub("", text)
    # 归一化标点 → 空格
    cleaned = _PUNCT.sub(" ", cleaned).strip()
    # 去除开头尾部空白
    if not cleaned:
        return text[:max_chars]
    # 截断到 max_chars
    if len(cleaned) > max_chars:
        cleaned = cleaned[:max_chars]
    return cleaned


def extract_scene_context(scene: dict) -> dict:
    """从 DSL scene 提取视觉相关上下文。

    Returns:
        dict with keys: narration_text, title, badge, purpose
    """
    purpose = scene.get("purpose", "default")
    narration_text = ""
    audio = scene.get("audio", {})
    narration = audio.get("narration", {})
    if isinstance(narration, dict):
        narration_text = narration.get("text", "")

    title = ""
    badge = ""
    for layer in scene.get("textLayers", []):
        role = layer.get("role", "")
        if role == "title" and not title:
            title = layer.get("content", "")
        elif role == "badge" and not badge:
            badge = layer.get("content", "")

    return {
        "narration_text": narration_text,
        "title": title,
        "badge": badge,
        "purpose": purpose,
    }


def resolve_style_guide(template: Optional[dict], variant_id: str = "") -> dict:
    """合并模版根级 + 变体级的 imageStyleGuide。

    变体级字段覆盖根级同名字段，purposeOverrides 做 merge。
    """
    if not template:
        return {}

    base = dict(template.get("imageStyleGuide", {}) or {})
    if not variant_id:
        return base

    variant = (template.get("variants", {}) or {}).get(variant_id, {})
    variant_guide = dict(variant.get("imageStyleGuide", {}) or {})
    if not variant_guide:
        return base

    # 合并 purposeOverrides
    base_overrides = dict(base.get("purposeOverrides", {}) or {})
    variant_overrides = dict(variant_guide.get("purposeOverrides", {}) or {})

    merged = {**base, **variant_guide}
    if base_overrides or variant_overrides:
        merged_po = {**base_overrides, **variant_overrides}
        merged["purposeOverrides"] = merged_po

    return merged


# ---------------------------------------------------------------------------
# 核心构建函数
# ---------------------------------------------------------------------------


def build_enhanced_prompt(
    purpose: str,
    topic: str,
    style: str = "",
    narration_text: str = "",
    scene_title: str = "",
    style_guide: Optional[dict] = None,
) -> dict:
    """构建增强 prompt，返回 {prompt, negative_prompt, guidance_scale}。

    优先使用 style_guide 中的模板和修饰符；无 style_guide 时使用内置默认模板。
    """
    style_guide = style_guide or {}
    style_suffix = f"{style}风格" if style else ""

    # 提取旁白摘要
    narration_summary = summarize_narration(narration_text)
    if not narration_summary and scene_title:
        narration_summary = scene_title
    if not narration_summary:
        narration_summary = topic or ""

    # 基础变量
    vars_ = {
        "narration_summary": narration_summary,
        "topic": topic or "",
        "style_suffix": style_suffix,
        "scene_title": scene_title or "",
    }

    # 决定 prompt 模板
    purpose_overrides = style_guide.get("purposeOverrides", {}) or {}
    override = purpose_overrides.get(purpose, {})

    prompt_template = override.get("promptTemplate", "")
    if not prompt_template:
        prompt_template = DEFAULT_PURPOSE_PATTERNS.get(
            purpose, DEFAULT_PURPOSE_PATTERNS.get("point", "")
        )

    # 格式化模板（安全替换）
    prompt = _safe_format(prompt_template, vars_)

    # 追加 styleModifier
    style_modifier = override.get("styleModifier", "")
    if style_modifier:
        prompt = f"{prompt}，{style_modifier}"

    # 追加 baseStyle
    base_style = style_guide.get("baseStyle", "")
    if base_style:
        prompt = f"{prompt}，{base_style}"

    # 追加 colorDirective
    color_directive = style_guide.get("colorDirective", "")
    if color_directive:
        prompt = f"{prompt}，{color_directive}"

    # negative prompt
    negative_prompt = override.get("negativePrompt", "") or style_guide.get("negativePrompt", "")

    # guidance scale
    guidance_scale = style_guide.get("guidanceScale")

    result = {"prompt": prompt}
    if negative_prompt:
        result["negativePrompt"] = negative_prompt
    if guidance_scale is not None:
        result["guidanceScale"] = guidance_scale

    return result


def _safe_format(template: str, vars_: dict) -> str:
    """安全的字符串格式化，缺失变量保留占位符原文。"""
    try:
        return template.format(**vars_)
    except (KeyError, IndexError):
        result = template
        for k, v in vars_.items():
            result = result.replace("{" + k + "}", v)
        return result


# ---------------------------------------------------------------------------
# 一站式入口
# ---------------------------------------------------------------------------


def enrich_asset_payload(
    payload: dict,
    scene: Optional[dict] = None,
    template: Optional[dict] = None,
    dsl_meta: Optional[dict] = None,
    variant_id: str = "",
) -> dict:
    """增强 asset payload 中的图片 prompt。

    - prompt 已超过 WELL_CRAFTED_THRESHOLD 字符的，仅补充 negativePrompt（如缺失）
    - 否则用 build_enhanced_prompt 重新构建

    Returns:
        新的 payload dict（不修改原始对象）
    """
    payload = dict(payload)
    current_prompt = payload.get("prompt", "")

    meta = dsl_meta or {}
    topic = meta.get("topic", meta.get("title", ""))
    style = meta.get("style", "")

    style_guide = resolve_style_guide(template, variant_id)

    # 已精心编写的 prompt → 仅补充 negativePrompt
    if len(current_prompt) > WELL_CRAFTED_THRESHOLD:
        if "negativePrompt" not in payload and style_guide.get("negativePrompt"):
            payload["negativePrompt"] = style_guide["negativePrompt"]
        if "guidanceScale" not in payload and style_guide.get("guidanceScale") is not None:
            payload["guidanceScale"] = style_guide["guidanceScale"]
        return payload

    # 从 scene 提取上下文
    scene_ctx = extract_scene_context(scene) if scene else {}
    purpose = scene_ctx.get("purpose", "point")
    narration_text = scene_ctx.get("narration_text", "")
    scene_title = scene_ctx.get("title", "") or scene_ctx.get("badge", "")

    enhanced = build_enhanced_prompt(
        purpose=purpose,
        topic=topic,
        style=style,
        narration_text=narration_text,
        scene_title=scene_title,
        style_guide=style_guide,
    )

    payload["prompt"] = enhanced["prompt"]
    if "negativePrompt" in enhanced:
        payload["negativePrompt"] = enhanced["negativePrompt"]
    if "guidanceScale" in enhanced:
        payload["guidanceScale"] = enhanced["guidanceScale"]

    return payload
