"""
실거래 매수 사이징 — 시뮬(sim_tier_enhanced)과 동일 인과 tier·weight 정책.
"""

from __future__ import annotations

import json
from pathlib import Path
from typing import Any

import pandas as pd

from config import (
    GT_SIGNAL_CAUSAL,
    TRADING_FEE_RATE,
)
from deepcoin.ground_truth.causal_gt_hybrid import (
    _attach_drawdown_to_buys,
    _bar_index_at,
    _close_series_from_df,
    _drawdown_pct_at_index,
    hybrid_tier_scale,
)
from deepcoin.ground_truth.gt_model import leg_entry_weights, remaining_weight_sum
from deepcoin.matching.position_sizing import compute_buy_amount_krw
from deepcoin.paths import OPS_STATE_DIR

LIVE_SIZING_STATE_JSON = OPS_STATE_DIR / "live_sizing_state.json"


class LivePositionState:
    """
    미청산 leg·과거 leg 수익·매수 weight 추적 (시뮬 enrich/causal tier 정합).
    """

    def __init__(self) -> None:
        """빈 포지션 상태."""
        self.current_leg_id: int = 0
        self.open_buys: list[dict[str, Any]] = []
        self.completed_leg_ret: dict[int, float] = {}
        self.leg_cost_krw: float = 0.0
        self.leg_proceeds_krw: float = 0.0

    @classmethod
    def load(cls, path: Path | None = None) -> LivePositionState:
        """
        디스크에서 상태 복원.

        Args:
            path: JSON 경로. None이면 기본 경로.

        Returns:
            LivePositionState 인스턴스.
        """
        p = path or LIVE_SIZING_STATE_JSON
        st = cls()
        if not p.is_file():
            return st
        try:
            data = json.loads(p.read_text(encoding="utf-8"))
        except (json.JSONDecodeError, OSError):
            return st
        st.current_leg_id = int(data.get("current_leg_id") or 0)
        st.open_buys = list(data.get("open_buys") or [])
        st.completed_leg_ret = {
            int(k): float(v) for k, v in (data.get("completed_leg_ret") or {}).items()
        }
        st.leg_cost_krw = float(data.get("leg_cost_krw") or 0.0)
        st.leg_proceeds_krw = float(data.get("leg_proceeds_krw") or 0.0)
        return st

    def save(self, path: Path | None = None) -> None:
        """
        상태를 디스크에 저장.

        Args:
            path: JSON 경로. None이면 기본 경로.
        """
        p = path or LIVE_SIZING_STATE_JSON
        p.parent.mkdir(parents=True, exist_ok=True)
        payload = {
            "current_leg_id": self.current_leg_id,
            "open_buys": self.open_buys,
            "completed_leg_ret": self.completed_leg_ret,
            "leg_cost_krw": round(self.leg_cost_krw, 0),
            "leg_proceeds_krw": round(self.leg_proceeds_krw, 0),
        }
        p.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")

    def _start_new_leg_if_needed(self) -> None:
        """포지션 없을 때 새 leg 시작."""
        if not self.open_buys:
            self.current_leg_id += 1
            self.leg_cost_krw = 0.0
            self.leg_proceeds_krw = 0.0

    def record_buy(self, dt: str, price: float, amount_krw: float, fee: float) -> None:
        """
        체결 매수 기록.

        Args:
            dt: 체결 시각.
            price: 체결가.
            amount_krw: 매수 원화.
            fee: 수수료.
        """
        self._start_new_leg_if_needed()
        self.open_buys.append({"dt": dt, "price": price, "amount_krw": amount_krw})
        self.leg_cost_krw += amount_krw + fee

    def record_sell(self, amount_krw: float, fee: float, *, full_close: bool) -> None:
        """
        체결 매도 기록.

        Args:
            amount_krw: 매도 원화(총액).
            fee: 수수료.
            full_close: leg 전량 청산 여부.
        """
        net = amount_krw - fee
        self.leg_proceeds_krw += net
        if full_close and self.leg_cost_krw > 0:
            ret_pct = (self.leg_proceeds_krw - self.leg_cost_krw) / self.leg_cost_krw * 100.0
            self.completed_leg_ret[self.current_leg_id] = ret_pct
            self.open_buys = []
            self.leg_cost_krw = 0.0
            self.leg_proceeds_krw = 0.0

    def plan_buy_amount_krw(
        self,
        dt: str,
        price: float,
        cash: float,
        qty: float,
        df: pd.DataFrame | None = None,
        *,
        enhanced: bool = True,
        fee_rate: float = TRADING_FEE_RATE,
    ) -> float:
        """
        시뮬과 동일 tier·weight로 매수 원화 산출.

        Args:
            dt: 신호 시각.
            price: 종가.
            cash: 가용 원화.
            qty: 보유 수량.
            df: OHLC (drawdown).
            enhanced: conviction·medium tier 사용.
            fee_rate: 수수료율.

        Returns:
            매수 원화.
        """
        self._start_new_leg_if_needed()
        prices = [float(b["price"]) for b in self.open_buys] + [price]
        weights = leg_entry_weights(prices)
        idx = len(self.open_buys)
        weight = float(weights[idx])
        w_sum = float(sum(weights[idx:]))
        trade: dict[str, Any] = {
            "dt": dt,
            "action": "buy",
            "price": price,
            "leg_id": self.current_leg_id,
            "weight": round(weight, 4),
        }
        if df is not None and not df.empty:
            attached = _attach_drawdown_to_buys([trade], df)
            if attached:
                trade = attached[0]
        from deepcoin.ground_truth.hybrid_dd_calibrate import load_hybrid_dd_params

        dd_params = load_hybrid_dd_params()
        scale = hybrid_tier_scale(
            trade,
            completed_leg_ret=self.completed_leg_ret,
            enhanced=enhanced,
            dd_large_pct=dd_params.get("dd_large_pct"),
            dd_medium_pct=dd_params.get("dd_medium_pct"),
        )
        return compute_buy_amount_krw(
            cash,
            qty,
            price,
            weight,
            w_sum,
            asset_pct_scale=scale,
            fee_rate=fee_rate,
            ignore_weight_split=bool(trade.get("conviction_buy")),
        )


def drawdown_pct_from_df(df: pd.DataFrame, dt: str) -> float:
    """
    bar 시점 drawdown % (인과적).

    Args:
        df: DatetimeIndex OHLC.
        dt: 시각 문자열.

    Returns:
        drawdown %.
    """
    if df.empty:
        return 0.0
    close_s = _close_series_from_df(df)
    bar_idx = _bar_index_at(df, dt)
    return _drawdown_pct_at_index(close_s, bar_idx)


def live_sizing_enabled() -> bool:
    """실거래 사이징을 시뮬 인과 tier와 정합할지."""
    return bool(GT_SIGNAL_CAUSAL)
