"""
TradeAnalyzerクラスのユニットテスト

Buy/Sell Volume集計、OFI計算、Cumulative Delta計算のテスト。
"""

import pytest

from rithmic_bridge.models import TradeEvent, TradeAction, Side
from rithmic_bridge.client.trade_analyzer import TradeAnalyzer


def make_trade(
    trade_id: str,
    aggressor_side: str,
    price: float,
    size: int,
    timestamp: float = 1000.0,
) -> TradeEvent:
    """テスト用TradeEvent生成ヘルパー"""
    return TradeEvent(
        symbol="GCJ6.COMEX",
        action=TradeAction.EXECUTE,
        trade_id=trade_id,
        aggressor_side=aggressor_side,
        price=price,
        size=size,
        timestamp=timestamp,
    )


class TestTradeAnalyzerBasic:
    """TradeAnalyzerの基本機能テスト"""

    def setup_method(self):
        self.analyzer = TradeAnalyzer(window_size=100)

    def test_empty_stats(self):
        """空の状態で統計が正しいこと"""
        stats = self.analyzer.get_stats()
        assert stats.total_trades == 0
        assert stats.buy_volume == 0
        assert stats.sell_volume == 0
        assert stats.ofi == 0.0

    def test_single_buy_trade(self):
        """単一の買い約定が正しく記録されること"""
        self.analyzer.process_event(
            make_trade("1", Side.BID, 2500.0, 5)
        )
        stats = self.analyzer.get_stats()
        assert stats.total_trades == 1
        assert stats.buy_volume == 5
        assert stats.sell_volume == 0
        assert stats.ofi == 1.0  # 完全に買い優勢
        assert stats.aggressor_ratio == 1.0

    def test_single_sell_trade(self):
        """単一の売り約定が正しく記録されること"""
        self.analyzer.process_event(
            make_trade("1", Side.ASK, 2500.0, 3)
        )
        stats = self.analyzer.get_stats()
        assert stats.buy_volume == 0
        assert stats.sell_volume == 3
        assert stats.ofi == -1.0  # 完全に売り優勢
        assert stats.aggressor_ratio == 0.0

    def test_mixed_trades(self):
        """複数の買い/売り約定が正しく集約されること"""
        self.analyzer.process_event(make_trade("1", Side.BID, 2500.0, 5))
        self.analyzer.process_event(make_trade("2", Side.ASK, 2500.0, 3))
        self.analyzer.process_event(make_trade("3", Side.BID, 2500.1, 2))

        stats = self.analyzer.get_stats()
        assert stats.total_trades == 3
        assert stats.buy_volume == 7   # 5 + 2
        assert stats.sell_volume == 3
        assert stats.total_volume == 10
        assert stats.ofi == pytest.approx(0.4, abs=0.01)  # (7-3)/10

    def test_vwap(self):
        """VWAPが正しく計算されること"""
        self.analyzer.process_event(make_trade("1", Side.BID, 2500.0, 10))
        self.analyzer.process_event(make_trade("2", Side.ASK, 2501.0, 10))

        stats = self.analyzer.get_stats()
        expected_vwap = (2500.0 * 10 + 2501.0 * 10) / 20
        assert stats.vwap == pytest.approx(expected_vwap, abs=0.01)

    def test_last_price(self):
        """最終約定価格が正しいこと"""
        self.analyzer.process_event(make_trade("1", Side.BID, 2500.0, 5))
        self.analyzer.process_event(make_trade("2", Side.ASK, 2501.5, 3))

        stats = self.analyzer.get_stats()
        assert stats.last_price == 2501.5


