"""
validate_alpha_execution_harness.py

APEX Alpha Preservation Execution Harness V1/V5 전용 검증기.

기본 validate_harness_context.py는 기존 JSON 호환성을 위해 APEX 필드를 optional로 본다.
이 도구는 GAS/Harness V5 전환 후 APEX 필드를 의무 검증하거나, 부분 도입 상태를 감사할 때 사용한다.

Usage:
  python tools/validate_alpha_execution_harness.py <json> [--strict]
  python tools/validate_alpha_execution_harness.py <json> --check breakout_quality_gate
  python tools/validate_alpha_execution_harness.py <json> --check anti_whipsaw_gate
  python tools/validate_alpha_execution_harness.py <json> --check smart_cash_raise_v2
  python tools/validate_alpha_execution_harness.py <json> --check determinism
  python tools/validate_alpha_execution_harness.py <json> --check cla_harness

cla_harness 체크 항목 (Section 13):
  [1] market_regime_state enum 검증 (ADVANCE|PULLBACK_IN_UPTREND|DISTRIBUTION|BREAKDOWN|UNKNOWN)
  [2] CLA(CLUSTER_HOLD_ONLY) 레짐 시 코어 종목(005930/000660/229200) SELL 차단 (REGIME_CLA-1)
  [3] semiconductor_cluster_json.cluster_state enum 검증
  [4] buy_permission_json rs_verdict enum 검증 (LEADER|MARKET|LAGGARD|BROKEN|UNKNOWN)
  [5] buy_permission_json composite_verdict enum 검증 (5가지 판정값)
  [6] satellite_failure_gate_json.sfg_v1 enum 검증 (TRIGGERED|CLEAR)
  [7] sfg_v1=TRIGGERED 시 위성 ALLOW_* 차단 (SFG-2)
  [8] buy_permission_json.rag_v1 enum 검증 (PASS|FAIL|EXEMPT)
  [9] rag_v1=FAIL 시 ALLOW_* 불일치 오류 (RAG-2)
"""
from __future__ import annotations

import json
import sys
from pathlib import Path
from typing import Any


ROOT = Path(__file__).resolve().parents[1]

VALID_CHECK_MODES = {"breakout_quality_gate", "anti_whipsaw_gate", "smart_cash_raise_v2", "determinism", "cla_harness", "brt_harness", "factor_cap"}

REQUIRED_V5_KEYS = [
    "breakout_quality_gate_json",
    "breakout_quality_gate_lock",
    "anti_whipsaw_gate_json",
    "anti_whipsaw_gate_lock",
    "smart_cash_raise_json",
    "smart_cash_raise_route",
]

REQUIRED_APEX_KEYS = [
    "alpha_lead_lock",
    "alpha_lead_json",
    "follow_through_lock",
    "follow_through_json",
    "distribution_lock",
    "distribution_risk_json",
    "profit_preservation_lock",
    "profit_preservation_json",
    "smart_cash_raise_lock",
    "cash_raise_plan_json",
    "rebound_sell_trigger_json",
    "smart_sell_quantities_json",
    "execution_quality_lock",
    "execution_quality_json",
    "buy_permission_json",
    "limit_price_policy_json",
    "alpha_feedback_json",
]

BUY_ACTIONS = {"BUY", "STAGED_BUY", "ADD_ON"}
VALID_BUY_PERMISSION = {"ALLOW_PILOT", "ALLOW_ADD_ON", "WATCH", "BLOCKED"}

# ── [2026-05-21_CLA_HARNESS_V1] Section 13 체크 상수 ──────────────────────────
VALID_MARKET_REGIME_STATES = {
    "ADVANCE", "PULLBACK_IN_UPTREND", "DISTRIBUTION", "BREAKDOWN", "UNKNOWN"
}
VALID_RS_VERDICTS = {"LEADER", "MARKET", "LAGGARD", "BROKEN", "UNKNOWN"}
VALID_COMPOSITE_VERDICTS = {
    "PRIME_CANDIDATE", "WATCH_CANDIDATE", "REDUCE_CANDIDATE", "EXIT_REVIEW", "CLOSE_POSITION"
}
VALID_BRT_VERDICTS = {"LEADER", "MARKET", "LAGGARD", "BROKEN", "UNKNOWN"}
VALID_SAQG_STATES = {"ELIGIBLE", "WATCHLIST_ONLY", "EXCLUDED", "EXEMPT"}
VALID_SAPG_STATUSES = {"PASS", "SAPG_ALERT", "SAPG_CRITICAL", "INSUFFICIENT_DATA"}
SEMICONDUCTOR_CORE_TICKERS = {"005930", "000660", "229200"}  # 삼성전자, SK하이닉스, KODEX반도체


