from __future__ import annotations

import argparse
import json
from pathlib import Path
from typing import Any


ROOT = Path(__file__).resolve().parents[1]
DEFAULT_JSON = ROOT / "GatherTradingData.json"
DEFAULT_TRUTH = ROOT / "Temp" / "operational_truth_score_v1.json"
DEFAULT_DQ = ROOT / "Temp" / "data_quality_reconciliation_v1.json"
DEFAULT_FJ = ROOT / "Temp" / "final_judgment_gate_v1.json"
DEFAULT_SCR = ROOT / "Temp" / "smart_cash_recovery_v7.json"
DEFAULT_HARDENING = ROOT / "Temp" / "strategy_hardening_harness_v2.json"
DEFAULT_OUT = ROOT / "Temp" / "execution_readiness_matrix_v1.json"


def _load(path: Path) -> dict[str, Any]:
    if not path.exists():
        return {}
    try:
        obj = json.loads(path.read_text(encoding="utf-8"))
    except Exception:
        return {}
    return obj if isinstance(obj, dict) else {}


def _as_float(value: Any, default: float = 0.0) -> float:
    try:
        if value is None or value == "":
            return default
        return float(value)
    except Exception:
        return default


def _clamp_score(value: Any) -> float:
    return round(max(0.0, min(100.0, _as_float(value))), 2)


def _axis_gate(score: float, hard_block: bool = False) -> str:
    if hard_block or score < 50.0:
        return "BLOCK"
    if score < 100.0:
        return "WATCH"
    return "PASS_100"


def _axis(axis: str, score: Any, source_json: str, formula_id: str, reason: str = "", hard_block: bool = False) -> dict[str, Any]:
    s = _clamp_score(score)
    gate = _axis_gate(s, hard_block=hard_block)
    return {
        "axis": axis,
        "score_0_100": s,
        "gate": gate,
        "blocking_reason": reason if gate != "PASS_100" else "NONE",
        "source_json": source_json,
        "formula_id": formula_id,
    }


