#!/usr/bin/env python3
"""
soke-ai-coach-director · 闸门一次性校验器（v2.42）
Usage:
    python3 scripts/gate_check.py /tmp/scenario_get.json

输入：+get-scenario 重定向后的 JSON 文件（自动处理 {data:{...}} 嵌套）
输出：单行结果 —— 全过输出 "GATE PASS"，否则 "GATE FAIL" + 逐项失败原因
退出码：0 = 通过，1 = 失败

设计目的：消除闸门阶段逐字段拆轮探查（历史实测浪费 ~8 积分）。
AI 只需跑这一个脚本，禁止再用 Python 逐步看返回结构。
"""

import json
import sys


def unwrap(raw):
    """处理 CLI 返回的可能嵌套：{data:{...}} / {code,data} / 裸对象。"""
    if not isinstance(raw, dict):
        return raw
    d = raw.get("data")
    if isinstance(d, dict):
        return d
    return raw


def check(sc: dict) -> list:
    errs = []

    # ---------- Step 8 发布闸门（4 项，任一缺失禁止发布） ----------

    # 1. 封面 = 固定默认图（非空且正确前缀）
    cover = sc.get("scenario_cover") or sc.get("cover")
    if not cover or "roles_avatars" in str(cover) or not str(cover).startswith("http"):
        errs.append(f"[发布1] 封面缺失或非法: {cover!r:.80}")

    # 2/3. 全部角色 avatar / voice_id 非空
    roles = sc.get("ai_roles") or []
    if not roles:
        errs.append("[发布2] ai_roles 为空")
    for i, r in enumerate(roles):
        rd = r.get("role_data") or r
        av = rd.get("avatar")
        vi = rd.get("voice_id")
        if not av or not str(av).startswith("http"):
            errs.append(f"[发布2] 角色{i}({rd.get('name','?')}) avatar 非法: {av!r:.60}")
        if not vi:
            errs.append(f"[发布3] 角色{i}({rd.get('name','?')}) voice_id 为空")

    # 4. 无"角色数据异常"：OS 存在且足够长
    os_text = sc.get("ai_roles_overall_description") or ""
    if len(os_text) < 500:
        errs.append(f"[发布4] OS 过短({len(os_text)}<500)，疑似角色数据异常")

    # ---------- Step 7 补查 3 项（Part 自检已过的 9 项跳过） ----------

    # 8. Part 内容完整：name/key_points/tags 非空
    if not sc.get("name"):
        errs.append("[闸门8] 场景名为空")
    if not sc.get("key_points"):
        errs.append("[闸门8] key_points 为空（检查 Part1 是否误写数组）")
    if not (sc.get("tags") and len(sc.get("tags", [])) >= 3):
        errs.append("[闸门8] tags 少于 3 个")
    if sc.get("scene_lang") not in {"zh", "tw", "en"}:
        errs.append(f"[闸门8] scene_lang 必须是 zh、tw 或 en，当前 {sc.get('scene_lang')!r}")

    # 12. 整体设定 ≤ 3000 字
    if len(os_text) > 3000:
        errs.append(f"[闸门12] OS 超长({len(os_text)}>3000)")

    # 5. Part3 环节存在（回读 conversation_steps 非空；空则提示 CLI 限制，不阻塞）
    steps = sc.get("conversation_steps") or []
    if not steps:
        print("WARN: conversation_steps 为空——已知 CLI/平台限制，发布后走 Web 后台补录（不阻塞）")

    # 6. Part4 评分维度存在且权重和 = 100
    cd = ((sc.get("scoring_criteria_config") or {}).get("criteria_data")) or sc.get("criteria_data") or {}
    dims = cd.get("dimensions") or []
    if not dims:
        print("WARN: 评分维度在 get 返回中不可见——以 Part4 自检 + apply 成功为准（不阻塞）")
    else:
        w = sum(d.get("weight", 0) for d in dims)
        if w != 100:
            errs.append(f"[闸门6→发布] 维度权重和={w}≠100")

    return errs


def main() -> int:
    if len(sys.argv) != 2:
        print("Usage: gate_check.py <scenario_get.json>", file=sys.stderr)
        return 1
    try:
        with open(sys.argv[1], encoding="utf-8") as f:
            raw = json.load(f)
    except Exception as e:
        print(f"GATE FAIL: JSON 读取失败 {e}", file=sys.stderr)
        return 1

    sc = unwrap(raw)
    errs = check(sc)
    if errs:
        print("GATE FAIL")
        for e in errs:
            print(f"  {e}")
        return 1
    print("GATE PASS")
    return 0


if __name__ == "__main__":
    sys.exit(main())