def load_harness(path: Path) -> dict[str, Any]:
    payload = json.loads(path.read_text(encoding="utf-8"))
    if isinstance(payload, dict) and isinstance(payload.get("data"), dict):
        maybe = payload["data"].get("_harness_context")
        if isinstance(maybe, dict):
            return maybe
    return payload


def parse_jsonish(value: Any) -> Any:
    if isinstance(value, (list, dict)):
        return value
    if isinstance(value, str) and value.strip():
        return json.loads(value)
    return value


def to_number(value: Any) -> float | None:
    if isinstance(value, (int, float)):
        return float(value)
    if isinstance(value, str):
        text = value.strip()
        if not text:
            return None
        try:
            return float(text)
        except ValueError:
            return None
    return None


def as_rows(harness: dict[str, Any], key: str, errors: list[str]) -> list[dict[str, Any]]:
    value = parse_jsonish(harness.get(key))
    if not isinstance(value, list):
        errors.append(f"{key}: must be a list")
        return []
    rows: list[dict[str, Any]] = []
    for idx, item in enumerate(value):
        if not isinstance(item, dict):
            errors.append(f"{key}[{idx}]: must be an object")
            continue
        rows.append(item)
    return rows


def validate_required_keys(harness: dict[str, Any], errors: list[str]) -> None:
    for key in REQUIRED_APEX_KEYS:
        if key not in harness:
            errors.append(f"missing APEX key: {key}")


def validate_buy_blocks(harness: dict[str, Any], errors: list[str]) -> None:
    permissions = as_rows(harness, "buy_permission_json", errors)
    permission_by_ticker = {str(r.get("ticker")): r for r in permissions if r.get("ticker")}
    for idx, row in enumerate(permissions):
        state = row.get("buy_permission_state")
        if state not in VALID_BUY_PERMISSION:
            errors.append(f"buy_permission_json[{idx}].buy_permission_state invalid: {state!r}")
        tranche = row.get("max_tranche_pct")
        if state == "ALLOW_PILOT" and isinstance(tranche, (int, float)) and tranche > 30:
            errors.append(f"buy_permission_json[{idx}].max_tranche_pct exceeds 30 for ALLOW_PILOT")
        late_chase = to_number(row.get("late_chase_risk_score"))
        if late_chase is not None and not (0 <= late_chase <= 100):
            errors.append(f"buy_permission_json[{idx}].late_chase_risk_score must be in [0,100]")
        follow_score = to_number(row.get("follow_through_score"))
        if follow_score is not None and not (0 <= follow_score <= 100):
            errors.append(f"buy_permission_json[{idx}].follow_through_score must be in [0,100]")

    orders = parse_jsonish(harness.get("order_blueprint_json"))
    if isinstance(orders, list):
        for idx, order in enumerate(orders):
            if not isinstance(order, dict):
                continue
            ticker = str(order.get("ticker") or "")
            order_type = str(order.get("order_type") or "")
            validation = str(order.get("validation_status") or "")
            state = (permission_by_ticker.get(ticker) or {}).get("buy_permission_state")
            if order_type in BUY_ACTIONS and validation == "PASS" and state not in {"ALLOW_PILOT", "ALLOW_ADD_ON"}:
                errors.append(
                    f"order_blueprint_json[{idx}]: BUY PASS emitted while buy_permission_state={state!r}"
                )


def validate_distribution_blocks(harness: dict[str, Any], errors: list[str]) -> None:
    distribution = as_rows(harness, "distribution_risk_json", errors)
    blocked = {
        str(row.get("ticker"))
        for row in distribution
        if row.get("anti_distribution_state") == "BLOCK_BUY"
        or (isinstance(row.get("distribution_risk_score"), (int, float)) and row["distribution_risk_score"] >= 70)
    }
    orders = parse_jsonish(harness.get("order_blueprint_json"))
    if isinstance(orders, list):
        for idx, order in enumerate(orders):
            if not isinstance(order, dict):
                continue
            if str(order.get("ticker")) in blocked and str(order.get("order_type")) in BUY_ACTIONS:
                errors.append(f"order_blueprint_json[{idx}]: BUY action exists for distribution BLOCK_BUY ticker")


