#!/usr/bin/env python3
"""
validate_harness_json.py
harness_context 의도·결과 검증
JSON 경로: data.data._harness_context
"""

import json, pathlib, sys

# Windows cp949 터미널 호환
if sys.stdout.encoding and sys.stdout.encoding.lower() not in ('utf-8','utf8'):
    sys.stdout = open(sys.stdout.fileno(), mode='w', encoding='utf-8', buffering=1)

JSON_PATH = pathlib.Path('GatherTradingData.json')
SEP  = '=' * 60
SEP2 = '-' * 100

def safe_json(val):
    if isinstance(val, str):
        try:
            return json.loads(val)
        except Exception:
            return val
    return val

def main():
    if not JSON_PATH.exists():
        print(f'[ERROR] {JSON_PATH} not found')
        sys.exit(1)

    root = json.loads(JSON_PATH.read_text(encoding='utf-8'))

    # harness_context 탐색
    hc = None
    hc_path = 'unknown'
    try:
        hc = root['data']['_harness_context']
        hc_path = 'data._harness_context'
    except (KeyError, TypeError):
        pass
    if hc is None:
        for key in ['_harness_context', 'harness_context']:
            if key in root and isinstance(root[key], dict):
                hc = root[key]; hc_path = key; break
    if hc is None:
        print('[ERROR] harness_context 찾을 수 없음')
        print('root keys:', list(root.keys())[:10])
        sys.exit(1)

    print(SEP)
    print(f'파일: {JSON_PATH.name}')
    print(f'harness_context 경로: {hc_path}')
    print(f'키 수: {len(hc)}')
    print(SEP)

    # ── 1. HARNESS META ──────────────────────────────────────────────────────
    print('\n[1] HARNESS META')
    for k in ['harness_version', 'computed_at', 'captured_at', 'intraday_lock', 'mrs_score']:
        v = hc.get(k, '(missing)')
        warn = ''
        if k == 'harness_version':
            if v == '2026-05-19-X4R1':
                warn = '  <- OK (최신 X4R1)'
            else:
                warn = f'  <- [WARN] expected 2026-05-19-X4R1, got {v}'
        print(f'  {k}: {v}{warn}')

    # ── 2. H1 GUARDS ────────────────────────────────────────────────────────
    print('\n[2] H1 포트폴리오 가드 (QEH_AUDIT_BLOCK)')
    h1 = [
        ('total_heat_pct',         'TOTAL_HEAT_V1      '),
        ('heat_gate_status',       'HEAT_GATE          '),
        ('cash_floor_status',      'CASH_RATIOS_V1     '),
        ('cash_floor_min_pct',     'cash_floor_min_pct '),
        ('settlement_cash_pct',    'D+2 현금%          '),
        ('immediate_cash_krw',     'immediate_cash_krw '),
        ('buy_power_krw',          'buy_power_krw      '),
        ('total_asset_krw',        'total_asset_krw    '),
        ('performance_label',      'bayesian_label     '),
        ('performance_multiplier', 'bayesian_mult      '),
    ]
    for k, label in h1:
        v = hc.get(k, '(missing)')
        print(f'  [{label}] {v}')

    # ── 3. Alpha-Shield ──────────────────────────────────────────────────────
    print('\n[3] Alpha-Shield (X1/X3/W1~W4) [신규 2026-05-19-X1W1/X4R1]')
    as_keys = [
        'alpha_shield_lock',
        'alpha_shield_critical_alert_count',
        'alpha_shield_critical_alert_flag',
        'alpha_shield_formula_ids',
        'alpha_shield_computed_at',
    ]
    as_ok = True
    for k in as_keys:
        v = hc.get(k, '(missing)')
        flag = ' <- [FAIL] 누락' if v == '(missing)' else ''
        if v == '(missing)': as_ok = False
        print(f'  {k}: {v}{flag}')

    alpha_raw = hc.get('alpha_shield_json')
    if alpha_raw is None:
        print('  alpha_shield_json: (missing) <- [FAIL]')
        as_ok = False
    else:
        holdings = safe_json(alpha_raw)
        if isinstance(holdings, list) and holdings:
            print(f'\n  per-holding ({len(holdings)}종목):')
            cols = f'  {"Ticker":<8} {"MRG_Gate":<22} {"RS_Status":<16} {"W1":<20} {"W2":<20} {"W3":<20} {"W4":<24} {"Fires"}'
            print(cols)
            print('  ' + '-' * 135)
            for h in holdings:
                t     = h.get('ticker', '?')
                mrg   = h.get('mrg_gate', '?')
                rs    = h.get('rs_status', '?')
                w1    = h.get('w1_status', '?')
                w2    = h.get('w2_status', '?')
                w3    = h.get('w3_status', '?')
                w4    = h.get('w4_status', '?')
                fires = h.get('radar_fires', '?')
                crit  = ' [CRITICAL]' if h.get('critical_alert') == 'CRITICAL_ALERT' else ''
                print(f'  {t:<8} {mrg:<22} {rs:<16} {w1:<20} {w2:<20} {w3:<20} {w4:<24} {fires}{crit}')

            # deviation_ratio / rs_ratio 상세
            print()
            print(f'  {"Ticker":<8} {"deviation_ratio":<18} {"rs_ratio":<12} {"sector":<12} {"sector_rank":<12} {"flow_accel_ratio"}')
            print('  ' + '-' * 90)
            for h in holdings:
                t   = h.get('ticker', '?')
                dr  = h.get('deviation_ratio', 'N/A')
                rsr = h.get('rs_ratio', 'N/A')
                sec = h.get('sector', 'N/A') or 'N/A'
                srk = h.get('sector_rank', 'N/A')
                far = h.get('flow_accel_ratio', 'N/A')
                print(f'  {t:<8} {str(dr):<18} {str(rsr):<12} {str(sec):<12} {str(srk):<12} {far}')
        else:
            print(f'  alpha_shield_json: parse error or empty ({type(holdings).__name__})')
            as_ok = False

    print()
    print('  Alpha-Shield 전체 상태:', 'PASS' if as_ok else '[FAIL] 일부 누락')

    # ── 4. X4 Ratchet ────────────────────────────────────────────────────────
    print('\n[4] X4 Ratchet -- prices_json [신규 2026-05-19-X4R1]')
    prices_raw = hc.get('prices_json')
    if prices_raw is None:
        print('  prices_json: (missing)')
    else:
        prices = safe_json(prices_raw)
        if isinstance(prices, list):
            has_ratchet = any('ratchet_applied' in p for p in prices)
            if not has_ratchet:
                print('  [FAIL] ratchet_applied 필드 없음 -- X4 미적용')
            else:
                print(f'  {"Ticker":<8} {"stop_price":<12} {"avg_cost":<10} {"atr20":<8} {"ratchet_applied":<32} note[:50]')
                print('  ' + '-' * 120)
                for p in prices:
                    if 'error' in p:
                        print(f'  {p.get("ticker","?"):<8} ERROR: {p.get("error")}')
                        continue
                    t   = p.get('ticker', '?')
                    sp  = p.get('stop_price', '?')
                    ac  = p.get('avg_cost', '?')
                    atr = p.get('atr20', '?')
                    ra  = p.get('ratchet_applied', '(missing)')
                    note = str(p.get('ratchet_note', ''))[:50]
                    flag = ' <- [RATCHET ON]' if 'RATCHET' in str(ra) and 'INACTIVE' not in str(ra) and 'PASS' not in str(ra) else ''
                    print(f'  {t:<8} {str(sp):<12} {str(ac):<10} {str(atr):<8} {str(ra):<32} {note}{flag}')
        else:
            print(f'  prices_json type: {type(prices).__name__}')

    # ── 5. Gate 1d -- Decision Trace ─────────────────────────────────────────
    print('\n[5] Gate 1d (MEAN_REVERSION_GATE) -- decision_trace [신규 Gate]')
    traces_raw = hc.get('decision_trace_json')
    if traces_raw is None:
        print('  decision_trace_json: (missing)')
    else:
        traces = safe_json(traces_raw)
        if isinstance(traces, list):
            mrg = [t for t in traces if t.get('state') == 'MEAN_REVERSION_GATE']
            if not mrg:
                print('  [INFO] MEAN_REVERSION_GATE trace 없음')
                print('         -- 모든 종목이 BUY 아니거나 데이터 부재 (정상 가능)')
            else:
                print(f'  {"Ticker":<8} {"Result":<20} reason')
                print('  ' + '-' * 80)
                for t in mrg:
                    print(f'  {t.get("ticker","?"):<8} {t.get("result","?"):<20} {t.get("reason","?")}')
        else:
            print(f'  decode error: {type(traces).__name__}')

    # ── 6. Sell Priority ─────────────────────────────────────────────────────
    print('\n[6] Sell Priority (H2)')
    sc_raw = hc.get('sell_candidates_json')
    if sc_raw:
        cands = safe_json(sc_raw)
        if isinstance(cands, list):
            print(f'  {"Rank":<5} {"Ticker":<8} {"Tier":<5} {"Score":<7} {"Trim_Style":<14} reason[:65]')
            print('  ' + '-' * 110)
            for c in cands[:12]:
                print(f'  {str(c.get("rank","?")):<5} {c.get("ticker","?"):<8} '
                      f'{str(c.get("tier","?")):<5} {str(c.get("score","?")):<7} '
                      f'{str(c.get("trim_style","?")):<14} {str(c.get("reason",""))[:65]}')

    # ── 7. H5 Decision Flow ───────────────────────────────────────────────────
    print('\n[7] H5 Decision Flow')
    dec_raw = hc.get('decisions_json')
    if dec_raw:
        decs = safe_json(dec_raw)
        if isinstance(decs, list):
            print(f'  {"Ticker":<8} {"base_action":<20} {"final_action":<20} {"gate_changed":<14} trace gates')
            print('  ' + '-' * 100)
            for d in decs:
                gates = [tr.get('gate','?') + ':' + tr.get('result','?')
                         for tr in d.get('gate_trace', [])]
                gc = str(d.get('gate_changed', '?'))
                print(f'  {d.get("ticker","?"):<8} {d.get("base_action","?"):<20} '
                      f'{d.get("final_action","?"):<20} {gc:<14} {", ".join(gates)}')

    print()
    print(SEP)
    print('검증 완료')

if __name__ == '__main__':
    main()
