"""§4 场景 C · 失败回路 + TDD 救场 · slide 57-68 (12 内容页) · v0.2 真实内容版

嵌入 6 张图（A2/D3/C3/F2/A5/A6/D4）+ 5 张自画版面 + 引用 04-review-report.md
"""

from pathlib import Path
from pptx.util import Inches
from pptx.enum.text import PP_ALIGN

from build_main import (
    _fill_ph, _add_textbox, _add_card,
    NAVY, CORAL, CORAL_LIGHT, BLUE_ACCENT, GREEN, GOLD,
    GRAY_300, GRAY_500, GRAY_700, WHITE,
)
from build_sec_M1 import _add_centered_image, _title


def build_slide_57_fail_loop(prs):
    s = prs.slides.add_slide(prs.slide_layouts[3])
    _title(s, "§5 · 失败回路总览 · 6 阶段门任一失败 → 回 ③ 实现")
    _add_centered_image(s, "A2-six-stages.png")
    return s


def build_slide_58_dual_verdict(prs):
    s = prs.slides.add_slide(prs.slide_layouts[3])
    _title(s, "§5 · code-reviewer 双 verdict · 代码 × SIT Audit 独立")
    _add_centered_image(s, "D3-reviewer-dual-verdict.png")
    return s


def build_slide_59_role_boundary(prs):
    s = prs.slides.add_slide(prs.slide_layouts[3])
    _title(s, "§5 · code-reviewer 红线 · 永远不动源码")
    _add_centered_image(s, "C3-role-boundary.png")
    return s


def build_slide_60_sit_audit_4(prs):
    s = prs.slides.add_slide(prs.slide_layouts[3])
    _title(s, "§5 · SIT Audit 4 项 · reviewer 在 review 时审 progress", size_pt=24)
    items = [
        ("①  链路完整性", "5 道盲测题走完三步链路 · 每步输入输出可追溯（stdout 含时间戳）"),
        ("②  AC 覆盖",     "5 条 AC 全部跑通 · 实测数据贴 progress（不能口述）"),
        ("③  证据真实",     "sit-log 文件存在 · 输出 SHA256 列出 · 引用文件实存"),
        ("④  反例边界",     "3 类异常必跑（违禁内容 / 大文件 / 纯文字图）· 每类有 stdout 证据"),
    ]
    for i, (head, body) in enumerate(items):
        y = Inches(1.7 + i * 1.05)
        _add_card(s, Inches(0.55), y, Inches(12.2), Inches(0.95),
                  fill=WHITE, border=NAVY, accent=NAVY)
        _add_textbox(s, Inches(0.85), y + Inches(0.15), Inches(3.2), Inches(0.6),
                     head, size_pt=22, bold=True, color=NAVY)
        _add_textbox(s, Inches(4.2), y + Inches(0.22), Inches(8.4), Inches(0.55),
                     body, size_pt=14, color=GRAY_700)
    _add_textbox(s, Inches(0.55), Inches(6.4), Inches(12.2), Inches(0.4),
                 "完整 review 报告样例 · 04-review-report.md（PR #287 PII 防护缺失案例）",
                 size_pt=12, color=GRAY_500, align=PP_ALIGN.CENTER)
    _add_textbox(s, Inches(0.55), Inches(6.9), Inches(12.2), Inches(0.4),
                 "SIT 缺证据比代码烂更严重 · '讲不清楚做对了'比代码烂更危险",
                 size_pt=13, color=CORAL, bold=True, align=PP_ALIGN.CENTER)
    return s


def build_slide_61_receiving_review(prs):
    s = prs.slides.add_slide(prs.slide_layouts[3])
    _title(s, "§5 · dev 收 review · 4 原则（同名 skill）")
    principles = [
        ("①  不盲改",           "每条 review 评估技术合理性，理解'为什么'"),
        ("②  不 performative 同意", "不为了快速过 review 假装同意（事后会被打回）"),
        ("③  技术不同意 push back", "带数据 / 测试 / 设计取舍证据反驳"),
        ("④  改完不 amend",      "新 commit 而非 amend · 保留 review 轨迹"),
    ]
    for i, (head, body) in enumerate(principles):
        y = Inches(1.7 + i * 1.3)
        color = [CORAL, GOLD, NAVY, GREEN][i]
        _add_card(s, Inches(0.55), y, Inches(12.2), Inches(1.15),
                  fill=WHITE, border=color, accent=color)
        _add_textbox(s, Inches(0.85), y + Inches(0.2), Inches(4.5), Inches(0.7),
                     head, size_pt=18, bold=True, color=color)
        _add_textbox(s, Inches(5.5), y + Inches(0.3), Inches(7.2), Inches(0.65),
                     body, size_pt=13, color=GRAY_700)
    return s