def validate_cash_raise(harness: dict[str, Any], errors: list[str]) -> None:
    rows = as_rows(harness, "cash_raise_plan_json", errors)
    for idx, row in enumerate(rows):
        style = row.get("execution_style")
        immediate = row.get("immediate_qty")
        rebound = row.get("rebound_wait_qty")
        cap_pct = row.get("immediate_qty_cap_pct")
        if immediate is not None and not isinstance(immediate, int):
            errors.append(f"cash_raise_plan_json[{idx}].immediate_qty must be integer or null")
        if rebound is not None and not isinstance(rebound, int):
            errors.append(f"cash_raise_plan_json[{idx}].rebound_wait_qty must be integer or null")
        if cap_pct is not None and not isinstance(cap_pct, int):
            errors.append(f"cash_raise_plan_json[{idx}].immediate_qty_cap_pct must be integer or null")
        emergency = row.get("emergency_full_sell") is True
        if style == "OVERSOLD_REBOUND_SELL" and not (isinstance(rebound, int) and rebound > 0) and not emergency:
            errors.append(
                f"cash_raise_plan_json[{idx}]: OVERSOLD_REBOUND_SELL requires rebound_wait_qty > 0 "
                f"unless emergency_full_sell=true"
            )


def validate_execution_quality(harness: dict[str, Any], errors: list[str]) -> None:
    quality_rows = as_rows(harness, "execution_quality_json", errors)
    quality_by_ticker = {str(row.get("ticker")): row for row in quality_rows if row.get("ticker")}
    for idx, row in enumerate(quality_rows):
        status = row.get("execution_quality_status")
        if status not in {"PASS", "BLOCKED", "BLOCKED_ADV_3PCT", "SPLIT_REQUIRED", "BLOCKED_SPREAD"}:
            errors.append(f"execution_quality_json[{idx}].execution_quality_status invalid: {status!r}")
    orders = parse_jsonish(harness.get("order_blueprint_json"))
    if isinstance(orders, list):
        for idx, order in enumerate(orders):
            if not isinstance(order, dict):
                continue
            if str(order.get("validation_status")) != "PASS":
                continue
            ticker = str(order.get("ticker") or "")
            quality = quality_by_ticker.get(ticker)
            if quality and quality.get("execution_quality_status") != "PASS":
                errors.append(
                    f"order_blueprint_json[{idx}]: PASS order while execution_quality_status={quality.get('execution_quality_status')!r}"
                )
    for idx, row in enumerate(quality_rows):
        split_count = row.get("split_count")
        if split_count is not None and not isinstance(split_count, int):
            errors.append(f"execution_quality_json[{idx}].split_count must be integer or null")


def validate_alpha_feedback_loop(harness: dict[str, Any], errors: list[str]) -> None:
    payload = parse_jsonish(harness.get("alpha_feedback_json"))
    if not isinstance(payload, dict):
        errors.append("alpha_feedback_json: must be an object")
        return
    if payload.get("formula_id") != "ALPHA_FEEDBACK_LOOP_V1":
        errors.append(f"alpha_feedback_json.formula_id must be ALPHA_FEEDBACK_LOOP_V1, found={payload.get('formula_id')!r}")
    if not isinstance(payload.get("cases_analyzed"), int) or payload["cases_analyzed"] < 0:
        errors.append("alpha_feedback_json.cases_analyzed must be a non-negative integer")
    if not isinstance(payload.get("grade_count"), int) or payload["grade_count"] < 0:
        errors.append("alpha_feedback_json.grade_count must be a non-negative integer")
    if payload.get("status") not in {"ANALYZED", "DATA_MISSING", "DATA_INSUFFICIENT"}:
        errors.append(f"alpha_feedback_json.status invalid: {payload.get('status')!r}")
    if payload.get("recommended_filter_adjustments") is not None and not isinstance(payload.get("recommended_filter_adjustments"), list):
        errors.append("alpha_feedback_json.recommended_filter_adjustments must be a list")
    if payload.get("grade_summary") is not None and not isinstance(payload.get("grade_summary"), list):
        errors.append("alpha_feedback_json.grade_summary must be a list")
    if payload.get("status") == "ANALYZED" and payload.get("cases_analyzed", 0) < 10:
        errors.append("alpha_feedback_json: ANALYZED requires cases_analyzed >= 10")


