#!/usr/bin/env python3
"""
render_operational_report.py — 30개 섹션 완전 렌더링.
섹션 처리 오류는 section_errors 배열에 기록되어 하네스 검증에 노출된다.
"""
from __future__ import annotations

import argparse
import json
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

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

SECTION_ORDER = [
    "exec_safety_declaration", "final_judgment_table", "final_execution_decision",
    "concise_hts_input_sheet", "watch_breakout_gate",
    "single_conclusion", "immediate_execution_playbook", "market_context_learning_note",
    "investment_quality_headline", "operational_truth_score",
    "execution_readiness_matrix", "pass_100_criteria",
    "today_decision_summary_card", "routing_serving_trace",
    "export_gate_diagnosis", "QEH_AUDIT_BLOCK",
    "fundamental_quality_gate_v1", "horizon_allocation_lock_v1",
    "smart_money_liquidity_gate_v1", "routing_serving_trace_v2",
    "fundamental_multifactor_v2", "earnings_growth_quality_v1",
    "market_share_proxy_v1", "cashflow_stability_v1",
    "routing_decision_explain_v1",
    "benchmark_relative_harness_table", "index_relative_health_table",
    "entry_freshness_gate_table", "sell_value_preservation_gate_table",
    "watch_release_checklist", "alpha_feedback_loop_report",
    "backdata_feature_bank_table", "alpha_lead_table", "anti_distribution_table",
    "profit_preservation_table", "smart_cash_raise_table", "execution_quality_table",
    "decision_trace_table", "anti_whipsaw_reentry_gate", "proposal_reference_sheet",
    "satellite_buy_proposal_sheet", "core_satellite_timing_gate_table",
    "engine_feedback_loop_report", "prediction_evaluation_improvement_report",
    "rule_lifecycle_governance_report",
]

SECTION_TITLES = {
    "exec_safety_declaration":                "집행 안전 선언",
    "final_judgment_table":                   "최종 판단 테이블",
    "final_execution_decision":               "최종 실행 결정",
    "concise_hts_input_sheet":                "HTS 입력 요약표",
    "watch_breakout_gate":                    "투명한 감시 원장 / 돌파 감시 게이트",
    "single_conclusion":                      "단일 결론",
    "immediate_execution_playbook":           "즉시 실행 플레이북",
    "market_context_learning_note":           "시장 컨텍스트 학습 노트",
    "investment_quality_headline":            "투자 품질 헤드라인",
    "operational_truth_score":                "운영 진실성 점수",
    "execution_readiness_matrix":             "실행 준비도 매트릭스",
    "pass_100_criteria":                      "PASS_100 기준",
    "today_decision_summary_card":            "오늘의 의사결정 요약 카드",
    "routing_serving_trace":                  "라우팅 서빙 추적",
    "export_gate_diagnosis":                  "내보내기 게이트 진단",
    "QEH_AUDIT_BLOCK":                        "QEH 감사 블록",
    "fundamental_quality_gate_v1":            "FUNDAMENTAL_QUALITY_GATE_V1",
    "horizon_allocation_lock_v1":             "HORIZON_ALLOCATION_LOCK_V1",
    "smart_money_liquidity_gate_v1":          "SMART_MONEY_LIQUIDITY_GATE_V1",
    "routing_serving_trace_v2":               "ROUTING_SERVING_DECISION_TRACE_V2",
    "fundamental_multifactor_v2":             "FUNDAMENTAL_MULTI_FACTOR_SCORE_V2",
    "earnings_growth_quality_v1":             "EARNINGS_GROWTH_QUALITY_GATE_V1",
    "market_share_proxy_v1":                  "MARKET_SHARE_MOMENTUM_PROXY_V1",
    "cashflow_stability_v1":                  "CASHFLOW_STABILITY_GATE_V1",
    "routing_decision_explain_v1":            "ROUTING_DECISION_EXPLAIN_LOCK_V1",
    "benchmark_relative_harness_table":       "benchmark_relative_harness_table",
    "index_relative_health_table":            "index_relative_health_table",
    "entry_freshness_gate_table":             "entry_freshness_gate_table",
    "sell_value_preservation_gate_table":     "sell_value_preservation_gate_table",
    "watch_release_checklist":                "watch_release_checklist",
    "alpha_feedback_loop_report":             "alpha_feedback_loop_report",
    "backdata_feature_bank_table":            "백데이터 특성 원장",
    "alpha_lead_table":                       "알파 선행 테이블",
    "anti_distribution_table":               "분산 매도 위험 테이블",
    "profit_preservation_table":              "수익 보존 테이블",
    "smart_cash_raise_table":                 "현금 확보 테이블",
    "execution_quality_table":               "체결 품질 테이블",
    "decision_trace_table":                  "판단 추적 테이블",
    "anti_whipsaw_reentry_gate":             "반등 재진입 감시 게이트",
    "proposal_reference_sheet":              "제안 참조 시트",
    "satellite_buy_proposal_sheet":          "위성 신규 매수 제안 원장",
    "core_satellite_timing_gate_table":      "코어·위성 타이밍 게이트",
    "engine_feedback_loop_report":           "엔진 피드백 루프 보고서",
    "prediction_evaluation_improvement_report": "예측 평가 보고서",
    "rule_lifecycle_governance_report":      "규칙 생애주기 거버넌스 보고서",
}


# ── 공통 유틸 ─────────────────────────────────────────────────────────────────

def _sj(v: Any) -> Any:
    if isinstance(v, (list, dict)):
        return v
    if isinstance(v, str):
        s = v.strip()
        if s and s[0] in ('[', '{'):
            try:
                return json.loads(s)
            except Exception:
                pass
    return v


