"""
meta_growth.py — 元成长回路

分析修复型 commit 的模式，自动生成检测规则，注入监控管线。
Phase 3 核心组件，与 Tier3 漂移检测配合使用。

用法:
    # 分析最近修复并生成规则（手动）
    python meta_growth.py --analyze

    # 查看当前规则清单
    python meta_growth.py --list

    # 激活规则（Stage 1 → 2）
    python meta_growth.py --activate <rule_id>

工作流:
    git log (fix commits)
      → diff_analyzer.py (提取变更模式)
      → classifier.py (分类修复类型)
      → rule_generator.py (生成检测规则)
      → grow_rules/growth_meta.json (持久化)
      → health_check 管线消费
"""
import os, sys, re, json, subprocess, hashlib
from datetime import datetime, timezone

# ─── 路径 ───────────────────────────────────────────────
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
GROW_RULES_DIR = os.path.join(SCRIPT_DIR, "grow_rules")
TEMPLATES_PATH = os.path.join(SCRIPT_DIR, "pattern_templates", "templates.json")
GROWTH_META_PATH = os.path.join(GROW_RULES_DIR, "growth_meta.json")

# 项目根目录（用于 git 操作）
_PROJECT_DIR = os.path.normpath(os.path.join(SCRIPT_DIR, "..", ".."))
_AMS_ROOT = os.environ.get("AGENTSMEETING_ROOT", "")
if _AMS_ROOT:
    _PROJECT_DIR = _AMS_ROOT

os.makedirs(GROW_RULES_DIR, exist_ok=True)

# ─── 修复类型分类规则 ─────────────────────────────────
FIX_PATTERNS = [
    {
        "category": "port_misconfig",
        "name": "端口/health 端点变更",
        "patterns": [
            r'port\s*[:=]\s*\d{4,5}',
            r'health.*endpoint',
            r':\d{4,5}/health',
            r'health_check.*url',
        ],
        "check_type": "port_health",
        "confidence_base": 0.7,
    },
    {
        "category": "path_error",
        "name": "文件路径变更",
        "patterns": [
            r'os\.path\.(join|exists|normpath)',
            r'Path\(.*\)',
            r'sys\.path\.insert',
            r'__file__',
            r'AGENTSMEETING_ROOT',
        ],
        "check_type": "file_exists",
        "confidence_base": 0.6,
    },
    {
        "category": "env_var",
        "name": "环境变量变更",
        "patterns": [
            r'os\.environ\.get',
            r'environ\[',
            r'environ\.get\(',
        ],
        "check_type": "env_check",
        "confidence_base": 0.65,
    },
    {
        "category": "cmd_arg",
        "name": "命令/参数变更",
        "patterns": [
            r'subprocess\.run\(\[',
            r'subprocess\.Popen\(\[',
            r'pkill\b',
            r'fuser\b',
            r'pgrep\b',
        ],
        "check_type": "cmd_available",
        "confidence_base": 0.6,
    },
    {
        "category": "timeout",
        "name": "超时/重试变更",
        "patterns": [
            r'timeout\s*[:=]\s*\d+',
            r'settimeout',
            r'retry\b',
            r'CREATION_NO_WINDOW',
            r'threaded\s*[:=]\s*True',
        ],
        "check_type": "timeout_threshold",
        "confidence_base": 0.7,
    },
    {
        "category": "import_fix",
        "name": "导入/依赖变更",
        "patterns": [
            r'^import\s+\w+',
            r'^from\s+\w+\s+import',
            r'ModuleNotFoundError',
            r'ImportError',
        ],
        "check_type": "dependency",
        "confidence_base": 0.5,
    },
    {
        "category": "permission",
        "name": "权限/安全变更",
        "patterns": [
            r'chmod\b',
            r'ssh\b.*key',
            r'token\b',
            r'repr\(',
            r'escap\w+\(',
        ],
        "check_type": "security_scan",
        "confidence_base": 0.4,
    },
]


def git_log(fmt="%h %ad %s", n=20, date_fmt="%m-%d %H:%M", diff_filter="", pathspec=""):
    """执行 git log 返回行列表"""
    cmd = ["git", "log", f"--format={fmt}", f"--date=format:{date_fmt}", f"-{n}"]
    if diff_filter:
        cmd.append(f"--diff-filter={diff_filter}")
    if pathspec:
        cmd.append("--")
        cmd.append(pathspec)
    try:
        r = subprocess.run(cmd, cwd=_PROJECT_DIR, capture_output=True,
                           text=True, timeout=10, encoding="utf-8", errors="replace")
        if r.returncode != 0:
            return []
        return [l.strip() for l in r.stdout.split("\n") if l.strip()]
    except:
        return []


