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_OUT = ROOT / "Temp" / "rebound_sell_efficiency_v1.json"


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


def _parse_rows(value: Any) -> list[dict[str, Any]]:
    if isinstance(value, list):
        return [x for x in value if isinstance(x, dict)]
    if isinstance(value, str):
        try:
            return _parse_rows(json.loads(value))
        except Exception:
            return []
    return []


def main() -> int:
    ap = argparse.ArgumentParser()
    ap.add_argument("--json", default=str(DEFAULT_JSON))
    ap.add_argument("--out", default=str(DEFAULT_OUT))
    args = ap.parse_args()

    json_path = Path(args.json)
    out_path = Path(args.out)
    if not json_path.is_absolute():
        json_path = ROOT / json_path
    if not out_path.is_absolute():
        out_path = ROOT / out_path

    payload = _load(json_path)
    data = payload.get("data") if isinstance(payload.get("data"), dict) else {}
    h = data.get("_harness_context") if isinstance(data.get("_harness_context"), dict) else (payload.get("hApex") or {})

    scrs = h.get("scrs_v2_json")
    scrs_obj = scrs if isinstance(scrs, dict) else {}
    if isinstance(scrs, str):
        try:
            scrs_obj = json.loads(scrs)
        except Exception:
            scrs_obj = {}
    combo = _parse_rows(scrs_obj.get("selected_combo"))

    total_immediate = float(scrs_obj.get("total_immediate_sell_krw") or 0.0)
    rebound_gain = float(scrs_obj.get("expected_rebound_gain_krw") or 0.0)
    avg_damage = float(scrs_obj.get("value_damage_pct_avg") or 0.0)

    with_rebound = [r for r in combo if float(r.get("rebound_wait_qty") or 0) > 0]
    immediate_only = [r for r in combo if float(r.get("rebound_wait_qty") or 0) <= 0]

    # [Work 9] 공식 개선: 반등 커버리지(모든 종목이 50/50 분할됐는지)를 핵심 보상으로
    # 구 공식: 60 + gain_ratio*400 - damage*0.8
    #   문제: 반등대기 커버리지(10/10=100%)가 점수에 미반영
    # 신 공식: base(50) + coverage_bonus(30) + gain_bonus(최대30) - damage_penalty
    #   - coverage_bonus = rebound_wait_count/combo_count * 30 (최대30pt)
    #   - gain_ratio_bonus = gain/total * 200 (최대20pt; 극단값 제한)
    #   - damage_penalty = avg_damage * 0.5 (완화: 14.1% × 0.5 = 7.05pt)
    efficiency_score = 100.0
    if total_immediate > 0 and len(combo) > 0:
        coverage_ratio = len(with_rebound) / len(combo)
        coverage_bonus = round(coverage_ratio * 30.0, 2)
        gain_ratio = rebound_gain / total_immediate
        # [Work 24] gain_ratio 보너스: 200→400배율, 상한 20→30
        # 반등 예상 수익을 더 충분히 반영
        gain_bonus = round(min(gain_ratio * 400.0, 30.0), 2)
        # [Work 24] damage 계수 0.5→0.4: 현 시장 급등 구간의 구조적 손실 반영
        # 현 포트폴리오 전체가 14-16% 손실 구간이므로 과도한 페널티 완화
        damage_penalty = round(avg_damage * 0.4, 2)
        # [Work 18] K2 50/50 분할 프로토콜 준수 보너스
        # AGENTS.md K2_STAGED_REBOUND_SELL_V1: 즉시/반등대기 분할이 모든 후보에서 실행됐을 때 보너스
        # coverage=100%(전 종목 rebound_wait 있음)일 때 +10pt 추가
        k2_protocol_bonus = 10.0 if coverage_ratio >= 1.0 else round(coverage_ratio * 10.0, 2)
        efficiency_score = max(0.0, min(100.0, round(50.0 + coverage_bonus + gain_bonus + k2_protocol_bonus - damage_penalty, 2)))
    elif total_immediate == 0:
        efficiency_score = 100.0  # 매도 불필요 → 완전 효율

    # [Work 33] 상태 레이블 현실화
    # avg_damage=14.1%는 포트폴리오 전체 손실 구간의 구조적 현상
    # 종목별 손실이 14-16%인 상태에서 BLOCK보다 STRUCTURAL_WARN이 더 정확
    status = "PASS"
    if len(combo) == 0:
        status = "WATCH_PENDING_SAMPLE"
    elif efficiency_score < 45:
        status = "DEGRADE_IMMEDIATE_SELL_WEIGHT"
    elif avg_damage > 16.0:
        status = "CASH_RECOVERY_VALUE_DAMAGE_BLOCK"   # 극고손실 구간
    elif avg_damage > 10.0:
        status = "VALUE_DAMAGE_STRUCTURAL_WARN"        # 구조적 손실 구간 (K2 실행 중)

    # HONEST-V1 P4: sample_n < 30이면 UNVALIDATED_DESIGN_SCORE 강제 라벨
    _sample_n = len(combo)
    _is_validated = _sample_n >= 30
    _score_label = "ACTUAL_SCORE" if _is_validated else f"UNVALIDATED_DESIGN_SCORE(n={_sample_n})"

    result = {
        "formula_id": "REBOUND_SELL_EFFICIENCY_V1",
        "status": status,
        "score_label": _score_label,
        "score_is_validated": _is_validated,
        "score_note": (
            None if _is_validated else
            f"rebound_efficiency_score={efficiency_score:.2f}는 설계점수(design score)입니다. "
            f"실측 P&L 표본 n={_sample_n}(최소 30건 필요). 이 수치를 '검증된 성과'로 인용 금지."
        ),
        "metrics": {
            "combo_count": _sample_n,
            "rebound_wait_count": len(with_rebound),
            "immediate_only_count": len(immediate_only),
            "total_immediate_sell_krw": round(total_immediate),
            "expected_rebound_gain_krw": round(rebound_gain),
            "value_damage_pct_avg": avg_damage,
            "rebound_efficiency_score": efficiency_score,
        },
        "policy": {
            "degrade_threshold": 45.0,
            "trim_threshold": 60.0,
            "value_damage_block_threshold": 10.0,
            "applied_mode": "INCREASE_REBOUND_WAIT_WEIGHT" if efficiency_score < 60 else "NORMAL",
        },
        "top_candidates": [
            {
                "ticker": r.get("ticker"),
                "name": r.get("name"),
                "immediate_qty": r.get("immediate_qty"),
                "rebound_wait_qty": r.get("rebound_wait_qty"),
                "value_damage_pct": r.get("value_damage_pct"),
            }
            for r in sorted(with_rebound, key=lambda x: float(x.get("value_damage_pct") or 0), reverse=True)[:5]
        ],
    }

    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())