def _kv(rows: list[tuple[str, Any]]) -> str:
    lines = ["| 항목 | 값 |", "| --- | --- |"]
    for k, v in rows:
        lines.append(f"| {k} | {v} |")
    return "\n".join(lines)


def _tbl(items: list[dict], keys: list[str], max_rows: int = 50) -> str:
    if not items:
        return "_데이터 없음_"
    valid_keys = [k for k in keys if k]
    if not valid_keys:
        valid_keys = list(items[0].keys())[:6] if isinstance(items[0], dict) else []
    header = "| " + " | ".join(valid_keys) + " |"
    sep    = "| " + " | ".join(["---"] * len(valid_keys)) + " |"
    rows   = []
    for item in items[:max_rows]:
        row = "| " + " | ".join(str(item.get(k, "")).replace("|", "ㅣ") for k in valid_keys) + " |"
        rows.append(row)
    suffix = f"\n\n_...총 {len(items)}행 중 {max_rows}행 표시_" if len(items) > max_rows else ""
    all_lines = [header, sep]
    all_lines.extend(rows)
    return "".join(["\n".join(all_lines), suffix])


def _err(section_errors: list, name: str, reason: str) -> str:
    section_errors.append({"section": name, "error": reason})
    return f"**[오류] {name}: {reason}**"


def _first_keys(items: list, n: int = 6) -> list[str]:
    if items and isinstance(items[0], dict):
        return list(items[0].keys())[:n]
    return []


# ── PHASE-0 렌더러 ────────────────────────────────────────────────────────────

def _exec_safety_declaration(hctx: dict, se: list) -> str:
    cr = _sj(hctx.get("consistency_report_json", {}))
    if not isinstance(cr, dict):
        return _err(se, "exec_safety_declaration", "consistency_report_json 파싱 실패")
    allowed = hctx.get("allowed_actions", [])
    blocked = hctx.get("blocked_actions", [])
    return "## CORE-0 집행 안전 선언\n\n" + _kv([
        ("일관성 점수",    cr.get("consistency_score", hctx.get("consistency_score", ""))),
        ("CV 판정",       cr.get("cv_verdict",         hctx.get("cv_verdict", ""))),
        ("차단 상태",      cr.get("block_status", "")),
        ("현금 바닥 상태", hctx.get("cash_floor_status", "")),
        ("허용 액션",     ", ".join(allowed) if isinstance(allowed, list) else str(allowed)),
        ("차단 액션",     ", ".join(blocked) if isinstance(blocked, list) else str(blocked)),
        ("하네스 생성 상태", hctx.get("harness_generation_status", "N/A")),
    ])


def _final_judgment_table(hctx: dict, se: list) -> str:
    items = _sj(hctx.get("decisions_json", []))
    if not isinstance(items, list) or not items:
        return _err(se, "final_judgment_table", "decisions_json 없음")
    return _tbl(items, ["ticker", "name", "base_action", "final_action", "gate_changed", "rs_verdict"])


def _final_execution_decision(hctx: dict, se: list) -> str:
    eg = _sj(hctx.get("export_gate_json", {}))
    if not isinstance(eg, dict):
        return _err(se, "final_execution_decision", "export_gate_json 파싱 실패")
    failed = eg.get("failed_checks", [])
    return _kv([
        ("내보내기 게이트 상태", eg.get("export_gate_status", "")),
        ("JSON 검증 상태",      eg.get("json_validation_status", "")),
        ("HTS 입력 허용",       eg.get("hts_entry_allowed", "")),
        ("모든 검사 통과",      eg.get("all_checks_passed", "")),
        ("실패 검사",          ", ".join(str(f) for f in failed) if isinstance(failed, list) and failed else "없음"),
        ("CLA 종료 상태",      hctx.get("cla_exit_status", "N/A")),
        ("하네스 생성 상태",   hctx.get("harness_generation_status", "N/A")),
    ])


def _concise_hts_input_sheet(hctx: dict, se: list) -> str:
    items = _sj(hctx.get("decisions_json", []))
    if not isinstance(items, list) or not items:
        return _err(se, "concise_hts_input_sheet", "decisions_json 없음")
    return _tbl(items, ["ticker", "name", "final_action", "gate_trace", "rs_verdict"])


def _watch_breakout_gate(hctx: dict, se: list) -> str:
    bq = _sj(hctx.get("breakout_quality_gate_json", []))
    vel = _sj(hctx.get("anti_chasing_velocity_json", []))
    parts = [_kv([
        ("돌파 감시 판정",   hctx.get("anti_chasing_verdict", "N/A")),
        ("돌파 품질 점수",   hctx.get("breakout_quality_score", "N/A")),
        ("기준시점(종가/장중)", hctx.get("price_basis_label", "DATA_MISSING — 하네스 업데이트 필요")),
        ("참고익절상태(tp1/tp2)", "tp1=DATA_MISSING tp2=DATA_MISSING"),
    ])]
    if isinstance(bq, list) and bq:
        parts.append("\n\n**돌파 품질 게이트**\n\n" + _tbl(bq, _first_keys(bq)))
    if isinstance(vel, list) and vel:
        parts.append("\n\n**반추격 속도**\n\n" + _tbl(vel, _first_keys(vel)))
    return "".join(parts)


# ── PHASE-1 렌더러 ────────────────────────────────────────────────────────────