def build_slide_62_push_back(prs):
    s = prs.slides.add_slide(prs.slide_layouts[3])
    _title(s, "§5 · Push back 真实案例 · 文生图风格评分 A/B 实验")
    _add_centered_image(s, "F2-pushback-bar.png")
    return s


def build_slide_63_tdd_cycle(prs):
    """v1.3: 改 PPT 自画三角循环（借 template t-10）· 与 §0 p-009 风格统一"""
    from build_sec_M1 import _draw_tdd_triangle
    s = prs.slides.add_slide(prs.slide_layouts[3])
    _title(s, "§5 · TDD 救场 · Red → Green → Refactor（Kent Beck Canon）")
    _draw_tdd_triangle(s, center_text="AGF\n强制路径",
                       center_subtext="commit + skill + DoD 三层兜底")
    # 底部 AGF 三层兜底注解
    _add_textbox(s, Inches(0.55), Inches(6.6), Inches(12.2), Inches(0.5),
                 "AGF 三层兜底：commit 顺序检查（test: 必须早于 feat:）+ skill 强制 + DoD checklist + reviewer 看 git log",
                 size_pt=13, color=CORAL, align=PP_ALIGN.CENTER, bold=True)
    _add_textbox(s, Inches(0.55), Inches(7.05), Inches(12.2), Inches(0.4),
                 "Kent Beck《Test-Driven Development: By Example》2002 · ISBN 0321146530",
                 size_pt=11, color=GRAY_500, align=PP_ALIGN.CENTER)
    return s


def build_slide_64_tdd_enforcement(prs):
    """v1.3: 改 PPT 自画 4 齿轮（大圆水平排列 + 违规漏过箭头串联）
    替代 v1.2 A6 drawio 嵌入（圆内文字越界 + PNG 低分辨率问题）
    """
    from pptx.enum.shapes import MSO_SHAPE as _MS
    from pptx.util import Pt as _Pt
    from pptx.enum.text import MSO_ANCHOR as _MA2
    s = prs.slides.add_slide(prs.slide_layouts[3])
    _title(s, "§5 · TDD 强制链路 · 4 齿轮兜底（任一失效不影响其他）")

    # 4 个大圆参数（直径 2.6 in，避免文字越界）
    D = 2.6
    R = D / 2
    gap = 0.4
    total_w = 4 * D + 3 * gap  # 11.6 in
    start_x = (13.333 - total_w) / 2  # 居中
    y_center = 4.0  # 圆心 y

    gears = [
        # (label, sub1, sub2, sub3, fill, font_color)
        ("① commit 顺序",
         "test: 早于 feat:",
         "reviewer 看 git log",
         "判违规",
         CORAL, WHITE),
        ("② Skill 强制",
         "agf-running-sit-tests",
         "test-driven-development",
         "skill 不走不行",
         GOLD, NAVY),
        ("③ DoD checklist",
         "Unit + SIT 必填",
         "无证据 = 不可 done",
         "PBI 层硬要求",
         NAVY, WHITE),
        ("④ reviewer 审",
         "git log --oneline",
         "test commit 缺位",
         "→ request changes",
         GREEN, WHITE),
    ]

    for i, (label, s1, s2, s3, fc, tc) in enumerate(gears):
        cx = start_x + R + i * (D + gap)
        x = cx - R
        y = y_center - R
        circle = s.shapes.add_shape(_MS.OVAL,
                                     Inches(x), Inches(y),
                                     Inches(D), Inches(D))
        circle.fill.solid(); circle.fill.fore_color.rgb = fc
        circle.line.color.rgb = NAVY; circle.line.width = _Pt(2)
        # 圆内文字（4 行：label + 3 sub），居中
        _add_textbox(s, Inches(x), Inches(y + 0.32), Inches(D), Inches(0.5),
                     label, size_pt=16, bold=True, color=tc,
                     align=PP_ALIGN.CENTER, anchor=_MA2.MIDDLE)
        for j, sub in enumerate([s1, s2, s3]):
            _add_textbox(s, Inches(x + 0.15), Inches(y + 1.0 + j * 0.42),
                         Inches(D - 0.3), Inches(0.4),
                         sub, size_pt=11, color=tc,
                         align=PP_ALIGN.CENTER, anchor=_MA2.MIDDLE)

    # 3 条"违规漏过"红箭头（圆之间）
    for i in range(3):
        from_cx = start_x + R + i * (D + gap) + R  # 第 i 圆右边
        to_cx = start_x + R + (i + 1) * (D + gap) - R  # 第 i+1 圆左边
        line = s.shapes.add_connector(2,
                                       Inches(from_cx), Inches(y_center),
                                       Inches(to_cx), Inches(y_center))
        line.line.color.rgb = CORAL; line.line.width = _Pt(2.5)
        # 末端箭头
        from pptx.oxml.ns import qn as _qn
        from lxml import etree as _etree
        spPr = line.line._get_or_add_ln()
        tail = _etree.SubElement(spPr, _qn("a:tailEnd"))
        tail.set("type", "triangle"); tail.set("w", "lg"); tail.set("len", "lg")
        # 箭头上方标 "违规漏过"
        lbl_x = (from_cx + to_cx) / 2 - 0.4
        _add_textbox(s, Inches(lbl_x), Inches(y_center - 0.35),
                     Inches(0.8), Inches(0.3),
                     "违规漏过", size_pt=10, color=CORAL, bold=True,
                     align=PP_ALIGN.CENTER)

    # 底部金句
    _add_textbox(s, Inches(0.55), Inches(6.0), Inches(12.2), Inches(0.5),
                 "任一齿轮失效不影响其他 · 4 道关卡 + 4 道兜底 = 8 次拦截机会",
                 size_pt=15, bold=True, color=CORAL, align=PP_ALIGN.CENTER)
    _add_textbox(s, Inches(0.55), Inches(6.55), Inches(12.2), Inches(0.4),
                 "真实失效场景示例：",
                 size_pt=12, bold=True, color=NAVY, align=PP_ALIGN.CENTER)
    _add_textbox(s, Inches(0.55), Inches(6.95), Inches(12.2), Inches(0.4),
                 "dev 跳过 ① commit 顺序 → ② skill 强制要求跑 SIT → ③ DoD 检查 SIT 证据 → ④ reviewer 看 git log 反推",
                 size_pt=10, color=GRAY_700, align=PP_ALIGN.CENTER)
    return s


