"""
Trade分析モジュール

約定（Trade Print）イベントを分析し、
Buy/Sellボリューム、Order Flow Imbalance（OFI）、
アグレッサー比率等の特徴量を計算する。

スキャルピング戦略やRecurrent PPOモデルの入力として
Order Flow分析に必要な指標を提供する。
"""

import logging
from collections import deque
from dataclasses import dataclass, field
from typing import Optional, List

from ..models import TradeEvent, Side

logger = logging.getLogger(__name__)

# numpyはオプション依存
try:
    import numpy as np
    HAS_NUMPY = True
except ImportError:
    HAS_NUMPY = False


@dataclass
class TradeStats:
    """直近の取引統計情報"""
    # 取引数
    total_trades: int = 0
    # Buy (bid aggressor) ボリューム
    buy_volume: int = 0
    # Sell (ask aggressor) ボリューム
    sell_volume: int = 0
    # 合計ボリューム
    total_volume: int = 0
    # VWAP (Volume Weighted Average Price)
    vwap: float = 0.0
    # Order Flow Imbalance (OFI)
    ofi: float = 0.0
    # アグレッサー比率 (買い成行の割合)
    aggressor_ratio: float = 0.5
    # 最終約定価格
    last_price: float = 0.0
    # 最大約定価格
    high_price: float = 0.0
    # 最小約定価格
    low_price: float = 0.0


class TradeAnalyzer:
    """
    約定（Trade Print）分析クラス。

    直近Nティックの取引履歴を保持し、
    Order Flow分析に必要な特徴量を計算する。
    """

    def __init__(self, window_size: int = 100):
        """
        Args:
            window_size: 分析ウィンドウサイズ（保持するトレード数）
        """
        self.window_size = window_size
        # 直近の取引履歴（FIFO）
        self._trades: deque[TradeEvent] = deque(maxlen=window_size)
        # 累積統計
        self._total_trades = 0

    def process_event(self, event: TradeEvent) -> None:
        """
        Tradeイベントを処理する。

        Args:
            event: TradeEvent
        """
        self._trades.append(event)
        self._total_trades += 1

    def get_stats(self) -> TradeStats:
        """
        直近ウィンドウ内の取引統計を計算する。

        Returns:
            TradeStats: 統計情報
        """
        if not self._trades:
            return TradeStats()

        buy_volume = 0
        sell_volume = 0
        total_volume = 0
        price_volume_sum = 0.0
        high_price = float("-inf")
        low_price = float("inf")

        for trade in self._trades:
            volume = trade.size
            total_volume += volume
            price_volume_sum += trade.price * volume

            if trade.aggressor_side == Side.BID:
                # 買い成行（Buy Aggressor） = テイカーが買い
                buy_volume += volume
            else:
                # 売り成行（Sell Aggressor） = テイカーが売り
                sell_volume += volume

            if trade.price > high_price:
                high_price = trade.price
            if trade.price < low_price:
                low_price = trade.price

        # VWAP計算
        vwap = price_volume_sum / total_volume if total_volume > 0 else 0.0

        # Order Flow Imbalance (OFI)
        # 正: 買い圧力優勢、負: 売り圧力優勢
        ofi = (buy_volume - sell_volume) / total_volume if total_volume > 0 else 0.0

        # アグレッサー比率（買い成行の割合）
        aggressor_ratio = buy_volume / total_volume if total_volume > 0 else 0.5

        return TradeStats(
            total_trades=len(self._trades),
            buy_volume=buy_volume,
            sell_volume=sell_volume,
            total_volume=total_volume,
            vwap=vwap,
            ofi=ofi,
            aggressor_ratio=aggressor_ratio,
            last_price=self._trades[-1].price,
            high_price=high_price if high_price != float("-inf") else 0.0,
            low_price=low_price if low_price != float("inf") else 0.0,
        )

    def get_recent_trades(self, n: int = 10) -> List[TradeEvent]:
        """直近N件の取引を取得する"""
        trades = list(self._trades)
        return trades[-n:] if len(trades) >= n else trades

    def get_buy_sell_ratio(self, n: Optional[int] = None) -> float:
        """
        Buy/Sell比率を計算する。

        Args:
            n: 直近N件で計算 (Noneの場合は全ウィンドウ)

        Returns:
            float: 買いボリューム / 売りボリューム（売りが0の場合はinf）
        """
        trades = list(self._trades)
        if n is not None:
            trades = trades[-n:]

        buy_vol = sum(
            t.size for t in trades if t.aggressor_side == Side.BID
        )
        sell_vol = sum(
            t.size for t in trades if t.aggressor_side == Side.ASK
        )

        if sell_vol == 0:
            return float("inf") if buy_vol > 0 else 1.0
        return buy_vol / sell_vol

    def get_cumulative_delta(self, n: Optional[int] = None) -> int:
        """
        Cumulative Delta（累積デルタ）を計算する。

        Buy Volume - Sell Volumeの累積値。
        正の方向への推移は買い圧力の増加を示す。

        Args:
            n: 直近N件で計算 (Noneの場合は全ウィンドウ)

        Returns:
            int: 累積デルタ値
        """
        trades = list(self._trades)
        if n is not None:
            trades = trades[-n:]

        delta = 0
        for trade in trades:
            if trade.aggressor_side == Side.BID:
                delta += trade.size
            else:
                delta -= trade.size

        return delta

    def get_trade_rate(self) -> float:
        """
        取引レート（1秒あたりの取引数）を計算する。

        Returns:
            float: 取引レート
        """
        if len(self._trades) < 2:
            return 0.0

        time_span = self._trades[-1].timestamp - self._trades[0].timestamp
        if time_span <= 0:
            return 0.0

        return len(self._trades) / time_span

    def to_numpy(self) -> Optional["np.ndarray"]:
        """
        取引統計をNumpy配列に変換する。

        配列形式:
            [buy_volume, sell_volume, total_volume,
             ofi, aggressor_ratio, cumulative_delta,
             vwap, last_price, trade_rate, trade_count]

        Returns:
            np.ndarray: (10,) 形状の配列、
                        numpyが未インストールの場合はNone
        """
        if not HAS_NUMPY:
            logger.warning("numpyが利用できません")
            return None

        stats = self.get_stats()
        cum_delta = self.get_cumulative_delta()
        trade_rate = self.get_trade_rate()

        return np.array([
            float(stats.buy_volume),
            float(stats.sell_volume),
            float(stats.total_volume),
            stats.ofi,
            stats.aggressor_ratio,
            float(cum_delta),
            stats.vwap,
            stats.last_price,
            trade_rate,
            float(stats.total_trades),
        ], dtype=np.float32)

    def clear(self) -> None:
        """取引履歴をクリアする"""
        self._trades.clear()
        self._total_trades = 0

    @property
    def trade_count(self) -> int:
        """現在のウィンドウ内の取引数"""
        return len(self._trades)

    @property
    def total_trades_processed(self) -> int:
        """累計処理済み取引数"""
        return self._total_trades

    def __repr__(self) -> str:
        stats = self.get_stats()
        return (
            f"TradeAnalyzer(window={self.window_size}, "
            f"trades={self.trade_count}, "
            f"buy_vol={stats.buy_volume}, "
            f"sell_vol={stats.sell_volume}, "
            f"ofi={stats.ofi:.3f})"
        )