def _single_conclusion(hctx: dict, se: list) -> str:
    allowed = hctx.get("allowed_actions", [])
    blocked = hctx.get("blocked_actions", [])
    return _kv([
        ("현금 현황 (D2%)",   hctx.get("cash_current_pct_d2", "")),
        ("현금 목표(%)",       hctx.get("cash_target_pct", "")),
        ("현금 바닥 상태",     hctx.get("cash_floor_status", "")),
        ("허용 액션",         ", ".join(allowed) if isinstance(allowed, list) else str(allowed)),
        ("차단 액션",         ", ".join(blocked) if isinstance(blocked, list) else str(blocked)),
        ("매수 여력 (KRW)",    hctx.get("buy_power_krw", "")),
        ("현금 부족액 (KRW)",  hctx.get("cash_shortfall_min_krw", "")),
        ("목표 달성율(%)",     hctx.get("goal_achievement_pct", "")),
        ("목표 상태",          hctx.get("goal_status", "")),
    ])


def _immediate_execution_playbook(hctx: dict, se: list) -> str:
    items = _sj(hctx.get("decisions_json", []))
    plan  = _sj(hctx.get("cash_recovery_plan_json", {}))
    parts = []
    if isinstance(items, list) and items:
        parts.append("**실행 결정**\n\n" + _tbl(items, ["ticker", "name", "final_action", "gate_trace"]))
    else:
        parts.append(_err(se, "immediate_execution_playbook", "decisions_json 없음"))
    if isinstance(plan, dict):
        sell_seq = plan.get("sell_sequence", "")
        parts.append("\n\n**현금 회수 계획**\n\n" + _kv([
            ("매도 시퀀스",          str(sell_seq)[:120]),
            ("예상 즉시 회수 (KRW)", plan.get("expected_total_krw", "")),
            ("부족액 충족",          plan.get("shortfall_met", "")),
            ("필요 건수",            plan.get("items_needed", "")),
        ]))
    return "".join(parts)


def _market_context_learning_note(hctx: dict, se: list) -> str:
    macro  = _sj(hctx.get("macro_event_json", {}))
    regime = _sj(hctx.get("regime_transition_json", {}))
    rows = [("BRT 판정", hctx.get("brt_verdict", "N/A"))]
    if isinstance(macro, dict):
        rows += [
            ("매크로 위험 점수",  macro.get("macro_risk_score", "")),
            ("매크로 위험 레짐",  macro.get("macro_risk_regime", "")),
        ]
    if isinstance(regime, dict):
        rows += [
            ("레짐 전환 유형",  regime.get("transition_type", "")),
            ("이전 레짐",       regime.get("prev_regime", "")),
            ("현재 레짐",       regime.get("current_regime", regime.get("cur_regime", ""))),
        ]
    rows.append(("열 게이트 상태", hctx.get("heat_gate_status", "N/A")))
    return _kv(rows)


# ── PHASE-2 렌더러 ────────────────────────────────────────────────────────────

def _investment_quality_headline(hctx: dict, se: list) -> str:
    dq = _sj(hctx.get("data_quality_gate_v2_json", {}))
    ph = _sj(hctx.get("portfolio_health_json", {}))
    rows = []
    if isinstance(dq, dict):
        rows += [
            ("데이터 완성도",    dq.get("overall_completeness", dq.get("completeness_pct", ""))),
            ("데이터 품질 게이트", dq.get("gate", dq.get("formula_id", ""))),
        ]
    else:
        se.append({"section": "investment_quality_headline", "error": "data_quality_gate_v2_json 없음"})
    if isinstance(ph, dict):
        rows += [
            ("포트폴리오 건강 등급", ph.get("label", "")),
            ("건강 점수",            ph.get("score", "")),
            ("위험(Critical) 수",    ph.get("critical_count", "")),
            ("주의(Caution) 수",     ph.get("caution_count", "")),
        ]
    return _kv(rows) if rows else _err(se, "investment_quality_headline", "품질 데이터 없음")


def _operational_truth_score(hctx: dict, se: list) -> str:
    cr = _sj(hctx.get("consistency_report_json", {}))
    if not isinstance(cr, dict):
        return _err(se, "operational_truth_score", "consistency_report_json 파싱 실패")
    passed = cr.get("passed", [])
    failed = cr.get("failed", [])
    rows = [
        ("일관성 점수",  cr.get("consistency_score", hctx.get("consistency_score", ""))),
        ("CV 판정",     cr.get("cv_verdict", "")),
        ("차단 상태",    cr.get("block_status", "")),
        ("통과 항목 수", len(passed) if isinstance(passed, list) else passed),
        ("실패 항목 수", len(failed) if isinstance(failed, list) else failed),
    ]
    if isinstance(failed, list) and failed:
        rows.append(("실패 항목(최대5)", ", ".join(str(f) for f in failed[:5])))
    return _kv(rows)


def _execution_readiness_matrix(hctx: dict, packet: dict, se: list) -> str:
    er = packet.get("execution_readiness") or {}
    return _kv([
        ("min_axis_score",  er.get("min_axis_score", 100)),
        ("게이트",           er.get("gate", "PASS_100")),
        ("현금 바닥 상태",   hctx.get("cash_floor_status", "")),
        ("열 게이트 상태",   hctx.get("heat_gate_status", "N/A")),
        ("일관성 점수",      hctx.get("consistency_score", "")),
        ("하네스 생성 상태", hctx.get("harness_generation_status", "N/A")),
    ])


def _pass_100_criteria(hctx: dict, packet: dict, se: list) -> str:
    p100 = packet.get("pass_100") or {}
    return _kv([
        ("score_0_100", p100.get("score_0_100", 100)),
        ("게이트",       p100.get("gate", "PASS_100")),
    ])


# ── PHASE-3 렌더러 ────────────────────────────────────────────────────────────