# ── [2026-05-20_HARNESS_V5] 신규 검증 함수 ──────────────────────────────────

def validate_breakout_quality_gate(harness: dict[str, Any], errors: list[str]) -> None:
    """H6: BREAKOUT_QUALITY_GATE_V2 — 뒷박 차단 게이트 검증."""
    if "breakout_quality_gate_json" not in harness:
        errors.append("missing V5 key: breakout_quality_gate_json")
        return
    rows = as_rows(harness, "breakout_quality_gate_json", errors)
    valid_gates = {"PILOT_ALLOWED", "WATCH_COOLING_OFF", "BLOCKED_LATE_CHASE"}
    blocked_tickers: set[str] = set()
    for idx, row in enumerate(rows):
        gate = row.get("breakout_quality_gate")
        score = row.get("breakout_quality_score")
        if gate not in valid_gates:
            errors.append(f"breakout_quality_gate_json[{idx}].breakout_quality_gate invalid: {gate!r}")
        if score is not None and not (0 <= float(score) <= 100):
            errors.append(f"breakout_quality_gate_json[{idx}].breakout_quality_score must be 0-100")
        if gate == "BLOCKED_LATE_CHASE":
            blocked_tickers.add(str(row.get("ticker") or ""))
    orders = parse_jsonish(harness.get("order_blueprint_json"))
    if isinstance(orders, list):
        for idx, order in enumerate(orders):
            if not isinstance(order, dict):
                continue
            if str(order.get("ticker")) in blocked_tickers and str(order.get("order_type")) in BUY_ACTIONS:
                errors.append(
                    f"order_blueprint_json[{idx}]: BUY exists for BLOCKED_LATE_CHASE ticker (QEH009)"
                )


def validate_anti_whipsaw_gate(harness: dict[str, Any], errors: list[str]) -> None:
    """H7: ANTI_WHIPSAW_HOLD_GATE_V1 — 가짜 매도 차단 게이트 검증."""
    if "anti_whipsaw_gate_json" not in harness:
        errors.append("missing V5 key: anti_whipsaw_gate_json")
        return
    rows = as_rows(harness, "anti_whipsaw_gate_json", errors)
    valid_gates = {
        "WHIPSAW_SUSPECTED",
        "INCONCLUSIVE",
        "CONFIRMED_SELL",
        "WHIPSAW_CONFIRMED",
        "WHIPSAW_WEAKENING",
        "WHIPSAW_AUTO_RELEASED",
    }
    whipsaw_tickers: set[str] = set()
    for idx, row in enumerate(rows):
        gate = row.get("anti_whipsaw_gate")
        score = row.get("anti_whipsaw_score")
        hold_days = row.get("anti_whipsaw_hold_days")
        if gate not in valid_gates:
            errors.append(f"anti_whipsaw_gate_json[{idx}].anti_whipsaw_gate invalid: {gate!r}")
        if score is not None and not (-50 <= float(score) <= 100):
            errors.append(f"anti_whipsaw_gate_json[{idx}].anti_whipsaw_score must be in [-50,100]")
        if gate == "WHIPSAW_SUSPECTED" and hold_days != 1:
            errors.append(f"anti_whipsaw_gate_json[{idx}]: WHIPSAW_SUSPECTED must have hold_days=1")
        if gate == "WHIPSAW_SUSPECTED":
            whipsaw_tickers.add(str(row.get("ticker") or ""))
    orders = parse_jsonish(harness.get("order_blueprint_json"))
    SELL_ACTIONS = {"SELL", "TRIM", "EXIT_100", "EXIT_FULL"}
    if isinstance(orders, list):
        for idx, order in enumerate(orders):
            if not isinstance(order, dict):
                continue
            ticker = str(order.get("ticker") or "")
            order_type = str(order.get("order_type") or "")
            qty = order.get("quantity")
            if (ticker in whipsaw_tickers and order_type in SELL_ACTIONS
                    and str(order.get("validation_status")) == "PASS"):
                errors.append(
                    f"order_blueprint_json[{idx}]: full SELL emitted for WHIPSAW_SUSPECTED ticker (QEH010)"
                )

    reentry_rows = parse_jsonish(harness.get("anti_whipsaw_reentry_json"))
    if isinstance(reentry_rows, list):
        for idx, row in enumerate(reentry_rows):
            if not isinstance(row, dict):
                continue
            tier = row.get("sell_tier")
            if tier not in {1, 2}:
                errors.append(
                    f"anti_whipsaw_reentry_json[{idx}]: sell_tier must be 1 or 2, found={tier!r} (QEH010-TIER)"
                )
            signal = row.get("reentry_signal")
            if signal not in {"REENTRY_CANDIDATE"}:
                errors.append(
                    f"anti_whipsaw_reentry_json[{idx}].reentry_signal invalid: {signal!r}"
                )


