"""Test suite for base candles functionality (Trade, Candle, BaseCandles)."""

import asyncio

import pytest

from mm_toolbox.candles.base import Candle, Trade


class TestTradeStructure:
    """Test the Trade data structure."""

    def test_trade_creation(self):
        """Test Trade structure creation."""
        trade = Trade(time_ms=1640995200000, is_buy=True, price=100.50, size=1.5)

        assert trade.time_ms == 1640995200000
        assert trade.is_buy is True
        assert trade.price == 100.50
        assert trade.size == 1.5

    def test_trade_edge_values(self):
        """Test Trade with edge values."""
        # msgspec.Struct doesn't validate, so these should work
        edge_trade = Trade(time_ms=1000, is_buy=True, price=0.0, size=0.0)
        assert edge_trade.price == 0.0
        assert edge_trade.size == 0.0

        # Test with large values
        large_trade = Trade(
            time_ms=9223372036854775807, is_buy=False, price=999999.99, size=10000.0
        )
        assert large_trade.time_ms == 9223372036854775807
        assert large_trade.price == 999999.99
        assert large_trade.size == 10000.0

    def test_trade_buy_sell_types(self):
        """Test Trade with different buy/sell types."""
        buy_trade = Trade(time_ms=1000, is_buy=True, price=100.0, size=1.0)
        sell_trade = Trade(time_ms=2000, is_buy=False, price=99.0, size=2.0)

        assert buy_trade.is_buy is True
        assert sell_trade.is_buy is False


class TestCandleStructure:
    """Test the Candle data structure."""

    def test_candle_empty_creation(self):
        """Test Candle.empty() creation."""
        candle = Candle.empty()

        assert candle.open_time_ms == 0
        assert candle.close_time_ms == 0
        assert candle.open_price == 0.0
        assert candle.high_price == 0.0
        assert candle.low_price == 0.0
        assert candle.close_price == 0.0
        assert candle.buy_size == 0.0
        assert candle.buy_volume == 0.0
        assert candle.sell_size == 0.0
        assert candle.sell_volume == 0.0
        assert candle.vwap == 0.0
        assert candle.num_trades == 0
        assert candle.trades == []

    def test_candle_full_creation(self):
        """Test Candle creation with all fields."""
        candle = Candle(
            open_time_ms=1000,
            close_time_ms=2000,
            open_price=100.0,
            high_price=102.0,
            low_price=99.0,
            close_price=101.0,
            buy_size=1.0,
            buy_volume=100.0,
            sell_size=0.5,
            sell_volume=50.0,
            vwap=100.5,
            num_trades=2,
            trades=[Trade(time_ms=1000, is_buy=True, price=100.0, size=1.0)],
        )

        assert candle.open_time_ms == 1000
        assert candle.close_time_ms == 2000
        assert candle.open_price == 100.0
        assert candle.high_price == 102.0
        assert candle.low_price == 99.0
        assert candle.close_price == 101.0
        assert candle.buy_size == 1.0
        assert candle.buy_volume == 100.0
        assert candle.sell_size == 0.5
        assert candle.sell_volume == 50.0
        assert candle.vwap == 100.5
        assert candle.num_trades == 2
        assert len(candle.trades) == 1

    def test_candle_reset(self):
        """Test Candle reset functionality."""
        candle = Candle(
            open_time_ms=1000,
            close_time_ms=2000,
            open_price=100.0,
            high_price=102.0,
            low_price=99.0,
            close_price=101.0,
            buy_size=1.0,
            buy_volume=100.0,
            sell_size=0.5,
            sell_volume=50.0,
            vwap=100.5,
            num_trades=2,
            trades=[Trade(time_ms=1000, is_buy=True, price=100.0, size=1.0)],
        )

        candle.reset()

        # Should be same as empty candle after reset
        empty_candle = Candle.empty()
        assert candle.open_time_ms == empty_candle.open_time_ms
        assert candle.num_trades == empty_candle.num_trades
        assert candle.vwap == empty_candle.vwap

    def test_candle_copy(self):
        """Test Candle copy functionality."""
        original = Candle(
            open_time_ms=1000,
            close_time_ms=2000,
            open_price=100.0,
            high_price=102.0,
            low_price=99.0,
            close_price=101.0,
            buy_size=1.0,
            buy_volume=100.0,
            sell_size=0.5,
            sell_volume=50.0,
            vwap=100.5,
            num_trades=1,
            trades=[],
        )

        copied = original.copy()

        # Should be equal but different objects
        assert copied.open_price == original.open_price
        assert copied.num_trades == original.num_trades
        assert copied.vwap == original.vwap