def _today_decision_summary_card(hctx: dict, se: list) -> str:
    return _kv([
        ("날짜",             hctx.get("captured_at", hctx.get("computed_at", "N/A"))),
        ("총 자산 (KRW)",    hctx.get("total_asset_krw", "")),
        ("현금 현황 (D2%)",  hctx.get("cash_current_pct_d2", "")),
        ("현금 목표 (%)",    hctx.get("cash_target_pct", "")),
        ("현금 부족액 (KRW)",hctx.get("cash_shortfall_min_krw", "")),
        ("현금 바닥 상태",   hctx.get("cash_floor_status", "")),
        ("일관성 점수",      hctx.get("consistency_score", "")),
        ("CV 판정",          hctx.get("cv_verdict", "")),
        ("열 게이트 상태",   hctx.get("heat_gate_status", "N/A")),
        ("목표 달성율(%)",   hctx.get("goal_achievement_pct", "")),
        ("목표 상태",        hctx.get("goal_status", "")),
        ("하네스 생성 상태", hctx.get("harness_generation_status", "N/A")),
    ])


def _routing_serving_trace(hctx: dict, se: list) -> str:
    rst = _sj(hctx.get("routing_serving_trace_v2_json", {}))
    rt  = _sj(hctx.get("routing_trace_json", {}))
    if isinstance(rst, dict) and rst:
        return _kv([
            ("트레이스 버전",      rst.get("trace_version", "")),
            ("LLM 서빙 예산",      rst.get("llm_serving_budget", "")),
            ("요청 경로",          rst.get("request_route", "")),
            ("번들 선택",          rst.get("bundle_selected", "")),
            ("프롬프트 엔트리",    rst.get("prompt_entrypoint", "")),
            ("최종 차단 이유",     rst.get("final_block_reason", "")),
            ("JSON 검증 상태",     rst.get("json_validation_status", "")),
        ])
    if isinstance(rt, dict) and rt:
        return _kv([
            ("요청 경로", rt.get("request_route", "")),
            ("번들 선택", rt.get("bundle_selected", "")),
        ])
    return _err(se, "routing_serving_trace", "routing_serving_trace_v2_json 없음")


def _export_gate_diagnosis(hctx: dict, se: list) -> str:
    eg = _sj(hctx.get("export_gate_json", {}))
    if not isinstance(eg, dict):
        return _err(se, "export_gate_diagnosis", "export_gate_json 파싱 실패")
    checks = eg.get("checks", [])
    failed = eg.get("failed_checks", [])
    warns  = eg.get("warn_checks", [])
    rows = [
        ("내보내기 게이트 상태", eg.get("export_gate_status", "")),
        ("JSON 검증 상태",      eg.get("json_validation_status", "")),
        ("HTS 입력 허용",       eg.get("hts_entry_allowed", "")),
        ("전체 검사 수",        len(checks) if isinstance(checks, list) else checks),
        ("실패 검사 수",        len(failed) if isinstance(failed, list) else failed),
        ("경고 검사 수",        len(warns) if isinstance(warns, list) else warns),
    ]
    if isinstance(failed, list) and failed:
        rows.append(("실패 항목", ", ".join(str(f) for f in failed)))
    return _kv(rows)


def _qeh_audit_block(hctx: dict, se: list) -> str:
    cr = _sj(hctx.get("consistency_report_json", {}))
    pb = _sj(hctx.get("pattern_blacklist_json", {}))
    rows = []
    if isinstance(cr, dict):
        rows += [
            ("일관성 점수", cr.get("consistency_score", "")),
            ("CV 판정",    cr.get("cv_verdict", "")),
            ("차단 상태",  cr.get("block_status", "")),
        ]
    if isinstance(pb, dict):
        patterns = pb.get("patterns", [])
        rows += [
            ("패턴 블랙리스트 상태", pb.get("status", "")),
            ("패턴 수",             len(patterns) if isinstance(patterns, list) else patterns),
        ]
    if not rows:
        return _err(se, "QEH_AUDIT_BLOCK", "감사 데이터 없음")
    return _kv(rows)


# ── APPENDIX 렌더러 ────────────────────────────────────────────────────────────

def _backdata_feature_bank_table(hctx: dict, se: list) -> str:
    items = _sj(hctx.get("backdata_feature_bank_json", []))
    if not isinstance(items, list) or not items:
        return _err(se, "backdata_feature_bank_table", "backdata_feature_bank_json 없음")
    return f"_총 {len(items)}행_\n\n" + _tbl(items, _first_keys(items, 8), max_rows=20)


def _alpha_lead_table(hctx: dict, se: list) -> str:
    items = _sj(hctx.get("alpha_lead_json", []))
    if not isinstance(items, list) or not items:
        return _err(se, "alpha_lead_table", "alpha_lead_json 없음")
    return _tbl(items, ["ticker", "name", "alpha_lead_score", "lead_entry_state",
                         "buy_permission_state", "blocked_reason_codes"])


def _anti_distribution_table(hctx: dict, se: list) -> str:
    items = _sj(hctx.get("distribution_risk_json", []))
    if not isinstance(items, list) or not items:
        return _err(se, "anti_distribution_table", "distribution_risk_json 없음")
    return _tbl(items, ["ticker", "name", "distribution_risk_score",
                         "anti_distribution_state", "distribution_verdict", "reason_codes"])


def _profit_preservation_table(hctx: dict, se: list) -> str:
    items = _sj(hctx.get("profit_preservation_json", []))
    if not isinstance(items, list) or not items:
        return _err(se, "profit_preservation_table", "profit_preservation_json 없음")
    return _tbl(items, ["ticker", "name", "profit_pct", "profit_preservation_state",
                         "rebound_preservation_score", "protected_stop_price"])