def git_show(commit_hash):
    """返回 commit 的 diff stat + 完整 diff"""
    try:
        r = subprocess.run(
            ["git", "show", commit_hash, "--stat", "--"],
            cwd=_PROJECT_DIR, capture_output=True, text=True,
            timeout=10, encoding="utf-8", errors="replace")
        stat = r.stdout or ""
        r2 = subprocess.run(
            ["git", "diff", f"{commit_hash}^..{commit_hash}", "--"],
            cwd=_PROJECT_DIR, capture_output=True, text=True,
            timeout=10, encoding="utf-8", errors="replace")
        full_diff = r2.stdout or ""
        return stat, full_diff
    except:
        return "", ""


def is_fix_commit(msg):
    """判断 commit 是否为修复型提交"""
    msg_lower = msg.lower()
    fix_keywords = ["fix:", "bugfix", "hotfix", "correct", "修复", "修",
                    "resolve", "patch", "rollback", "revert"]
    return any(kw in msg_lower for kw in fix_keywords)


def classify_fix(diff_text):
    """分析 diff 文本，返回匹配的修复分类列表"""
    matches = []
    for pattern_def in FIX_PATTERNS:
        score = 0
        for p in pattern_def["patterns"]:
            try:
                if re.search(p, diff_text, re.IGNORECASE):
                    score += 1
            except re.error:
                continue
        if score > 0:
            confidence = min(1.0, pattern_def["confidence_base"] + score * 0.1)
            matches.append({
                "category": pattern_def["category"],
                "check_type": pattern_def["check_type"],
                "name": pattern_def["name"],
                "confidence": round(confidence, 2),
                "match_count": score,
            })
    return matches


def generate_rule(commit_hash, commit_msg, classifications, stat_text):
    """根据分类结果生成检测规则"""
    rules = []
    for c in classifications:
        rule_id = "meta-growth-" + hashlib.md5(
            f"{commit_hash}:{c['category']}".encode()).hexdigest()[:6]

        # 从 diff stat 中提取变更的文件类型做 target 信息
        target_files = []
        if stat_text:
            for line in stat_text.split("\n"):
                line = line.strip()
                if line.endswith(".py") or "/" in line:
                    target_files.append(line.split()[-1] if line.split() else line)

        rule = {
            "rule_id": rule_id,
            "source_commit": commit_hash,
            "source_msg": commit_msg[:120],
            "category": c["category"],
            "check_type": c["check_type"],
            "name": c["name"],
            "confidence": c["confidence"],
            "stage": 1,
            "active": False,
            "created": datetime.now(timezone.utc).isoformat(),
            "target_files": target_files[:5],
            "provenance": f"{commit_hash[:7]} {commit_msg[:80]}",
        }
        rules.append(rule)
    return rules


def load_growth_meta():
    """加载现有规则清单"""
    if os.path.exists(GROWTH_META_PATH):
        try:
            with open(GROWTH_META_PATH, "r", encoding="utf-8") as f:
                return json.load(f)
        except (json.JSONDecodeError, IOError):
            pass
    return {"rules": [], "stage": {"current": 1, "consecutive_fixes": 0}}


def save_growth_meta(data):
    """保存规则清单"""
    with open(GROWTH_META_PATH, "w", encoding="utf-8") as f:
        json.dump(data, f, indent=2, ensure_ascii=False)


def merge_rules(existing, new_rules):
    """合并新规则到现有清单（去重）"""
    existing_ids = {r["rule_id"] for r in existing["rules"]}
    for rule in new_rules:
        if rule["rule_id"] not in existing_ids:
            existing["rules"].append(rule)
            existing_ids.add(rule["rule_id"])
    return existing


def stage_transition(meta):
    """根据连续修复次数推进 stage"""
    consecutive = meta.get("stage", {}).get("consecutive_fixes", 0)
    current_stage = meta.get("stage", {}).get("current", 1)

    if current_stage == 1 and consecutive >= 3:
        meta["stage"]["current"] = 2
        # 激活 stage 1 规则
        for r in meta["rules"]:
            if r["stage"] == 1 and r["confidence"] >= 0.7:
                r["active"] = True
        print(f"[meta_growth] Stage 1 → 2 (连续 {consecutive} 次修复无退回)")
    elif current_stage == 2 and consecutive >= 5:
        meta["stage"]["current"] = 3
        for r in meta["rules"]:
            if r["stage"] <= 2 and r["confidence"] >= 0.6:
                r["active"] = True
        print(f"[meta_growth] Stage 2 → 3 (连续 {consecutive} 次泛化无退回)")

    return meta