def main() -> int:
    ap = argparse.ArgumentParser(description="Build EXECUTION_READINESS_MATRIX_V1.")
    ap.add_argument("--json", default=str(DEFAULT_JSON))
    ap.add_argument("--truth", default=str(DEFAULT_TRUTH))
    ap.add_argument("--dq", default=str(DEFAULT_DQ))
    ap.add_argument("--fj", default=str(DEFAULT_FJ))
    ap.add_argument("--scr", default=str(DEFAULT_SCR))
    ap.add_argument("--hardening", default=str(DEFAULT_HARDENING))
    ap.add_argument("--out", default=str(DEFAULT_OUT))
    args = ap.parse_args()

    def _rp(path_str: str) -> Path:
        path = Path(path_str)
        return path if path.is_absolute() else ROOT / path

    truth = _load(_rp(args.truth))
    dq = _load(_rp(args.dq))
    fj = _load(_rp(args.fj))
    scr = _load(_rp(args.scr))
    hardening = _load(_rp(args.hardening))

    domain_scores = hardening.get("domain_scores") if isinstance(hardening.get("domain_scores"), dict) else {}
    meta_scores = hardening.get("meta_scores") if isinstance(hardening.get("meta_scores"), dict) else {}

    cap_basis = _clamp_score(dq.get("confidence_cap_basis_score", truth.get("data_truth_score")))
    fundamental_domain = _clamp_score(domain_scores.get("fundamental"))
    fundamental_score = min(fundamental_domain, cap_basis)

    scr_allowed = bool(scr.get("execution_allowed"))
    scr_status = str(scr.get("status") or "UNKNOWN")
    scr_damage = _as_float(scr.get("value_damage_pct_avg"))
    if scr_allowed and scr_status == "PASS" and scr_damage <= 10.0:
        cash_recovery_score = 100.0
        cash_recovery_reason = ""
        cash_recovery_hard_block = False
    else:
        cash_recovery_score = 0.0
        cash_recovery_reason = f"SCR_STATUS={scr_status};EXECUTION_ALLOWED={scr_allowed};VALUE_DAMAGE={round(scr_damage, 2)}"
        cash_recovery_hard_block = True

    final_judgment_score = 100.0
    final_judgment_reason = ""
    fj_gate = str(fj.get("gate") or "MISSING")
    fj_coverage = _as_float(fj.get("coverage_pct"))
    fj_silent = int(_as_float(fj.get("silent_pass_violations")))
    if fj_gate != "PASS" or fj_coverage < 100.0 or fj_silent > 0:
        final_judgment_score = 0.0
        final_judgment_reason = f"FJ_GATE={fj_gate};COVERAGE={round(fj_coverage, 2)};SILENT={fj_silent}"

    axes = [
        _axis("data_integrity", truth.get("data_truth_score"), "operational_truth_score_v1.json", "OPERATIONAL_TRUTH_SCORE_V1", "DATA_TRUTH_LT_100"),
        _axis("routing_serving", domain_scores.get("routing_serving"), "strategy_hardening_harness_v2.json", "STRATEGY_HARDENING_HARNESS_V2", "ROUTING_SERVING_LT_100"),
        _axis("serving_output_lock", truth.get("report_consistency_score"), "operational_truth_score_v1.json", "OPERATIONAL_TRUTH_SCORE_V1", "REPORT_CONSISTENCY_LT_100"),
        _axis("decision_governance", truth.get("decision_truth_score"), "operational_truth_score_v1.json", "OPERATIONAL_TRUTH_SCORE_V1", "DECISION_TRUTH_LT_100"),
        _axis("final_judgment_lock", final_judgment_score, "final_judgment_gate_v1.json", "FINAL_JUDGMENT_GATE_V1", final_judgment_reason),
        _axis("fundamental_basis", fundamental_score, "data_quality_reconciliation_v1.json", "DATA_QUALITY_RECONCILIATION_V1", "CONFIDENCE_CAP_BASIS_LT_100"),
        _axis("horizon_policy", domain_scores.get("horizon_short_mid_long"), "strategy_hardening_harness_v2.json", "STRATEGY_HARDENING_HARNESS_V2", "HORIZON_POLICY_LT_100"),
        _axis("smart_money_liquidity", domain_scores.get("smart_money_liquidity"), "strategy_hardening_harness_v2.json", "STRATEGY_HARDENING_HARNESS_V2", "SMART_MONEY_LIQUIDITY_LT_100"),
        _axis("cash_recovery_execution", cash_recovery_score, "smart_cash_recovery_v7.json", "SMART_CASH_RECOVERY_V7", cash_recovery_reason, cash_recovery_hard_block),
        _axis("execution_availability", truth.get("execution_truth_score"), "operational_truth_score_v1.json", "OPERATIONAL_TRUTH_SCORE_V1", "EXECUTION_TRUTH_LT_100"),
        _axis("performance_readiness", truth.get("performance_readiness_score"), "operational_truth_score_v1.json", "OPERATIONAL_TRUTH_SCORE_V1", "PERFORMANCE_READINESS_LT_100"),
        _axis("report_consistency", truth.get("report_consistency_score"), "operational_truth_score_v1.json", "OPERATIONAL_TRUTH_SCORE_V1", "REPORT_CONSISTENCY_LT_100"),
    ]

    scores = [float(row["score_0_100"]) for row in axes]
    hard_blocks = [row for row in axes if row["gate"] == "BLOCK"]
    min_axis_score = round(min(scores), 2) if scores else 0.0
    average_axis_score = round(sum(scores) / len(scores), 2) if scores else 0.0

    truth_gate = str(truth.get("gate") or "MISSING")
    readiness_gate = str(meta_scores.get("readiness_gate") or "MISSING")
    if hard_blocks:
        gate = "BLOCK_EXECUTION"
    elif min_axis_score == 100.0 and truth_gate == "PASS_100" and readiness_gate == "PERFORMANCE_READY":
        gate = "PASS_100"
    else:
        gate = "WATCH_PENDING_SAMPLE"

    result = {
        "formula_id": "EXECUTION_READINESS_MATRIX_V1",
        "gate": gate,
        "min_axis_score": min_axis_score,
        "average_axis_score": average_axis_score,
        "hard_block_count": len(hard_blocks),
        "blocking_reasons": [str(row["blocking_reason"]) for row in axes if row["gate"] != "PASS_100"],
        "axes": axes,
        "targets": {
            "pass_100_rule": "all axes score_0_100 == 100, truth gate PASS_100, readiness_gate PERFORMANCE_READY",
            "block_rule": "any axis gate BLOCK",
            "watch_rule": "no BLOCK but at least one axis below 100 or insufficient performance sample",
        },
        "metric_basis": {
            "truth_gate": truth_gate,
            "truth_score_0_100": truth.get("score_0_100"),
            "readiness_gate": readiness_gate,
            "confidence_cap_basis_score": cap_basis,
            "fundamental_domain_score": fundamental_domain,
            "smart_cash_recovery_status": scr_status,
            "smart_cash_recovery_execution_allowed": scr_allowed,
            "smart_cash_recovery_value_damage_pct": round(scr_damage, 2),
        },
    }

    out_path = _rp(args.out)
    out_path.parent.mkdir(parents=True, exist_ok=True)
    out_path.write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8")
    print(json.dumps(result, ensure_ascii=False, indent=2))
    return 0


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