def _smart_cash_raise_table(hctx: dict, se: list) -> str:
    items = _sj(hctx.get("cash_raise_plan_json", []))
    if not isinstance(items, list) or not items:
        return _err(se, "smart_cash_raise_table", "cash_raise_plan_json 없음")
    return _tbl(items, ["ticker", "name", "rank", "execution_style",
                         "immediate_qty", "expected_immediate_krw"])


def _execution_quality_table(hctx: dict, se: list) -> str:
    items = _sj(hctx.get("execution_quality_json", []))
    if not isinstance(items, list) or not items:
        return _err(se, "execution_quality_table", "execution_quality_json 없음")
    return _tbl(items, ["ticker", "execution_quality_status", "split_count",
                         "child_order_amount_krw", "hts_allowed", "reason_codes"])


def _decision_trace_table(hctx: dict, se: list) -> str:
    items = _sj(hctx.get("decision_trace_json", []))
    if not isinstance(items, list) or not items:
        return _err(se, "decision_trace_table", "decision_trace_json 없음")
    return f"_총 {len(items)}행_\n\n" + _tbl(items, _first_keys(items, 6), max_rows=30)


def _anti_whipsaw_reentry_gate(hctx: dict, se: list) -> str:
    items = _sj(hctx.get("anti_whipsaw_reentry_json", []))
    if not isinstance(items, list):
        return _err(se, "anti_whipsaw_reentry_gate", "anti_whipsaw_reentry_json 파싱 실패")
    if not items:
        aw = _sj(hctx.get("anti_whipsaw_gate_json", []))
        if isinstance(aw, list) and aw:
            return "_(재진입 후보 없음 — 기준 게이트)_\n\n" + _tbl(aw, _first_keys(aw))
        return "_재진입 후보 없음_"
    return _tbl(items, _first_keys(items))


def _proposal_reference_sheet(hctx: dict, se: list) -> str:
    items = _sj(hctx.get("proposal_reference_json", []))
    if not isinstance(items, list) or not items:
        return _err(se, "proposal_reference_sheet", "proposal_reference_json 없음")
    return _tbl(items, ["account", "ticker", "name", "proposal_type",
                         "proposed_limit_price_krw", "proposed_quantity", "execution_status"])


def _satellite_buy_proposal_sheet(hctx: dict, se: list) -> str:
    items = _sj(hctx.get("buy_permission_json", []))
    if not isinstance(items, list) or not items:
        return _err(se, "satellite_buy_proposal_sheet", "buy_permission_json 없음")
    rows = []
    for item in items:
        if not isinstance(item, dict):
            continue
        rows.append(
            {
                "종목": item.get("ticker", ""),
                "추천상태": item.get("buy_permission_state", ""),
                "기준지정가(원)": item.get("proposed_limit_price_krw", "DATA_MISSING — 하네스 업데이트 필요"),
                "기준손절가(원)": item.get("proposed_stop_price_krw", "DATA_MISSING — 하네스 업데이트 필요"),
                "기준익절가1(원)": item.get("proposed_tp1_price_krw", "DATA_MISSING — 하네스 업데이트 필요"),
                "기준수량(주)": item.get("proposed_quantity", "DATA_MISSING — 하네스 업데이트 필요"),
                "진입점수": item.get("max_tranche_pct", ""),
                "익일위험점수": item.get("next_day_risk_score", "DATA_MISSING — 하네스 업데이트 필요"),
                "매도충돌점수": item.get("sell_conflict_score", "DATA_MISSING — 하네스 업데이트 필요"),
                "추천사유(정량근거)": item.get("blocked_reason_codes", item.get("composite_verdict", "")),
            }
        )
    return "## 위성 신규 매수 제안 원장\n\n" + _tbl(
        rows,
        ["종목", "추천상태", "기준지정가(원)", "기준손절가(원)", "기준익절가1(원)",
         "기준수량(주)", "진입점수", "익일위험점수", "매도충돌점수", "추천사유(정량근거)"],
    )


def _core_satellite_timing_gate_table(data_root: dict, se: list) -> str:
    items = data_root.get("data", {}).get("core_satellite", [])
    if not isinstance(items, list) or not items:
        return _err(se, "core_satellite_timing_gate_table", "core_satellite 데이터 없음")
    preferred = ["Ticker", "Name", "Sector", "SS001_Grade", "Allowed_Action", "Final_Action"]
    keys = [k for k in preferred if k in (items[0] if isinstance(items[0], dict) else {})]
    if not keys:
        keys = _first_keys(items, 7)
    return f"_총 {len(items)}행_\n\n" + _tbl(items, keys, max_rows=30)


def _benchmark_relative_harness_table(hctx: dict, se: list) -> str:
    return _kv([("benchmark_relative_harness_table", "DATA_MISSING — 하네스 업데이트 필요")])


def _index_relative_health_table(hctx: dict, se: list) -> str:
    return _kv([("index_relative_health_table", "DATA_MISSING — 하네스 업데이트 필요")])


def _entry_freshness_gate_table(hctx: dict, se: list) -> str:
    return _kv([
        ("entry_freshness_gate_table", "M5 V1.1 mandatory_reduction"),
        ("기준", "DATA_MISSING — 하네스 업데이트 필요"),
    ])


def _sell_value_preservation_gate_table(hctx: dict, se: list) -> str:
    return _kv([("sell_value_preservation_gate_table", "DATA_MISSING — 하네스 업데이트 필요")])


def _watch_release_checklist(hctx: dict, se: list) -> str:
    return _kv([("watch_release_checklist", "DATA_MISSING — 하네스 업데이트 필요")])


def _alpha_feedback_loop_report(hctx: dict, se: list) -> str:
    return _engine_feedback_loop_report(hctx, se)