def build_slide_65_qa_verdict_tree(prs):
    s = prs.slides.add_slide(prs.slide_layouts[3])
    _title(s, "§5 · qa-engineer Verdict 决策树 · 默认 Fail")
    _add_centered_image(s, "D4-qa-verdict-tree.png")
    return s


def build_slide_66_qa_default_fail(prs):
    s = prs.slides.add_slide(prs.slide_layouts[3])
    _title(s, "§5 · qa Verdict 默认 Fail · 没证据不升 Pass")
    _add_textbox(s, Inches(0.55), Inches(1.6), Inches(12.2), Inches(0.7),
                 "设计意图：让 qa 主动证明'为什么应该 pass'，而非寻找'为什么不能 pass'",
                 size_pt=18, color=CORAL, align=PP_ALIGN.CENTER, bold=True)
    _add_textbox(s, Inches(0.55), Inches(2.7), Inches(12.2), Inches(0.5),
                 "🔍 升 Pass 必备的 3 类证据（≥ 1）：",
                 size_pt=18, bold=True, color=NAVY)
    evidences = [
        ("📸  截图",   "前端 / 小程序 UI 渲染截图（含 URL + 时间戳）"),
        ("curl",   "API 实测 stdout（含 request + response + headers）"),
        ("💾  DB diff", "执行前后 DB 状态对比（pg_dump diff 或 select count）"),
    ]
    for i, (head, body) in enumerate(evidences):
        y = Inches(3.5 + i * 0.95)
        _add_card(s, Inches(0.55), y, Inches(12.2), Inches(0.85),
                  fill=WHITE, border=GREEN, accent=GREEN)
        _add_textbox(s, Inches(0.85), y + Inches(0.15), Inches(2.5), Inches(0.55),
                     head, size_pt=20, bold=True, color=GREEN)
        _add_textbox(s, Inches(3.5), y + Inches(0.22), Inches(9.0), Inches(0.5),
                     body, size_pt=14, color=GRAY_700)
    _add_textbox(s, Inches(0.55), Inches(6.6), Inches(12.2), Inches(0.4),
                 "反例：qa 写 'AC1 通过' 但没贴 curl → reviewer 拒收 → 自动 Fail",
                 size_pt=13, color=CORAL, align=PP_ALIGN.CENTER, bold=True)
    return s


