"""核心数据模型。参考 hummingbot strategy_v2 的 data_types 设计。"""
from __future__ import annotations

import time
import uuid
from dataclasses import dataclass, field
from enum import Enum
from typing import Optional


class Side(str, Enum):
    BUY = "BUY"
    SELL = "SELL"

    @property
    def opposite(self) -> "Side":
        return Side.SELL if self is Side.BUY else Side.BUY


class OrderType(str, Enum):
    MARKET = "MARKET"
    LIMIT = "LIMIT"


class OrderState(str, Enum):
    OPEN = "OPEN"
    FILLED = "FILLED"
    CANCELED = "CANCELED"
    FAILED = "FAILED"


class CloseType(str, Enum):
    """平仓原因，对应 hummingbot 的 CloseType。"""
    TAKE_PROFIT = "TAKE_PROFIT"
    STOP_LOSS = "STOP_LOSS"
    TRAILING_STOP = "TRAILING_STOP"
    TIME_LIMIT = "TIME_LIMIT"
    BOUNDARY_BREACH = "BOUNDARY_BREACH"  # 网格越界（冻结边界被突破）
    SIGNAL = "SIGNAL"
    FORCED = "FORCED"


@dataclass
class Order:
    pair: str
    side: Side
    order_type: OrderType
    amount: float          # base 数量
    price: Optional[float]  # LIMIT 必填；MARKET 为 None
    leverage: float = 1.0
    reduce_only: bool = False
    client_id: str = field(default_factory=lambda: uuid.uuid4().hex[:16])
    state: OrderState = OrderState.OPEN
    filled_amount: float = 0.0
    filled_price: float = 0.0
    fee_quote: float = 0.0
    created_ts: float = field(default_factory=time.time)
    filled_ts: Optional[float] = None

    @property
    def is_open(self) -> bool:
        return self.state is OrderState.OPEN

    def mark_filled(self, price: float, fee_quote: float, ts: float) -> None:
        self.state = OrderState.FILLED
        self.filled_amount = self.amount
        self.filled_price = price
        self.fee_quote = fee_quote
        self.filled_ts = ts


@dataclass
class Position:
    """聚合持仓（永续单向）。executor 内部可再分层记账。"""
    pair: str
    side: Side
    amount: float = 0.0          # base
    entry_price: float = 0.0     # 加权均价
    leverage: float = 1.0
    realized_pnl: float = 0.0
    fees_paid: float = 0.0

    @property
    def margin_quote(self) -> float:
        return self.amount * self.entry_price / max(self.leverage, 1e-9)

    def unrealized_pnl(self, mark_price: float) -> float:
        if self.amount <= 0:
            return 0.0
        diff = mark_price - self.entry_price
        if self.side is Side.SELL:
            diff = -diff
        return diff * self.amount

    def add(self, price: float, amount: float) -> None:
        new_amt = self.amount + amount
        if new_amt <= 0:
            return
        self.entry_price = (self.entry_price * self.amount + price * amount) / new_amt
        self.amount = new_amt

    def reduce(self, price: float, amount: float) -> float:
        """减仓，返回该笔已实现盈亏（不含费）。"""
        amount = min(amount, self.amount)
        diff = price - self.entry_price
        if self.side is Side.SELL:
            diff = -diff
        pnl = diff * amount
        self.amount -= amount
        self.realized_pnl += pnl
        if self.amount <= 1e-12:
            self.amount = 0.0
            self.entry_price = 0.0
        return pnl


@dataclass
class TradeRecord:
    """一笔完整开平（或一层网格的往返），用于回测统计。"""
    pair: str
    side: Side
    entry_price: float
    exit_price: float
    amount: float
    pnl_quote: float          # 净额（已扣费）
    fees_quote: float
    close_type: CloseType
    entry_ts: float
    exit_ts: float
    tag: str = ""

    @property
    def is_win(self) -> bool:
        return self.pnl_quote > 0