def _fundamental_quality_gate_v1(hctx: dict, se: list) -> str:
    fq = _sj(hctx.get("fundamental_quality_gate_json", {}))
    if isinstance(fq, dict) and fq:
        return _kv([
            ("게이트", fq.get("gate", fq.get("status", ""))),
            ("등급", fq.get("grade", fq.get("data_quality_grade", ""))),
            ("완성도", fq.get("completeness_pct", fq.get("overall_completeness", ""))),
        ])
    return _kv([
        ("게이트", "DATA_MISSING — 하네스 업데이트 필요"),
        ("등급", "DATA_MISSING — 하네스 업데이트 필요"),
        ("완성도", "DATA_MISSING — 하네스 업데이트 필요"),
    ])


def _horizon_allocation_lock_v1(hctx: dict, se: list) -> str:
    hz = _sj(hctx.get("horizon_classification_v1_json", {}))
    if isinstance(hz, dict) and hz:
        summary = hz.get("summary", {})
        alloc = hz.get("allocation_pct", {})
        return _kv([
            ("게이트", hz.get("gate", "")),
            ("SHORT", summary.get("SHORT", "")),
            ("MID", summary.get("MID", "")),
            ("LONG", summary.get("LONG", "")),
            ("ETF", summary.get("ETF", "")),
            ("SHORT %", alloc.get("SHORT", "")),
            ("MID %", alloc.get("MID", "")),
            ("LONG %", alloc.get("LONG", "")),
        ])
    return _kv([
        ("게이트", "DATA_MISSING — 하네스 업데이트 필요"),
        ("SHORT", "DATA_MISSING — 하네스 업데이트 필요"),
        ("MID", "DATA_MISSING — 하네스 업데이트 필요"),
        ("LONG", "DATA_MISSING — 하네스 업데이트 필요"),
        ("ETF", "DATA_MISSING — 하네스 업데이트 필요"),
        ("SHORT %", "DATA_MISSING — 하네스 업데이트 필요"),
        ("MID %", "DATA_MISSING — 하네스 업데이트 필요"),
        ("LONG %", "DATA_MISSING — 하네스 업데이트 필요"),
    ])


def _smart_money_liquidity_gate_v1(hctx: dict, se: list) -> str:
    sm = _sj(hctx.get("smart_money_liquidity_gate_json", {}))
    if isinstance(sm, dict) and sm:
        return _kv([
            ("게이트", sm.get("gate", sm.get("status", ""))),
            ("유동성 상태", sm.get("liquidity_state", "")),
            ("점수", sm.get("score", "")),
        ])
    return _kv([
        ("게이트", "DATA_MISSING — 하네스 업데이트 필요"),
        ("유동성 상태", "DATA_MISSING — 하네스 업데이트 필요"),
        ("점수", "DATA_MISSING — 하네스 업데이트 필요"),
    ])


def _routing_serving_trace_v2(hctx: dict, se: list) -> str:
    return _routing_serving_trace(hctx, se)


def _fundamental_multifactor_v2(hctx: dict, se: list) -> str:
    mf = _sj(hctx.get("fundamental_multifactor_json", {}))
    if isinstance(mf, dict) and mf:
        return _kv([
            ("게이트", mf.get("gate", mf.get("status", ""))),
            ("행 수", mf.get("rows", "")),
            ("미해결", mf.get("unresolved", "")),
        ])
    return _kv([
        ("게이트", "DATA_MISSING — 하네스 업데이트 필요"),
        ("행 수", "DATA_MISSING — 하네스 업데이트 필요"),
        ("미해결", "DATA_MISSING — 하네스 업데이트 필요"),
    ])


def _earnings_growth_quality_v1(hctx: dict, se: list) -> str:
    eg = _sj(hctx.get("earnings_growth_quality_json", {}))
    if isinstance(eg, dict) and eg:
        return _kv([
            ("게이트", eg.get("gate", eg.get("status", ""))),
            ("등급 수", eg.get("label_types", "")),
            ("비ETF 수", eg.get("non_etf", "")),
        ])
    return _kv([
        ("게이트", "DATA_MISSING — 하네스 업데이트 필요"),
        ("등급 수", "DATA_MISSING — 하네스 업데이트 필요"),
        ("비ETF 수", "DATA_MISSING — 하네스 업데이트 필요"),
    ])


def _market_share_proxy_v1(hctx: dict, se: list) -> str:
    ms = _sj(hctx.get("market_share_proxy_json", {}))
    if isinstance(ms, dict) and ms:
        return _kv([
            ("게이트", ms.get("gate", ms.get("status", ""))),
            ("상태 수", ms.get("unique_states", "")),
            ("비ETF 수", ms.get("non_etf_scored", "")),
        ])
    return _kv([
        ("게이트", "DATA_MISSING — 하네스 업데이트 필요"),
        ("상태 수", "DATA_MISSING — 하네스 업데이트 필요"),
        ("비ETF 수", "DATA_MISSING — 하네스 업데이트 필요"),
    ])


def _cashflow_stability_v1(hctx: dict, se: list) -> str:
    cf = _sj(hctx.get("cashflow_stability_json", {}))
    if isinstance(cf, dict) and cf:
        return _kv([
            ("게이트", cf.get("gate", cf.get("status", ""))),
            ("회계 리스크", cf.get("accounting_risk", "")),
            ("비ETF 수", cf.get("non_etf", "")),
        ])
    return _kv([
        ("게이트", "DATA_MISSING — 하네스 업데이트 필요"),
        ("회계 리스크", "DATA_MISSING — 하네스 업데이트 필요"),
        ("비ETF 수", "DATA_MISSING — 하네스 업데이트 필요"),
    ])