def build_slide_67_plan_mode(prs):
    s = prs.slides.add_slide(prs.slide_layouts[3])
    _title(s, "§5 · ai-agent-dev Plan Mode 6 触发点 · 必先拿 PL 授权")
    triggers = [
        ("①  prompt 重构",    "如三步合二步、prompt 模板大改"),
        ("②  模型切换",        "豆包 → DeepSeek / Qwen 等跨厂商"),
        ("③  链路结构变",      "增 / 减节点 / 改 fallback 链"),
        ("④  Token 预算变",    "单次调用 token 上限 / cache 策略调"),
        ("⑤  工具调用变",      "新增外部 API（如 search / function calling）"),
        ("⑥  数据 schema 变",   "DB 表 / API contract 影响下游"),
    ]
    from build_main import rotate4
    for i, (head, body) in enumerate(triggers):
        row = i // 2
        col = i % 2
        x = Inches(0.55 + col * 6.2)
        y = Inches(1.7 + row * 1.3)
        c = rotate4(i)
        _add_card(s, x, y, Inches(6.0), Inches(1.1),
                  fill=WHITE, border=c, accent=c)
        _add_textbox(s, x + Inches(0.3), y + Inches(0.15), Inches(3.5), Inches(0.55),
                     head, size_pt=20, bold=True, color=c)
        _add_textbox(s, x + Inches(0.3), y + Inches(0.65), Inches(5.5), Inches(0.4),
                     body, size_pt=13, color=GRAY_700)
    _add_textbox(s, Inches(0.55), Inches(6.7), Inches(12.2), Inches(0.4),
                 "任一触发点 → ai-agent-dev SendMessage PL 写 Plan + 等授权 → 不擅自启动",
                 size_pt=13, color=NAVY, align=PP_ALIGN.CENTER, bold=True)
    return s


def build_slide_68_no_source(prs):
    s = prs.slides.add_slide(prs.slide_layouts[3])
    _title(s, "§5 · 为什么 review / QA 不动源码 · 设计取舍")
    _add_textbox(s, Inches(0.55), Inches(1.6), Inches(12.2), Inches(0.7),
                 "核心取舍：避免'既当裁判又当球员'",
                 size_pt=22, bold=True, color=CORAL, align=PP_ALIGN.CENTER)
    # 前 2 反例（CORAL），后 2 正例（GREEN）
    pros = [
        ("✗  reviewer 改源码？", "他自己写的代码自己审 → 同人盲点", CORAL),
        ("✗  qa 改源码？",        "qa 同时改实现 → 测什么呢？测自己写的", CORAL),
        ("✓  review 只写 docs/reviews/",  "强制说出'哪里有问题、为什么'", GREEN),
        ("✓  qa 只写 docs/qa/",            "强制贴证据，不能用代码混过", GREEN),
    ]
    for i, (head, body, c) in enumerate(pros):
        y = Inches(2.8 + i * 0.95)
        _add_card(s, Inches(0.55), y, Inches(12.2), Inches(0.85),
                  fill=WHITE, border=c, accent=c)
        _add_textbox(s, Inches(0.85), y + Inches(0.15), Inches(4.5), Inches(0.55),
                     head, size_pt=18, bold=True, color=c)
        _add_textbox(s, Inches(5.5), y + Inches(0.22), Inches(7.0), Inches(0.5),
                     body, size_pt=14, color=GRAY_700)
    _add_textbox(s, Inches(0.55), Inches(6.9), Inches(12.2), Inches(0.4),
                 "反对意见可走 push back（带证据），但不能直接动源码 → 这是机制不是建议",
                 size_pt=12, color=GRAY_500, align=PP_ALIGN.CENTER)
    return s


def build(prs):
    """构建 §4 12 张内容 slide (slide 57-68)"""
    build_slide_57_fail_loop(prs)
    build_slide_58_dual_verdict(prs)
    build_slide_59_role_boundary(prs)
    build_slide_60_sit_audit_4(prs)
    build_slide_61_receiving_review(prs)
    build_slide_62_push_back(prs)
    build_slide_63_tdd_cycle(prs)
    build_slide_64_tdd_enforcement(prs)
    build_slide_65_qa_verdict_tree(prs)
    build_slide_66_qa_default_fail(prs)
    build_slide_67_plan_mode(prs)
    build_slide_68_no_source(prs)