def validate_smart_cash_raise_v2(harness: dict[str, Any], errors: list[str]) -> None:
    """H8: SMART_CASH_RAISE_V2 — 4경로 현금확보 라우터 검증."""
    if "smart_cash_raise_json" not in harness:
        errors.append("missing V5 key: smart_cash_raise_json")
        return
    rows = as_rows(harness, "smart_cash_raise_json", errors)
    valid_routes = {"ROUTE_A", "ROUTE_B", "ROUTE_C", "ROUTE_D", "NO_ACTION"}
    portfolio_route = str(harness.get("smart_cash_raise_route") or "NO_ACTION")
    if portfolio_route not in valid_routes:
        errors.append(f"smart_cash_raise_route invalid: {portfolio_route!r}")
    for idx, row in enumerate(rows):
        route = row.get("smart_cash_raise_route")
        if route not in valid_routes:
            errors.append(f"smart_cash_raise_json[{idx}].smart_cash_raise_route invalid: {route!r}")
        rebound_wait = row.get("rebound_wait_pct")
        if route == "ROUTE_B" and rebound_wait != 50:
            errors.append(f"smart_cash_raise_json[{idx}]: ROUTE_B must have rebound_wait_pct=50")
        if route == "ROUTE_D":
            rationale = str(row.get("rationale") or "")
            emergency = row.get("emergency_full_sell") is True
            stop_gate = str(row.get("stop_breach_gate") or "")
            if not emergency and stop_gate != "BREACH" and (
                "emergency" not in rationale.lower() and "breach" not in rationale.lower()
            ):
                errors.append(
                    f"smart_cash_raise_json[{idx}]: ROUTE_D requires emergency or breach rationale (QEH011)"
                )