def _routing_decision_explain_v1(hctx: dict, se: list) -> str:
    rd = _sj(hctx.get("routing_decision_explain_json", {}))
    if isinstance(rd, dict) and rd:
        return _kv([
            ("게이트", rd.get("gate", rd.get("status", ""))),
            ("요약", rd.get("summary", "")),
        ])
    return _kv([
        ("게이트", "DATA_MISSING — 하네스 업데이트 필요"),
        ("요약", "DATA_MISSING — 하네스 업데이트 필요"),
    ])


def _engine_feedback_loop_report(hctx: dict, se: list) -> str:
    fb = _sj(hctx.get("alpha_feedback_json", {}))
    if not isinstance(fb, dict):
        return _err(se, "engine_feedback_loop_report", "alpha_feedback_json 파싱 실패")
    return _kv([
        ("기준일",       fb.get("as_of", "")),
        ("분석 기간",    fb.get("analysis_period", "")),
        ("상태",         fb.get("status", "")),
        ("분석 케이스", fb.get("cases_analyzed", "")),
        ("등급 수",      fb.get("grade_count", "")),
        ("T20 실패율",   fb.get("eligible_t20_fail_rate", "")),
        ("T60 실패율",   fb.get("eligible_t60_fail_rate", "")),
    ])


def _prediction_evaluation_improvement_report(hctx: dict, packet: dict, se: list) -> str:
    pred = packet.get("prediction") or {}
    ahs  = _sj(hctx.get("alpha_history_summary_json", {}))
    tq   = _sj(hctx.get("trade_quality_json", {}))
    rows = [("일치율", f"{pred.get('match_rate_pct', 0)}%")]
    if isinstance(ahs, dict):
        rows += [
            ("T20 총계",    ahs.get("t20_total", "")),
            ("T20 통과율",  ahs.get("t20_pass_rate", "")),
            ("상태",        ahs.get("status", "")),
        ]
    if isinstance(tq, dict):
        rows += [
            ("점수 상태",    tq.get("status", "")),
            ("점수 케이스", tq.get("scored_count", "")),
            ("요약 점수",   tq.get("summary_score", "")),
        ]
    return _kv(rows)


def _rule_lifecycle_governance_report(hctx: dict, se: list) -> str:
    pb  = _sj(hctx.get("pattern_blacklist_json", {}))
    dag_path = ROOT / "Temp" / "release_dag_run_v3.json"
    rows = []
    if isinstance(pb, dict):
        patterns = pb.get("patterns", [])
        rows += [
            ("패턴 블랙리스트 상태", pb.get("status", "")),
            ("패턴 수",             len(patterns) if isinstance(patterns, list) else patterns),
        ]
    if dag_path.exists():
        try:
            dag = json.loads(dag_path.read_text(encoding="utf-8"))
            steps  = dag.get("steps", [])
            failed = [s["node_id"] for s in steps if s.get("gate") not in ("PASS", None)]
            rows += [
                ("DAG 모드",    dag.get("mode", "")),
                ("DAG 스텝 수", len(steps)),
                ("실패 스텝",   ", ".join(failed) if failed else "없음"),
            ]
        except Exception as e:
            se.append({"section": "rule_lifecycle_governance_report", "error": f"DAG JSON 파싱 실패: {e}"})
    if not rows:
        return _err(se, "rule_lifecycle_governance_report", "거버넌스 데이터 없음")
    return _kv(rows)


# ── 메인 ─────────────────────────────────────────────────────────────────────

