#!/usr/bin/env python3
"""
检查 MoFin prompt-manager 的跨提示词依赖一致性。

功能：
1. 对每个有 depends_on 的 prompt，验证上游 prompt 的 current_version 匹配
2. 对每个有 impacts 的 prompt，验证下游 prompt 的 depends_on.version 是否已正确更新
3. 输出状态：✅ 全部一致 / ⚠️ 有差异（列出差异详情）

用法：
  python3 check-prompt-deps.py
  python3 check-prompt-deps.py --registry /path/to/registry.json

集成方式：
  每次做策略分析前，或每次更新提示词后，运行一次确认一致性。
"""

import json
import sys
import os
from pathlib import Path

DEFAULT_REGISTRY = "/home/hmo/projects/MoFin/data/prompts/registry.json"

def load_registry(path: str) -> dict:
    with open(path, "r", encoding="utf-8") as f:
        return json.load(f)

def prompt_map(registry: dict) -> dict:
    """将 prompts 数组转为 id -> prompt 的字典"""
    return {p["id"]: p for p in registry.get("prompts", [])}

def check_depends_on(pm: dict) -> list:
    """检查所有 depends_on 声明是否匹配上游的 current_version"""
    issues = []
    for p in pm.values():
        deps = p.get("depends_on")
        if not deps:
            continue
        target_id = deps.get("prompt")
        expected_ver = deps.get("version")
        target = pm.get(target_id)
        if not target:
            issues.append({
                "type": "MISSING_UPSTREAM",
                "prompt": p["id"],
                "detail": f"依赖的上游提示词 '{target_id}' 在 registry 中不存在"
            })
            continue
        actual_ver = target.get("current_version")
        if actual_ver != expected_ver:
            issues.append({
                "type": "VERSION_MISMATCH",
                "prompt": p["id"],
                "detail": (
                    f"{p['id']}.depends_on → {target_id}@{expected_ver}, "
                    f"但 {target_id}.current_version = {actual_ver}"
                )
            })
    return issues

def check_impacts_fulfilled(pm: dict) -> list:
    """检查有 impacts 声明的 prompt，其下游是否已更新 depends_on 到当前版本"""
    issues = []
    for p in pm.values():
        impacted = p.get("impacts")
        if not impacted:
            continue
        my_id = p["id"]
        my_ver = p.get("current_version")
        for downstream_id in impacted:
            downstream = pm.get(downstream_id)
            if not downstream:
                issues.append({
                    "type": "MISSING_DOWNSTREAM",
                    "prompt": my_id,
                    "detail": f"impacts 中列出的 '{downstream_id}' 在 registry 中不存在"
                })
                continue
            dd = downstream.get("depends_on", {})
            expected_ver = dd.get("version") if dd.get("prompt") == my_id else None
            if expected_ver is None:
                issues.append({
                    "type": "MISSING_DEPENDS_ON",
                    "prompt": my_id,
                    "detail": (
                        f"{my_id}.impacts 声明了 {downstream_id}, "
                        f"但 {downstream_id} 没有 depends_on → {my_id}"
                    )
                })
            elif expected_ver != my_ver:
                issues.append({
                    "type": "STALE_DEPENDENCY",
                    "prompt": my_id,
                    "detail": (
                        f"{my_id}.current_version = {my_ver}, "
                        f"但 {downstream_id}.depends_on.version = {expected_ver}"
                    )
                })
    return issues

def check_integrity(pm: dict) -> list:
    """基本格式校验"""
    issues = []
    for p in pm.values():
        deps = p.get("depends_on")
        if deps:
            if not deps.get("prompt") or not deps.get("version"):
                issues.append({
                    "type": "INVALID_DEPENDS_ON",
                    "prompt": p["id"],
                    "detail": "depends_on 结构不完整，需包含 prompt 和 version"
                })
            if not deps.get("reason"):
                issues.append({
                    "type": "MISSING_REASON",
                    "prompt": p["id"],
                    "detail": "depends_on 缺少 reason 字段（说明为什么依赖）"
                })
        impacts = p.get("impacts")
        if impacts and not isinstance(impacts, list):
            issues.append({
                "type": "INVALID_IMPACTS",
                "prompt": p["id"],
                "detail": "impacts 必须是数组"
            })
    return issues

def main():
    reg_path = DEFAULT_REGISTRY
    if len(sys.argv) > 2 and sys.argv[1] == "--registry":
        reg_path = sys.argv[2]
    if not os.path.exists(reg_path):
        print(f"❌ 找不到 registry 文件: {reg_path}")
        sys.exit(1)

    registry = load_registry(reg_path)
    pm = prompt_map(registry)

    all_issues = []
    all_issues.extend(check_integrity(pm))
    all_issues.extend(check_depends_on(pm))
    all_issues.extend(check_impacts_fulfilled(pm))

    print(f"📋 MoFin 提示词版本依赖检查 ({registry.get('updated_at', '?')})")
    print(f"   共 {len(pm)} 个提示词")
    print()

    if not all_issues:
        print("✅ 全部一致，无依赖问题")
        # 打印当前依赖状态一览
        print()
        for p in pm.values():
            deps = p.get("depends_on")
            impacts = p.get("impacts")
            status = p["id"]
            if deps:
                status += f" ← 依赖 {deps['prompt']}@{deps['version']}"
            if impacts:
                status += f" → 影响 {', '.join(impacts)}"
            print(f"  {status}")
        sys.exit(0)

    # 按类型分组输出
    categories = {
        "MISSING_UPSTREAM": "上游提示词缺失",
        "VERSION_MISMATCH": "依赖版本不匹配",
        "MISSING_DOWNSTREAM": "下游提示词缺失",
        "MISSING_DEPENDS_ON": "下游缺少 depends_on 声明",
        "STALE_DEPENDENCY": "下游依赖版本未同步更新",
        "INVALID_DEPENDS_ON": "depends_on 结构不完整",
        "MISSING_REASON": "缺少依赖原因说明",
        "INVALID_IMPACTS": "impacts 格式错误",
    }

    has_error = False
    for issue in all_issues:
        cat = categories.get(issue["type"], issue["type"])
        marker = "❌" if issue["type"] in ("VERSION_MISMATCH", "STALE_DEPENDENCY", "MISSING_UPSTREAM") else "⚠️"
        if marker == "❌":
            has_error = True
        print(f"{marker} [{issue['prompt']}] {cat}")
        print(f"   {issue['detail']}")

    print()
    if has_error:
        print("❗ 存在必须修复的依赖问题，请在更新提示词前先处理。")
    else:
        print("💡 无版本冲突，但上面有信息缺失/规范问题，建议补充。")

    sys.exit(1 if has_error else 0)

if __name__ == "__main__":
    main()
