"""validate_golden_coverage_100.py — P1-016 Golden Case Coverage Acceptance Test

formula_golden_cases_v4.yaml(및 이전 버전)까지 포함해 golden_coverage_ratio가
목표치를 초과하는지 검증.

수락 기준:
  - golden_coverage_ratio >= 0.95 (95%+): PASS
  - decision_critical_min_cases >= 3: PASS for all 28 decision-critical formulas

Exit:
  0 = PASS (목표 커버리지 달성)
  1 = FAIL (커버리지 미달 또는 decision-critical 케이스 부족)
"""
from __future__ import annotations

import json
import subprocess
import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
COVERAGE_JSON = ROOT / "Temp" / "yaml_code_coverage_v1.json"
COVERAGE_TARGET = 0.95

DECISION_CRITICAL = {
    "STOP_BREACH_V1",
    "SMART_CASH_RECOVERY_V4",
    "SMART_CASH_RECOVERY_V7",
    "ANTI_LATE_ENTRY_PULLBACK_GATE_V4",
    "REGIME_CONDITIONAL_MACRO_FACTOR_V1",
    "MACRO_REGIME_ALIGNMENT_GATE_V2",
    "DISTRIBUTION_EXIT_PRESIGNAL_V2",
    "SELL_EXECUTION_TIMING_LOCK_V2",
    "SELL_EXECUTION_QUALITY_GATE_V1",
    "PORTFOLIO_HEALTH_V1",
    "INDEX_RELATIVE_HEALTH_GATE_V1",
    "CASH_RAISE_PARETO_EXECUTOR_V2",
    "CASH_RAISE_VALUE_OPTIMIZER_V3",
    "CASH_RECOVERY_OPTIMIZER_V4",
    "CASH_RECOVERY_V1",
    "DATA_MATURITY_TRUTH_GATE_V1",
    "DATA_MATURITY_TRUTH_GATE_VALIDATOR_V1",
    "DATA_QUALITY_GATE_V2_PY",
    "DATA_QUALITY_GATE_V3",
    "IMPUTED_DATA_EXPOSURE_GATE_V2",
    "OPERATIONAL_ALPHA_CALIBRATION_V2",
    "PREDICTIVE_ALPHA_DIALECTIC_ENGINE_V1_BRIDGE",
    "REBOUND_SELL_EFFICIENCY_V1",
    "SELL_ENGINE_AUDIT_V1",
    "SELL_SLIPPAGE_BUDGET_FACTOR_V1",
    "ENTRY_TIMING_DECILE_FACTOR_V1",
    "DYNAMIC_VALUE_PRESERVATION_SELL_V3_BRIDGE",
    "ARTIFACT_FRESHNESS_GATE_V1",
}

MIN_CASES_PER_CRITICAL = 3


def _rebuild_coverage() -> dict:
    """coverage JSON이 없으면 빌더를 실행해 갱신."""
    if not COVERAGE_JSON.exists():
        subprocess.run(
            [sys.executable, str(ROOT / "tools" / "build_yaml_code_coverage_v1.py")],
            check=True,
        )
    with COVERAGE_JSON.open(encoding="utf-8") as fh:
        return json.load(fh)


def _count_cases_per_formula() -> dict[str, int]:
    """golden_cases_v2/v3/v4 에서 formula_id별 케이스 수 집계."""
    import yaml  # type: ignore

    spec_dir = ROOT / "spec"
    files = [
        spec_dir / "formula_golden_cases_v2.yaml",
        spec_dir / "formula_golden_cases_v3.yaml",
        spec_dir / "formula_golden_cases_v4.yaml",
    ]
    counts: dict[str, int] = {}
    for f in files:
        if not f.exists():
            continue
        try:
            doc = yaml.safe_load(f.read_text(encoding="utf-8")) or {}
        except Exception:
            continue
        for entry in doc.get("golden_cases", []):
            fid = entry.get("formula_id")
            if fid:
                counts[fid] = counts.get(fid, 0) + 1
    return counts


def main() -> int:
    cov = _rebuild_coverage()
    ratio = cov.get("golden_coverage_ratio", 0.0)
    total = cov.get("yaml_formula_count", 0)
    golden = cov.get("golden_test_count", 0)
    uncovered = cov.get("golden_uncovered_rules", [])

    case_counts = _count_cases_per_formula()

    critical_missing: list[str] = []
    for fid in DECISION_CRITICAL:
        if case_counts.get(fid, 0) < MIN_CASES_PER_CRITICAL:
            critical_missing.append(fid)

    ok_ratio = ratio >= COVERAGE_TARGET
    ok_critical = len(critical_missing) == 0

    # 정직성 표기 (QE-M0-06): 이 게이트는 golden 케이스/파일의 "존재 수"만 집계한다.
    # tests/golden/generated/ 의 174개 파일은 실행되지 않는 placeholder이며, 수치
    # 실행 검증은 M3의 factor parity 게이트(validate_factor_parity_v1)로 대체된다.
    payload = {
        "formula_id": "GOLDEN_COVERAGE_100_V1",
        "gate": "PASS" if (ok_ratio and ok_critical) else "FAIL",
        "coverage_basis": "FILE_COUNT_ONLY",
        "golden_coverage_ratio": ratio,
        "yaml_formula_count": total,
        "golden_test_count": golden,
        "critical_missing": sorted(critical_missing),
        "uncovered_count": len(uncovered),
        "note": "coverage counts YAML golden-case entries/files only; generated golden test stubs are not executed",
    }
    out_path = ROOT / "Temp" / "golden_coverage_100_v1.json"
    out_path.parent.mkdir(parents=True, exist_ok=True)
    out_path.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")

    print(f"[GOLDEN_COVERAGE_100] total={total} golden={golden} ratio={ratio:.4f} "
          f"({'≥' if ok_ratio else '<'}{COVERAGE_TARGET}) "
          f"critical_missing={len(critical_missing)}")

    if critical_missing:
        print(f"  [WARN] decision-critical with <{MIN_CASES_PER_CRITICAL} cases:")
        for fid in critical_missing:
            print(f"    - {fid}: {case_counts.get(fid, 0)} case(s)")

    if uncovered:
        print(f"  [WARN] still uncovered ({len(uncovered)}): "
              + ", ".join(uncovered[:20])
              + ("..." if len(uncovered) > 20 else ""))

    if ok_ratio and ok_critical:
        print("  [PASS] golden_coverage_ratio >= 95% and all decision-critical formulas covered")
        return 0
    else:
        issues = []
        if not ok_ratio:
            issues.append(f"golden_coverage_ratio={ratio:.4f} < {COVERAGE_TARGET}")
        if not ok_critical:
            issues.append(f"{len(critical_missing)} critical formula(s) have <{MIN_CASES_PER_CRITICAL} cases")
        print(f"  [FAIL] {'; '.join(issues)}")
        return 1


if __name__ == "__main__":
    raise SystemExit(main())