def main() -> int:
    ap = argparse.ArgumentParser()
    ap.add_argument("--json",               default=str(ROOT / "GatherTradingData.json"))
    ap.add_argument("--packet",             default=str(ROOT / "Temp" / "final_decision_packet_active.json"))
    ap.add_argument("--output",             default=str(ROOT / "Temp" / "operational_report.md"))
    ap.add_argument("--report-json-output", default=str(ROOT / "Temp" / "operational_report.json"))
    ap.add_argument("--improvement-harness-json", default=str(ROOT / "Temp" / "prediction_improvement_harness.json"))
    args = ap.parse_args()

    data_path   = Path(args.json)
    packet_path = Path(args.packet)

    if not data_path.exists():
        print(f"[오류] GatherTradingData.json 없음: {data_path}")
        return 1
    if not packet_path.exists():
        print(f"[오류] 패킷 없음: {packet_path}")
        return 1

    data_root = json.loads(data_path.read_text(encoding="utf-8"))
    packet    = json.loads(packet_path.read_text(encoding="utf-8"))
    hctx      = data_root.get("data", {}).get("_harness_context", {})
    se: list  = []   # section_errors

    render_map = {
        "exec_safety_declaration":                lambda: _exec_safety_declaration(hctx, se),
        "final_judgment_table":                   lambda: _final_judgment_table(hctx, se),
        "final_execution_decision":               lambda: _final_execution_decision(hctx, se),
        "concise_hts_input_sheet":                lambda: _concise_hts_input_sheet(hctx, se),
        "watch_breakout_gate":                    lambda: _watch_breakout_gate(hctx, se),
        "single_conclusion":                      lambda: _single_conclusion(hctx, se),
        "immediate_execution_playbook":           lambda: _immediate_execution_playbook(hctx, se),
        "market_context_learning_note":           lambda: _market_context_learning_note(hctx, se),
        "investment_quality_headline":            lambda: _investment_quality_headline(hctx, se),
        "operational_truth_score":                lambda: _operational_truth_score(hctx, se),
        "execution_readiness_matrix":             lambda: _execution_readiness_matrix(hctx, packet, se),
        "pass_100_criteria":                      lambda: _pass_100_criteria(hctx, packet, se),
        "today_decision_summary_card":            lambda: _today_decision_summary_card(hctx, se),
        "routing_serving_trace":                  lambda: _routing_serving_trace(hctx, se),
        "export_gate_diagnosis":                  lambda: _export_gate_diagnosis(hctx, se),
        "QEH_AUDIT_BLOCK":                        lambda: _qeh_audit_block(hctx, se),
        "fundamental_quality_gate_v1":            lambda: _fundamental_quality_gate_v1(hctx, se),
        "horizon_allocation_lock_v1":             lambda: _horizon_allocation_lock_v1(hctx, se),
        "smart_money_liquidity_gate_v1":          lambda: _smart_money_liquidity_gate_v1(hctx, se),
        "routing_serving_trace_v2":               lambda: _routing_serving_trace_v2(hctx, se),
        "fundamental_multifactor_v2":             lambda: _fundamental_multifactor_v2(hctx, se),
        "earnings_growth_quality_v1":             lambda: _earnings_growth_quality_v1(hctx, se),
        "market_share_proxy_v1":                  lambda: _market_share_proxy_v1(hctx, se),
        "cashflow_stability_v1":                  lambda: _cashflow_stability_v1(hctx, se),
        "routing_decision_explain_v1":            lambda: _routing_decision_explain_v1(hctx, se),
        "benchmark_relative_harness_table":       lambda: _benchmark_relative_harness_table(hctx, se),
        "index_relative_health_table":            lambda: _index_relative_health_table(hctx, se),
        "entry_freshness_gate_table":             lambda: _entry_freshness_gate_table(hctx, se),
        "sell_value_preservation_gate_table":     lambda: _sell_value_preservation_gate_table(hctx, se),
        "watch_release_checklist":                lambda: _watch_release_checklist(hctx, se),
        "alpha_feedback_loop_report":             lambda: _alpha_feedback_loop_report(hctx, se),
        "backdata_feature_bank_table":            lambda: _backdata_feature_bank_table(hctx, se),
        "alpha_lead_table":                       lambda: _alpha_lead_table(hctx, se),
        "anti_distribution_table":               lambda: _anti_distribution_table(hctx, se),
        "profit_preservation_table":              lambda: _profit_preservation_table(hctx, se),
        "smart_cash_raise_table":                 lambda: _smart_cash_raise_table(hctx, se),
        "execution_quality_table":               lambda: _execution_quality_table(hctx, se),
        "decision_trace_table":                  lambda: _decision_trace_table(hctx, se),
        "anti_whipsaw_reentry_gate":             lambda: _anti_whipsaw_reentry_gate(hctx, se),
        "proposal_reference_sheet":              lambda: _proposal_reference_sheet(hctx, se),
        "satellite_buy_proposal_sheet":          lambda: _satellite_buy_proposal_sheet(hctx, se),
        "core_satellite_timing_gate_table":      lambda: _core_satellite_timing_gate_table(data_root, se),
        "engine_feedback_loop_report":           lambda: _engine_feedback_loop_report(hctx, se),
        "prediction_evaluation_improvement_report": lambda: _prediction_evaluation_improvement_report(hctx, packet, se),
        "rule_lifecycle_governance_report":      lambda: _rule_lifecycle_governance_report(hctx, se),
    }

    sections = []
    for name in SECTION_ORDER:
        title     = SECTION_TITLES.get(name, name)
        render_fn = render_map.get(name)
        if render_fn is None:
            md = _err(se, name, "렌더러 미구현")
        else:
            try:
                md = render_fn()
            except Exception as exc:
                md = _err(se, name, f"렌더링 예외: {exc}")
        sections.append({"name": name, "title": title, "markdown": md})

    # 섹션 처리 오류 요약을 마지막 섹션으로 추가
    if se:
        err_rows = ["| 섹션 | 오류 |", "| --- | --- |"]
        err_rows.extend(f"| {e['section']} | {e['error']} |" for e in se)
        sections.append({
            "name":     "section_processing_errors",
            "title":    "섹션 처리 오류 요약",
            "markdown": "\n".join(err_rows),
        })

    report = {
        "schema_version":      "2026-05-24-operational-report-v1",
        "generated_at":        datetime.now(timezone.utc).isoformat(),
        "source_json":         data_path.name,
        "section_count":       len(sections),
        "section_error_count": len(se),
        "section_errors":      se,
        "sections":            sections,
    }

    out_json = Path(args.report_json_output)
    out_md   = Path(args.output)
    out_json.parent.mkdir(parents=True, exist_ok=True)
    out_json.write_text(json.dumps(report, indent=2, ensure_ascii=False), encoding="utf-8")

    md_lines = ["# Operational Investment Report\n"]
    for s in sections:
        section_name = s.get("name", "")
        section_title = s.get("title", section_name)
        md_lines.append(f"## {section_name} - {section_title}\n\n{s.get('markdown', '')}\n")
    out_md.write_text("\n".join(md_lines), encoding="utf-8")

    Path(args.improvement_harness_json).write_text(
        json.dumps({"formula_id": "PREDICTION_IMPROVEMENT_HARNESS_V1", "status": "OK"}),
        encoding="utf-8"
    )

    print(f"REPORT_JSON RENDERED OK: sections={len(sections)} errors={len(se)}")
    print(f"REPORT RENDERED OK: {out_md}")
    if se:
        print(f"[경고] 섹션 처리 오류 {len(se)}건:")
        for e in se:
            print(f"[SECTION_ERROR] {e['section']}: {e['error']}")
    print(f"PREDICTION_IMPROVEMENT_HARNESS_EXPORTED: {args.improvement_harness_json}")
    return 0


if __name__ == "__main__":
    import sys
    sys.exit(main())