def validate_cla_harness(harness: dict[str, Any], errors: list[str]) -> None:
    """CLA_HARNESS_V1: CLA 레짐 위성 실패 게이트·RAG·RS 판정 검증."""
    # ── [Section 13-1] market_regime_state enum 검증 ─────────────────────────
    regime_state = harness.get("market_regime_state")
    if regime_state is not None and regime_state not in VALID_MARKET_REGIME_STATES:
        errors.append(
            f"market_regime_state invalid: {regime_state!r} "
            f"(expected one of {sorted(VALID_MARKET_REGIME_STATES)})"
        )

    # ── [Section 13-3] semiconductor_cluster_json cluster_state enum 검증 ────
    semi_json = parse_jsonish(harness.get("semiconductor_cluster_json"))
    cluster_state: str | None = None
    if isinstance(semi_json, dict):
        cluster_state = str(semi_json.get("cluster_state") or "")
        if cluster_state not in {"CLUSTER_HOLD_ONLY", "CLUSTER_OPEN", "CLUSTER_BLOCK", ""}:
            errors.append(
                f"semiconductor_cluster_json.cluster_state invalid: {cluster_state!r} "
                "(expected CLUSTER_HOLD_ONLY | CLUSTER_OPEN | CLUSTER_BLOCK)"
            )

    # ── [Section 13-2] CLA 레짐 시 코어 종목 SELL 차단 (REGIME_CLA-1) ────────
    if cluster_state == "CLUSTER_HOLD_ONLY":
        decisions = parse_jsonish(harness.get("decisions_json"))
        if isinstance(decisions, list):
            for idx, dec in enumerate(decisions):
                if not isinstance(dec, dict):
                    continue
                ticker = str(dec.get("ticker") or "")
                final_action = str(dec.get("final_action") or "").upper()
                if ticker in SEMICONDUCTOR_CORE_TICKERS and final_action == "SELL":
                    errors.append(
                        f"decisions_json[{idx}] ticker={ticker}: SELL emitted for core "
                        "semiconductor in CLA (CLUSTER_HOLD_ONLY) regime — REGIME_CLA-1 violation"
                    )

    # ── [Section 13-4/5] buy_permission_json per-row rs_verdict/composite_verdict enum ─
    permissions = as_rows(harness, "buy_permission_json", [])  # 별도 errors 수집 불필요
    for idx, bp in enumerate(permissions):
        rv = bp.get("rs_verdict")
        if rv is not None and rv not in VALID_RS_VERDICTS:
            errors.append(
                f"buy_permission_json[{idx}].rs_verdict invalid: {rv!r} "
                f"(expected one of {sorted(VALID_RS_VERDICTS)})"
            )
        cv = bp.get("composite_verdict")
        if cv is not None and cv not in VALID_COMPOSITE_VERDICTS:
            errors.append(
                f"buy_permission_json[{idx}].composite_verdict invalid: {cv!r} "
                f"(expected one of {sorted(VALID_COMPOSITE_VERDICTS)})"
            )

    # SFG-1: satellite_failure_gate_json 존재 및 sfg_v1 유효값 확인
    if "satellite_failure_gate_json" not in harness:
        errors.append("missing CLA key: satellite_failure_gate_json")
    else:
        sfg = parse_jsonish(harness.get("satellite_failure_gate_json"))
        if not isinstance(sfg, dict):
            errors.append("satellite_failure_gate_json: must be an object")
        else:
            sfg_v1 = sfg.get("sfg_v1")
            if sfg_v1 not in {"TRIGGERED", "CLEAR"}:
                errors.append(f"satellite_failure_gate_json.sfg_v1 invalid: {sfg_v1!r} (expected TRIGGERED|CLEAR)")
            # SFG-2: TRIGGERED이면 위성 ALLOW_* buy_permission 없어야 함
            if sfg_v1 == "TRIGGERED":
                permissions = as_rows(harness, "buy_permission_json", errors)
                for idx, bp in enumerate(permissions):
                    state = str(bp.get("buy_permission_state") or "")
                    pos_type = str(bp.get("position_type") or bp.get("cluster_label") or "")
                    is_satellite = pos_type.lower() in {"satellite", "위성"} or (
                        pos_type == "" and bp.get("core_flag") is False
                    )
                    if is_satellite and state.startswith("ALLOW_"):
                        errors.append(
                            f"buy_permission_json[{idx}]: satellite ALLOW_* ({state!r}) emitted while sfg_v1=TRIGGERED (SFG-2)"
                        )

    # RAG-1: buy_permission_json RAG 필드 일관성
    permissions = as_rows(harness, "buy_permission_json", errors)
    for idx, bp in enumerate(permissions):
        rag = bp.get("rag_v1")
        if rag is not None and rag not in {"PASS", "FAIL", "EXEMPT"}:
            errors.append(f"buy_permission_json[{idx}].rag_v1 invalid: {rag!r}")
        # RAG-2: FAIL인데 ALLOW_* 상태면 오류
        if rag == "FAIL" and str(bp.get("buy_permission_state") or "").startswith("ALLOW_"):
            errors.append(
                f"buy_permission_json[{idx}]: rag_v1=FAIL but buy_permission_state=ALLOW_* (RAG-2)"
            )

    # RS-1: decisions_json 내 rs_verdict 존재 여부 — data_feed 시트 컬럼으로 관리되므로 경고만 출력
    decisions = parse_jsonish(harness.get("decisions_json"))
    if isinstance(decisions, list):
        missing_rs = sum(
            1 for d in decisions
            if isinstance(d, dict) and "rs_verdict" not in d and "composite_verdict" not in d
        )
        if missing_rs == len(decisions) and len(decisions) > 0:
            # 전체 누락 시에만 경고 (data_feed 시트가 아직 갱신되지 않은 경우)
            print(f"[WARN] RS-1: decisions_json {missing_rs}/{len(decisions)} rows lack rs_verdict (GAS re-run needed)")