class TestTradeAnalyzerAdvanced:
    """TradeAnalyzerの高度な機能テスト"""

    def test_cumulative_delta(self):
        """Cumulative Deltaが正しく計算されること"""
        analyzer = TradeAnalyzer(window_size=100)
        analyzer.process_event(make_trade("1", Side.BID, 2500.0, 5))  # +5
        analyzer.process_event(make_trade("2", Side.ASK, 2500.0, 3))  # -3
        analyzer.process_event(make_trade("3", Side.BID, 2500.0, 2))  # +2

        delta = analyzer.get_cumulative_delta()
        assert delta == 4  # 5 - 3 + 2

    def test_cumulative_delta_recent(self):
        """直近N件のCumulative Deltaが正しいこと"""
        analyzer = TradeAnalyzer(window_size=100)
        analyzer.process_event(make_trade("1", Side.BID, 2500.0, 10))
        analyzer.process_event(make_trade("2", Side.ASK, 2500.0, 3))
        analyzer.process_event(make_trade("3", Side.BID, 2500.0, 2))

        # 直近2件 (-3 + 2 = -1)
        delta = analyzer.get_cumulative_delta(n=2)
        assert delta == -1

    def test_buy_sell_ratio(self):
        """Buy/Sell比率が正しいこと"""
        analyzer = TradeAnalyzer(window_size=100)
        analyzer.process_event(make_trade("1", Side.BID, 2500.0, 10))
        analyzer.process_event(make_trade("2", Side.ASK, 2500.0, 5))

        ratio = analyzer.get_buy_sell_ratio()
        assert ratio == 2.0  # 10 / 5

    def test_buy_sell_ratio_no_sells(self):
        """売りが0の場合のBuy/Sell比率"""
        analyzer = TradeAnalyzer(window_size=100)
        analyzer.process_event(make_trade("1", Side.BID, 2500.0, 5))

        ratio = analyzer.get_buy_sell_ratio()
        assert ratio == float("inf")

    def test_window_sliding(self):
        """ウィンドウサイズを超えた場合に古い約定が削除されること"""
        analyzer = TradeAnalyzer(window_size=3)

        analyzer.process_event(make_trade("1", Side.BID, 2500.0, 10))
        analyzer.process_event(make_trade("2", Side.BID, 2500.0, 20))
        analyzer.process_event(make_trade("3", Side.BID, 2500.0, 30))
        # ウィンドウ満杯 (10, 20, 30)

        analyzer.process_event(make_trade("4", Side.ASK, 2500.0, 5))
        # ウィンドウ: (20, 30, 5) - 最初の10が削除

        stats = analyzer.get_stats()
        assert stats.total_trades == 3  # ウィンドウサイズ
        assert stats.buy_volume == 50   # 20 + 30
        assert stats.sell_volume == 5

    def test_trade_rate(self):
        """取引レートが正しく計算されること"""
        analyzer = TradeAnalyzer(window_size=100)
        analyzer.process_event(make_trade("1", Side.BID, 2500.0, 5, timestamp=1000.0))
        analyzer.process_event(make_trade("2", Side.BID, 2500.0, 5, timestamp=1001.0))
        analyzer.process_event(make_trade("3", Side.BID, 2500.0, 5, timestamp=1002.0))

        rate = analyzer.get_trade_rate()
        assert rate == pytest.approx(1.5, abs=0.1)  # 3取引 / 2秒

    def test_clear(self):
        """クリアが正しく動作すること"""
        analyzer = TradeAnalyzer(window_size=100)
        analyzer.process_event(make_trade("1", Side.BID, 2500.0, 5))
        analyzer.clear()

        assert analyzer.trade_count == 0
        assert analyzer.total_trades_processed == 0

    def test_high_low_price(self):
        """最高値・最安値が正しいこと"""
        analyzer = TradeAnalyzer(window_size=100)
        analyzer.process_event(make_trade("1", Side.BID, 2500.0, 5))
        analyzer.process_event(make_trade("2", Side.ASK, 2502.0, 3))
        analyzer.process_event(make_trade("3", Side.BID, 2498.0, 2))

        stats = analyzer.get_stats()
        assert stats.high_price == 2502.0
        assert stats.low_price == 2498.0


class TestTradeAnalyzerNumpy:
    """TradeAnalyzerのNumpy変換テスト"""

    def test_to_numpy(self):
        """to_numpyが正しい形状の配列を返すこと"""
        try:
            import numpy as np
        except ImportError:
            pytest.skip("numpyが利用不可")

        analyzer = TradeAnalyzer(window_size=100)
        analyzer.process_event(make_trade("1", Side.BID, 2500.0, 5))

        arr = analyzer.to_numpy()
        assert arr is not None
        assert arr.shape == (10,)
        assert arr.dtype == np.float32
        # buy_volume=5がインデックス0に入っていること
        assert arr[0] == 5.0
