"""build_outcome_ledger_v1.py — OUTCOME_LEDGER_V1

P1-018: 매수/매도/보유/현금확보 품질을 분리 평가하는 성과 원장.
각 decision_id에 intended_action, executed_action, forward_return을 연결한다.
BUY/SELL/TRIM/WATCH별 성과를 분리하고, profit_giveback을 정직 추적한다.
operational T+20=0 구간에서는 T+5 기반 근사값과 DATA_MISSING_PENDING_T20을 정직 표기한다.
"""
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]
DEFAULT_HIST = ROOT / "Temp" / "proposal_evaluation_history.json"
DEFAULT_LEDGER = ROOT / "Temp" / "operational_t20_outcome_ledger_v1.json"
DEFAULT_SCR = ROOT / "Temp" / "smart_cash_recovery_v5.json"
DEFAULT_OUT = ROOT / "Temp" / "outcome_ledger_v1.json"


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


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


def _classify_action(action: str) -> str:
    a = action.upper()
    if any(x in a for x in ("BUY", "ADD_ON", "PILOT")):
        return "BUY"
    if any(x in a for x in ("SELL", "EXIT", "BREACH", "STOP")):
        return "SELL"
    if any(x in a for x in ("TRIM", "REDUCE")):
        return "TRIM"
    if any(x in a for x in ("WATCH", "HOLD", "NEUTRAL")):
        return "WATCH"
    return "OTHER"


def main() -> int:
    ap = argparse.ArgumentParser()
    ap.add_argument("--hist", default=str(DEFAULT_HIST))
    ap.add_argument("--ledger", default=str(DEFAULT_LEDGER))
    ap.add_argument("--scr", default=str(DEFAULT_SCR))
    ap.add_argument("--out", default=str(DEFAULT_OUT))
    args = ap.parse_args()

    hist = _load(Path(args.hist) if Path(args.hist).is_absolute() else ROOT / args.hist)
    ledger = _load(Path(args.ledger) if Path(args.ledger).is_absolute() else ROOT / args.ledger)
    scr = _load(Path(args.scr) if Path(args.scr).is_absolute() else ROOT / args.scr)

    records = hist.get("records") if isinstance(hist.get("records"), list) else []

    # 액션 타입별 분리
    buckets: dict[str, list[dict[str, Any]]] = {"BUY": [], "SELL": [], "TRIM": [], "WATCH": [], "OTHER": []}

    for rec in records:
        if not isinstance(rec, dict):
            continue
        action = str(rec.get("action") or rec.get("recommended_action") or "WATCH")
        bucket = _classify_action(action)

        # forward return 연결 (T+5 operational 우선, T+20 replay 차선)
        t5_return = rec.get("t5_return_pct")
        t20_return = rec.get("t20_return_pct")
        validation = str(rec.get("validation_status") or "")
        is_operational = validation.upper() != "REPLAY_BACKFILL"

        forward_return = None
        return_source = "DATA_MISSING_PENDING_T20"
        if is_operational and t5_return is not None:
            forward_return = _f(t5_return)
            return_source = "T5_OPERATIONAL"
        elif t20_return is not None:
            forward_return = _f(t20_return)
            return_source = "T20_REPLAY" if not is_operational else "T20_OPERATIONAL"

        buckets[bucket].append({
            "proposal_id": str(rec.get("proposal_id") or ""),
            "ticker": str(rec.get("ticker") or ""),
            "proposal_date": str(rec.get("proposal_date") or ""),
            "action": action,
            "action_bucket": bucket,
            "forward_return_pct": forward_return if forward_return is not None else "DATA_MISSING_PENDING_T20",
            "return_source": return_source,
            "t5_outcome": rec.get("t5_outcome"),
            "t20_outcome": rec.get("t20_outcome"),
            "is_operational": is_operational,
            "source_path": "Temp/outcome_ledger_v1.json",
            "formula_id": "OUTCOME_LEDGER_V1",
        })

    # 버킷별 성과 요약
    def _summarize(rows: list[dict[str, Any]]) -> dict[str, Any]:
        op_rows = [r for r in rows if r["is_operational"] and r["forward_return_pct"] != "DATA_MISSING_PENDING_T20"]
        if not op_rows:
            return {
                "sample_count": 0,
                "win_rate_pct": "DATA_MISSING_PENDING_T20",
                "avg_return_pct": "DATA_MISSING_PENDING_T20",
                "expectancy_pct": "DATA_MISSING_PENDING_T20",
            }
        returns = [_f(r["forward_return_pct"]) for r in op_rows]
        wins = [r for r in returns if r > 0]
        return {
            "sample_count": len(op_rows),
            "win_rate_pct": round(len(wins) / len(op_rows) * 100, 2),
            "avg_return_pct": round(sum(returns) / len(returns), 4),
            "expectancy_pct": round(sum(returns) / len(op_rows), 4),
        }

    buy_summary = _summarize(buckets["BUY"])
    sell_summary = _summarize(buckets["SELL"])
    trim_summary = _summarize(buckets["TRIM"])

    # profit_giveback 계산 (T+20 데이터 없으면 DATA_MISSING)
    sell_data = [r for r in buckets["SELL"] if r["is_operational"] and r["forward_return_pct"] != "DATA_MISSING_PENDING_T20"]
    if sell_data:
        negative_returns = [_f(r["forward_return_pct"]) for r in sell_data if _f(r["forward_return_pct"]) < 0]
        total_returns = [abs(_f(r["forward_return_pct"])) for r in sell_data if _f(r["forward_return_pct"]) != 0]
        profit_giveback = round(sum(negative_returns) / max(sum(total_returns), 1) * 100, 2) if total_returns else 0.0
    else:
        profit_giveback = "DATA_MISSING_PENDING_T20"

    # value_damage from smart cash recovery
    cash_raise_value_damage = _f(scr.get("value_damage_pct_avg_raw"), 0.0) if scr else 0.0

    result = {
        "formula_id": "OUTCOME_LEDGER_V1",
        "total_records": len(records),
        "bucket_counts": {k: len(v) for k, v in buckets.items()},
        "buy_performance": buy_summary,
        "sell_performance": sell_summary,
        "trim_performance": trim_summary,
        "profit_giveback_pct": profit_giveback,
        "cash_raise_value_damage_pct": cash_raise_value_damage,
        "reward_model": {
            "buy_expectancy_pct": buy_summary.get("expectancy_pct", "DATA_MISSING_PENDING_T20"),
            "sell_saved_loss_pct": "DATA_MISSING_PENDING_T20",
            "cash_raise_value_damage_pct": cash_raise_value_damage,
            "profit_giveback_pct": profit_giveback,
            "win_rate_pct": buy_summary.get("win_rate_pct", "DATA_MISSING_PENDING_T20"),
        },
        "pending_note": "operational T+20=0 구간 — T+5 기반 BUY/SELL/TRIM 집계. profit_giveback은 T+20 수신 후 갱신.",
        "generated_at": datetime.now(timezone.utc).isoformat(),
        "source_path": "Temp/outcome_ledger_v1.json",
    }

    out_path = Path(args.out) if Path(args.out).is_absolute() else ROOT / args.out
    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({k: v for k, v in result.items() if k not in ("buy_performance", "sell_performance", "trim_performance")}, indent=2, ensure_ascii=True))
    return 0


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