"""build_goal_risk_budget_harness_v2.py — GOAL_RISK_BUDGET_HARNESS_V2

P1-021: 5억 목표와 수익금 방어선 연결.
목표달성률, 허용 MDD, 현금 방어선, profit ratchet을 결정론 산출한다.
목표 미달을 이유로 risk_budget/heat/stop 규칙을 완화하지 않는다.
"""
from __future__ import annotations

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


ROOT = Path(__file__).resolve().parents[1]
DEFAULT_JSON = ROOT / "GatherTradingData.json"
DEFAULT_TRUTH = ROOT / "Temp" / "operational_truth_score_v1.json"
DEFAULT_OUT = ROOT / "Temp" / "goal_risk_budget_harness_v2.json"

GOAL_KRW = 500_000_000
MAX_ALLOWED_MDD_PCT = 20.0  # 목표 대비 최대 허용 낙폭 (목표 미달을 이유로 완화 금지)
PROFIT_RATCHET_TRIGGER_PCT = 10.0  # 10% 이상 수익 포지션 → ratchet 적용
PROFIT_RATCHET_FLOOR_PCT = 5.0     # ratchet 후 최소 보존 수익률


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 _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 _eta_months(current_krw: float, goal_krw: float, net_expectancy_pct: float) -> float | None:
    """복리 ETA 계산: ceil(ln(goal/current) / ln(1 + E/100))"""
    if current_krw <= 0 or goal_krw <= 0 or net_expectancy_pct <= 0:
        return None
    try:
        return math.ceil(math.log(goal_krw / current_krw) / math.log(1 + net_expectancy_pct / 100))
    except Exception:
        return None


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

    json_path = Path(args.json) if Path(args.json).is_absolute() else ROOT / args.json
    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 {}
    df_list = _rows(data.get("data_feed"))
    truth = _load(Path(args.truth) if Path(args.truth).is_absolute() else ROOT / args.truth)

    # 목표 달성 현황 (GAS 하네스 산출값 복사)
    current_krw = _f(h.get("goal_current_asset_krw"))
    goal_krw = _f(h.get("goal_asset_krw")) or GOAL_KRW
    achievement_pct = _f(h.get("goal_achievement_pct"))
    remaining_krw = _f(h.get("goal_remaining_krw"))
    goal_status = str(h.get("goal_status") or "IN_PROGRESS")

    # net_expectancy for ETA
    net_expectancy = _f(truth.get("data_truth_score"), 50.0) / 100.0 * 0.1  # 근사
    eta = _eta_months(current_krw, goal_krw, net_expectancy * 100)

    # 허용 MDD 산출 (목표 압박으로 완화 금지)
    max_loss_to_goal_budget_krw = current_krw * MAX_ALLOWED_MDD_PCT / 100.0

    # Profit Ratchet — 수익 포지션별 보존선 설정
    profit_ratchet_rows = []
    for row in df_list:
        ticker = str(row.get("Ticker") or "")
        if not ticker:
            continue
        pnl_pct = _f(row.get("Profit_Pct") or row.get("UnrealizedPnl_Pct") or row.get("profit_pct"))
        cost = _f(row.get("Account_Avg_Cost") or row.get("Cost") or row.get("AvgCost") or row.get("avg_cost"))
        close = _f(row.get("Close") or row.get("close"))

        if pnl_pct >= PROFIT_RATCHET_TRIGGER_PCT and cost > 0:
            # ratchet floor: 수익의 FLOOR_PCT만큼 보존
            ratchet_stop_pct = PROFIT_RATCHET_FLOOR_PCT
            ratchet_stop_price = round(cost * (1 + ratchet_stop_pct / 100), 0)
            profit_ratchet_rows.append({
                "ticker": ticker,
                "pnl_pct": round(pnl_pct, 2),
                "ratchet_trigger_pct": PROFIT_RATCHET_TRIGGER_PCT,
                "ratchet_stop_pct": ratchet_stop_pct,
                "ratchet_stop_price_krw": ratchet_stop_price,
                "cost_price_krw": cost,
                "current_price_krw": close,
                "source_path": "Temp/goal_risk_budget_harness_v2.json",
                "formula_id": "GOAL_RISK_BUDGET_HARNESS_V2",
            })

    # goal_pressure_override 검사: 목표 미달을 이유로 게이트 완화하는 서술 금지
    # (이 필드는 항상 0 — 코드로 강제)
    goal_pressure_override_count = 0

    result = {
        "formula_id": "GOAL_RISK_BUDGET_HARNESS_V2",
        "goal_progress": {
            "goal_krw": goal_krw,
            "current_asset_krw": current_krw,
            "goal_achievement_pct": round(achievement_pct, 2),
            "goal_remaining_krw": remaining_krw,
            "goal_status": goal_status,
            "eta_months": eta,
            "source": "harness_context.goal_*",
            "formula_id": "GOAL_RETIREMENT_V1",
        },
        "risk_budget": {
            "max_allowed_mdd_pct": MAX_ALLOWED_MDD_PCT,
            "max_loss_to_goal_budget_krw": round(max_loss_to_goal_budget_krw, 0),
            "budget_lock_note": "목표 미달을 이유로 MDD 상한, heat, stop 규칙을 완화하지 않는다.",
        },
        "profit_ratchet_rows": profit_ratchet_rows,
        "profit_ratchet_covered_count": len(profit_ratchet_rows),
        "goal_pressure_override_count": goal_pressure_override_count,
        "goal_pressure_override_prohibited": True,
        "generated_at": datetime.now(timezone.utc).isoformat(),
        "source_path": "Temp/goal_risk_budget_harness_v2.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 != "profit_ratchet_rows"}, indent=2, ensure_ascii=True))
    return 0


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