class TestBaseCandlesFunctionality:
    """Test base candles functionality through working subclasses."""

    def setup_method(self):
        """Set up test fixtures."""
        asyncio.set_event_loop(asyncio.new_event_loop())

    def test_base_candles_initialization(self):
        """Test BaseCandles initialization through subclasses."""
        from mm_toolbox.candles import (
            PriceCandles,
            TickCandles,
            TimeCandles,
            VolumeCandles,
        )

        # Test that all working subclasses can be created (tests base class)
        tc = TickCandles(5)
        vc = VolumeCandles(1000.0)
        time_c = TimeCandles(60.0)
        pc = PriceCandles(0.01)

        assert tc is not None
        assert vc is not None
        assert time_c is not None
        assert pc is not None

    def test_vwap_calculation_through_subclasses(self):
        """Test VWAP calculation logic through subclasses."""
        from mm_toolbox.candles import TickCandles

        tick_candles = TickCandles(10)

        # Process trades with known VWAP
        trades = [
            Trade(time_ms=1000, is_buy=True, price=100.0, size=1.0),  # 100 volume
            Trade(time_ms=2000, is_buy=True, price=110.0, size=2.0),  # 220 volume
            Trade(time_ms=3000, is_buy=False, price=90.0, size=1.0),  # 90 volume
        ]

        # Expected VWAP = (100*1 + 110*2 + 90*1) / (1 + 2 + 1) = 410 / 4 = 102.5
        for trade in trades:
            tick_candles.process_trade(trade)

        # VWAP calculation happens internally - if no crash, it's working
        assert True

    def test_vwap_uses_price_weighting(self):
        """Verify VWAP uses price-weighted sizes for calculations.

        Args:
            self (TestBaseCandlesFunctionality): Test instance.

        Returns:
            None
        """
        from mm_toolbox.candles import TickCandles

        tick_candles = TickCandles(3)

        trades = [
            Trade(time_ms=1000, is_buy=True, price=100.0, size=2.0),
            Trade(time_ms=2000, is_buy=True, price=110.0, size=1.0),
        ]

        for trade in trades:
            tick_candles.process_trade(trade)

        expected_vwap = (100.0 * 2.0 + 110.0 * 1.0) / (2.0 + 1.0)
        assert tick_candles.latest_candle.vwap == pytest.approx(expected_vwap)

    def test_vwap_resets_after_candle_completion(self):
        """Verify VWAP resets when a candle is completed and restarted.

        Args:
            self (TestBaseCandlesFunctionality): Test instance.

        Returns:
            None
        """
        from mm_toolbox.candles import TickCandles

        tick_candles = TickCandles(2)

        first_trades = [
            Trade(time_ms=1000, is_buy=True, price=100.0, size=2.0),
            Trade(time_ms=2000, is_buy=False, price=200.0, size=1.0),
        ]

        for trade in first_trades:
            tick_candles.process_trade(trade)

        next_trade = Trade(time_ms=3000, is_buy=True, price=50.0, size=4.0)
        tick_candles.process_trade(next_trade)

        assert tick_candles.latest_candle.num_trades == 1
        assert tick_candles.latest_candle.vwap == pytest.approx(50.0)

    def test_stale_trade_handling(self):
        """Test stale trade handling through subclasses."""
        from mm_toolbox.candles import TimeCandles

        time_candles = TimeCandles(60.0)

        # First trade
        trade1 = Trade(time_ms=1640995200000, is_buy=True, price=100.0, size=1.0)
        time_candles.process_trade(trade1)

        # Stale trade (earlier timestamp)
        stale_trade = Trade(time_ms=1640995199000, is_buy=True, price=99.0, size=1.0)
        time_candles.process_trade(stale_trade)

        # Should handle stale trade gracefully (likely ignored)
        assert True

    def test_async_future_recreation(self):
        """Test that async Futures are properly recreated."""
        from mm_toolbox.candles import VolumeCandles

        volume_candles = VolumeCandles(100.0)  # Small volume for quick triggers

        # Process trades that will trigger multiple candle resets
        high_volume_trades = [
            Trade(time_ms=1000, is_buy=True, price=100.0, size=2.0),  # 200 volume > 100
            Trade(time_ms=2000, is_buy=True, price=101.0, size=3.0),  # 303 volume > 100
            Trade(time_ms=3000, is_buy=True, price=102.0, size=1.5),  # 153 volume > 100
        ]

        for trade in high_volume_trades:
            volume_candles.process_trade(trade)

        # Should handle multiple Future recreations without errors
        assert True


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