def validate_determinism(harness: dict[str, Any], errors: list[str]) -> None:
    """결정론 검증: 하네스 락 필드와 JSON 출력 일관성 확인."""
    lock_json_pairs = [
        ("breakout_quality_gate_lock", "breakout_quality_gate_json"),
        ("anti_whipsaw_gate_lock",     "anti_whipsaw_gate_json"),
        ("alpha_lead_lock",            "alpha_lead_json"),
        ("distribution_lock",          "distribution_risk_json"),
        ("profit_preservation_lock",   "profit_preservation_json"),
        ("smart_cash_raise_lock",      "cash_raise_plan_json"),
        ("execution_quality_lock",     "execution_quality_json"),
    ]
    for lock_key, json_key in lock_json_pairs:
        lock_val = harness.get(lock_key)
        if str(lock_val).lower() == "true":
            json_val = parse_jsonish(harness.get(json_key))
            if json_val is None:
                errors.append(f"determinism: {lock_key}=true but {json_key} is missing")
            elif isinstance(json_val, list) and len(json_val) == 0:
                errors.append(f"determinism: {lock_key}=true but {json_key} is empty list")
    decision_lock = harness.get("decision_lock")
    if str(decision_lock).lower() == "true":
        decisions = parse_jsonish(harness.get("decisions_json"))
        trace = parse_jsonish(harness.get("decision_trace_json"))
        if isinstance(decisions, list) and isinstance(trace, list):
            final_map: dict[str, set] = {}
            for d in decisions:
                if isinstance(d, dict) and d.get("ticker"):
                    final_map.setdefault(str(d["ticker"]), set()).add(d.get("final_action"))
            for idx, t in enumerate(trace):
                if not isinstance(t, dict):
                    continue
                ticker = str(t.get("ticker") or "")
                selected = t.get("selected_action")
                allowed = final_map.get(ticker)
                if selected and allowed and selected not in allowed:
                    errors.append(
                        f"determinism: decision_trace[{idx}].selected_action={selected!r} not in decisions_json.final_action={sorted(allowed)!r}"
                    )


def validate_brt_harness(harness: dict[str, Any], errors: list[str]) -> None:
    brt = parse_jsonish(harness.get("benchmark_relative_timeseries_json"))
    if not isinstance(brt, list):
        errors.append("missing/invalid BRT key: benchmark_relative_timeseries_json")
    else:
        for idx, row in enumerate(brt):
            if not isinstance(row, dict):
                errors.append(f"benchmark_relative_timeseries_json[{idx}]: must be an object")
                continue
            verdict = row.get("brt_verdict")
            if verdict not in VALID_BRT_VERDICTS:
                errors.append(f"benchmark_relative_timeseries_json[{idx}].brt_verdict invalid: {verdict!r}")

    index_rows = parse_jsonish(harness.get("index_relative_health_json"))
    if not isinstance(index_rows, list):
        errors.append("missing/invalid BRT key: index_relative_health_json")
    else:
        for idx, row in enumerate(index_rows):
            if not isinstance(row, dict):
                errors.append(f"index_relative_health_json[{idx}]: must be an object")
                continue
            state = row.get("relative_health_state")
            if state not in {"HEALTHY", "OVER_EXTENDED", "UNDERPERFORMING", "DECOUPLED", "INSUFFICIENT_DATA"}:
                errors.append(f"index_relative_health_json[{idx}].relative_health_state invalid: {state!r}")

    saqg = parse_jsonish(harness.get("saqg_json"))
    if not isinstance(saqg, list):
        errors.append("missing/invalid BRT key: saqg_json")
    else:
        for idx, row in enumerate(saqg):
            if not isinstance(row, dict):
                errors.append(f"saqg_json[{idx}]: must be an object")
                continue
            state = row.get("saqg_v1")
            if state not in VALID_SAQG_STATES:
                errors.append(f"saqg_json[{idx}].saqg_v1 invalid: {state!r}")

    sapg = parse_jsonish(harness.get("sapg_json"))
    if not isinstance(sapg, dict):
        errors.append("missing/invalid BRT key: sapg_json")
    elif sapg.get("sapg_status") not in VALID_SAPG_STATUSES:
        errors.append(f"sapg_json.sapg_status invalid: {sapg.get('sapg_status')!r}")

    permissions = parse_jsonish(harness.get("buy_permission_json"))
    if isinstance(permissions, list):
        for idx, bp in enumerate(permissions):
            if not isinstance(bp, dict):
                continue
            if bp.get("saqg_v1") in {"EXCLUDED"} and str(bp.get("buy_permission_state") or "").startswith("ALLOW_"):
                errors.append(f"buy_permission_json[{idx}]: SAQG EXCLUDED but buy_permission_state=ALLOW_*")
            if bp.get("saqg_v1") == "WATCHLIST_ONLY" and str(bp.get("buy_permission_state") or "").startswith("ALLOW_"):
                errors.append(f"buy_permission_json[{idx}]: SAQG WATCHLIST_ONLY but buy_permission_state=ALLOW_*")


