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" / "value_preservation_scorer_v1.json"


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


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


def _obj(v: Any) -> dict[str, Any]:
    if isinstance(v, dict):
        return v
    if isinstance(v, str):
        try:
            x = json.loads(v)
            return x if isinstance(x, dict) else {}
        except Exception:
            return {}
    return {}


def _f(v: Any) -> float:
    try:
        return float(v)
    except Exception:
        return 0.0


def _bound(v: float, lo: float = 0.0, hi: float = 100.0) -> float:
    return max(lo, min(hi, v))


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

    jp = Path(args.json)
    op = Path(args.out)
    if not jp.is_absolute():
        jp = ROOT / jp
    if not op.is_absolute():
        op = ROOT / op

    payload = _load(jp)
    data = payload.get("data") if isinstance(payload.get("data"), dict) else {}
    h = data.get("_harness_context") if isinstance(data.get("_harness_context"), dict) else {}
    if isinstance(payload.get("hApex"), dict):
        h = dict(h) | payload["hApex"]

    scrs = _obj(h.get("scrs_v2_json"))
    rows = _rows(scrs.get("selected_combo"))
    prices = {str(r.get("ticker") or ""): r for r in _rows(h.get("prices_json"))}

    scored = []
    raw_damages: list[float] = []
    null_limit_price_count = 0
    MIN_SAMPLES = 30

    for r in rows:
        t = str(r.get("ticker") or "")
        p = prices.get(t, {})
        atr = _f(p.get("atr20") or p.get("ATR20"))
        prev_close = _f(p.get("prev_close") or p.get("prevClose"))
        current = _f(p.get("current_price") or p.get("current_price_krw"))
        adv20 = _f(p.get("adv20") or p.get("avg_trade_value_20d") or 0.0)

        # [VD1] raw_value_damage_pct — adjusted 마스킹 전 원본값
        damage_pct = _f(r.get("value_damage_pct"))
        raw_damages.append(damage_pct)

        price_stress = 0.0 if atr <= 0 else abs(current - prev_close) / atr * 10.0
        value_damage_score = round(_bound(damage_pct * 4.0 + price_stress), 2)
        rebound_potential = round(_bound(100.0 - value_damage_score + (_f(r.get("rebound_wait_qty")) > 0) * 10.0), 2)
        if rebound_potential >= 70:
            action = "WAIT_REBOUND"
        elif rebound_potential >= 45:
            action = "SPLIT_REBOUND"
        else:
            action = "EXECUTE_NOW"

        # [VD1] hts_limit_price null 감지 (비상 외 매도에서 null이면 설거지 위험)
        hts_lp = r.get("hts_limit_price")
        emergency = bool(r.get("emergency_full_sell") or r.get("emergency"))
        if hts_lp is None and not emergency:
            null_limit_price_count += 1

        # [VD1] participation_rate = qty / adv20 (5% 초과면 TWAP 권고)
        qty = _f(r.get("immediate_sell_qty") or r.get("qty") or 0)
        participation_rate = round(qty / adv20, 4) if adv20 > 0 and current > 0 else None

        scored.append(
            {
                "ticker": t,
                "name": r.get("name"),
                "value_damage_pct_raw": round(damage_pct, 2),
                "value_damage_score": value_damage_score,
                "rebound_potential": rebound_potential,
                "recommended_action": action,
                "hts_limit_price": hts_lp,
                "hts_limit_price_null": hts_lp is None,
                "emergency_full_sell": emergency,
                "participation_rate": participation_rate,
                "twap_recommended": participation_rate is not None and participation_rate > 0.05,
            }
        )

    # [VD1] raw_value_damage_pct_avg 기준 게이트
    raw_avg = round(sum(raw_damages) / len(raw_damages), 2) if raw_damages else 0.0
    n = len(scored)

    if n == 0:
        gate = "CAUTION"
        gate_reason = "NO_ROWS"
    elif n < MIN_SAMPLES:
        # [SG1] n<30 → 공허PASS 금지
        gate = "WATCH_PENDING_SAMPLE"
        gate_reason = f"INSUFFICIENT_SAMPLES(n={n}<{MIN_SAMPLES})"
    elif raw_avg > 10.0:
        # [VD1] raw 손상 10% 초과 → BLOCK
        gate = "BLOCK"
        gate_reason = f"VALUE_DAMAGE_GT_10(raw_avg={raw_avg}%)"
    else:
        gate = "PASS"
        gate_reason = f"OK(raw_avg={raw_avg}%,n={n})"

    distinct_actions = len({str(r.get("recommended_action") or "") for r in scored})
    out = {
        "formula_id": "VALUE_PRESERVATION_SCORER_V1",
        "gate": gate,
        "gate_reason": gate_reason,
        # [VD1] raw 기준 집계 — adjusted 마스킹 금지
        "raw_value_damage_pct_avg": raw_avg,
        "value_damage_gt_10": raw_avg > 10.0,
        "hts_limit_price_null_count_non_emergency": null_limit_price_count,
        "min_samples": MIN_SAMPLES,
        "rows": scored,
        "row_count": n,
        "distinct_actions": distinct_actions,
    }
    op.parent.mkdir(parents=True, exist_ok=True)
    op.write_text(json.dumps(out, ensure_ascii=False, indent=2), encoding="utf-8")
    print(json.dumps(out, ensure_ascii=False, indent=2))
    return 0


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