def analyze(commit_count=10):
    """主分析流程：读取最近 fix commits → 分类 → 生成规则"""
    meta = load_growth_meta()

    # 跳过已分析的 commits
    analyzed_hashes = {r["source_commit"] for r in meta["rules"]}

    # 获取最近的 fix commits
    commits = git_log(n=commit_count)
    new_rules = []
    fix_count = 0

    for line in commits:
        parts = line.split(" ", 2)
        if len(parts) < 3:
            continue
        h, dt, msg = parts[0], parts[1], parts[2]
        if h in analyzed_hashes:
            continue
        if not is_fix_commit(msg):
            continue

        fix_count += 1
        stat, diff = git_show(h)
        classifications = classify_fix(diff)
        if classifications:
            rules = generate_rule(h, msg, classifications, stat)
            new_rules.extend(rules)
            print(f"  {h} {dt} → {len(rules)} 条规则 ({', '.join(c['category'] for c in classifications)})")

    if new_rules:
        meta = merge_rules(meta, new_rules)
        meta["stage"]["consecutive_fixes"] = meta["stage"].get("consecutive_fixes", 0) + fix_count
        meta = stage_transition(meta)
        save_growth_meta(meta)
        print(f"\n[meta_growth] 新增 {len(new_rules)} 条规则，累计 {len(meta['rules'])} 条")
    else:
        print(f"[meta_growth] 未发现新的修复模式（最近 {commit_count} 条）")

    return meta


def list_rules():
    """列出所有规则"""
    meta = load_growth_meta()
    print(f"\n元成长规则 | Stage {meta['stage']['current']} | "
          f"连续修复: {meta['stage']['consecutive_fixes']} 次 | "
          f"共 {len(meta['rules'])} 条规则\n")
    print(f"{'ID':<22} {'类别':<16} {'置信度':<8} {'激活':<6} {'来源'}")
    print("-" * 80)
    for r in meta["rules"]:
        active = "✅" if r.get("active") else "⏸️"
        print(f"{r['rule_id']:<22} {r['category']:<16} "
              f"{r['confidence']:<8} {active:<6} {r.get('provenance',''):<40}")
    print()


def activate_rule(rule_id):
    """人工激活一条规则"""
    meta = load_growth_meta()
    for r in meta["rules"]:
        if r["rule_id"] == rule_id:
            r["active"] = True
            save_growth_meta(meta)
            print(f"[meta_growth] 规则 {rule_id} 已激活")
            return
    print(f"[meta_growth] 未找到规则: {rule_id}")


def api_status():
    """返回状态供 dashboard E Tab 调用"""
    meta = load_growth_meta()
    return {
        "ok": True,
        "status": "running" if meta["rules"] else "initial",
        "current_stage": meta["stage"]["current"],
        "consecutive_fixes": meta["stage"]["consecutive_fixes"],
        "total_rules": len(meta["rules"]),
        "active_rules": sum(1 for r in meta["rules"] if r.get("active")),
        "categories": list({r["category"] for r in meta["rules"]}),
        "latest_analysis": datetime.now(timezone.utc).isoformat(),
        "description": "Phase 3 元成长回路 — 分析修复模式，自动扩展检测规则",
    }


if __name__ == "__main__":
    if "--list" in sys.argv:
        list_rules()
    elif "--activate" in sys.argv:
        idx = sys.argv.index("--activate")
        if idx + 1 < len(sys.argv):
            activate_rule(sys.argv[idx + 1])
        else:
            print("用法: python meta_growth.py --activate <rule_id>")
    elif "--status" in sys.argv:
        print(json.dumps(api_status(), indent=2, ensure_ascii=False))
    else:
        # 默认: 分析最近 20 条 commits
        n = 20
        if "--analyze" in sys.argv:
            idx = sys.argv.index("--analyze")
            if idx + 1 < len(sys.argv) and sys.argv[idx + 1].isdigit():
                n = int(sys.argv[idx + 1])
        meta = analyze(commit_count=n)
        if "--json" in sys.argv:
            print(json.dumps(meta, indent=2, ensure_ascii=False))