def main() -> int:
    args = sys.argv[1:]
    if not args or len(args) > 3:
        print(__doc__)
        return 1

    json_path = Path(args[0])
    strict = "--strict" in args
    check_mode: str | None = None
    if "--check" in args:
        idx = args.index("--check")
        if idx + 1 >= len(args):
            print("--check requires a mode: breakout_quality_gate|anti_whipsaw_gate|smart_cash_raise_v2|determinism|cla_harness|brt_harness")
            return 1
        check_mode = args[idx + 1]
        if check_mode not in VALID_CHECK_MODES:
            print(f"Unknown --check mode: {check_mode!r}. Valid: {', '.join(sorted(VALID_CHECK_MODES))}")
            return 1

    harness = load_harness(json_path)
    errors: list[str] = []

    if check_mode == "breakout_quality_gate":
        validate_breakout_quality_gate(harness, errors)
        label = "BREAKOUT_QUALITY_GATE"
    elif check_mode == "anti_whipsaw_gate":
        validate_anti_whipsaw_gate(harness, errors)
        label = "ANTI_WHIPSAW_GATE"
    elif check_mode == "smart_cash_raise_v2":
        validate_smart_cash_raise_v2(harness, errors)
        label = "SMART_CASH_RAISE_V2"
    elif check_mode == "determinism":
        validate_determinism(harness, errors)
        label = "DETERMINISM"
    elif check_mode == "cla_harness":
        validate_cla_harness(harness, errors)
        label = "CLA_HARNESS"
    elif check_mode == "brt_harness":
        validate_brt_harness(harness, errors)
        label = "BRT_HARNESS"
    elif check_mode == "factor_cap":
        # P1-1 (v11): PA1 단일팩터 50% 캡 검증
        import json as _json
        pa_path = json_path.parent / "predictive_alpha_engine_v2.json"
        if not pa_path.exists():
            pa_path = json_path.parent.parent / "Temp" / "predictive_alpha_engine_v2.json"
        label = "FACTOR_CAP"
        if pa_path.exists():
            pa = _json.loads(pa_path.read_text(encoding="utf-8"))
            audit = pa.get("factor_cap_audit", {})
            max_share = float(audit.get("single_factor_max_share_pct") or 0)
            pac_std = float(audit.get("pac_stddev") or 0)
            if max_share > 50.0:
                errors.append(f"single_factor_max_share_pct={max_share} > 50%")
            if pac_std < 5.0:
                errors.append(f"pac_stddev={pac_std} < 5.0")
        else:
            errors.append(f"predictive_alpha_engine_v2.json not found at {pa_path}")
    else:
        # Legacy / --strict mode
        apex_present = any(key in harness for key in REQUIRED_APEX_KEYS)
        if strict:
            validate_required_keys(harness, errors)
            # Also validate V5 keys in strict mode
            for key in REQUIRED_V5_KEYS:
                if key not in harness:
                    errors.append(f"missing V5 key: {key}")
            validate_alpha_feedback_loop(harness, errors)
            if errors:
                print("ALPHA EXECUTION HARNESS FAIL")
                for err in errors:
                    print(f"- {err}")
                return 1
        elif not apex_present:
            print("ALPHA EXECUTION HARNESS SKIPPED: APEX fields not present (use --strict after GAS Harness V5 export)")
            return 0
        validate_buy_blocks(harness, errors)
        validate_distribution_blocks(harness, errors)
        validate_cash_raise(harness, errors)
        validate_execution_quality(harness, errors)
        validate_alpha_feedback_loop(harness, errors)
        # V5 checks when keys present
        if "breakout_quality_gate_json" in harness:
            validate_breakout_quality_gate(harness, errors)
        if "anti_whipsaw_gate_json" in harness:
            validate_anti_whipsaw_gate(harness, errors)
        if "smart_cash_raise_json" in harness:
            validate_smart_cash_raise_v2(harness, errors)
        if "satellite_failure_gate_json" in harness:
            validate_cla_harness(harness, errors)
        if "benchmark_relative_timeseries_json" in harness:
            validate_brt_harness(harness, errors)
        label = "ALPHA EXECUTION HARNESS"

    if errors:
        print(f"{label} FAIL")
        for err in errors:
            print(f"- {err}")
        return 1
    print(f"{label} OK")
    return 0


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