
import pytest
import pandas as pd
import numpy as np
from unittest.mock import MagicMock, AsyncMock, patch
from decimal import Decimal
from src.strategies.technical.pairs_trading import PairsTradingStrategy
from src.strategies.base import SignalType

@pytest.fixture
def mock_pricing_service():
    service = MagicMock()
    service.get_ohlcv = MagicMock()
    return service

@pytest.fixture
def strategy(mock_pricing_service):
    with patch('src.strategies.technical.pairs_trading.get_pricing_service', return_value=mock_pricing_service):
        params = {
            'second_symbol': 'AVAX/USD',
            'lookback_period': 5,
            'z_score_threshold': 1.5,
            'symbol': 'SOL/USD'
        }
        strat = PairsTradingStrategy("test_pairs", params)
        strat.enabled = True
        return strat

@pytest.mark.asyncio
async def test_pairs_trading_short_spread_signal(strategy, mock_pricing_service):
    # Setup Data
    # Scenario: SOL (A) pumps relative to AVAX (B) -> Spread widens -> Z-Score High -> Sell A / Buy B
    
    # Prices for A (SOL): 100, 100, 100, 100, 120 (Pump)
    ohlcv_a = [
        [0, 100, 100, 100, 100, 1000],
        [0, 100, 100, 100, 100, 1000],
        [0, 100, 100, 100, 100, 1000],
        [0, 100, 100, 100, 100, 1000],
        [0, 120, 120, 120, 120, 1000], 
    ]
    
    # Prices for B (AVAX): 25, 25, 25, 25, 25 (Flat)
    ohlcv_b = [
        [0, 25, 25, 25, 25, 1000],
        [0, 25, 25, 25, 25, 1000],
        [0, 25, 25, 25, 25, 1000],
        [0, 25, 25, 25, 25, 1000],
        [0, 25, 25, 25, 25, 1000], 
    ]
    
    # Spread: 4, 4, 4, 4, 4.8
    # Mean: 4.16, StdDev: approx small but let's see.
    # Actually StdDev will be non-zero because of the last value.
    
    mock_pricing_service.get_ohlcv.side_effect = [ohlcv_a, ohlcv_b]
    
    # Execute
    signal = await strategy.on_tick("SOL/USD", Decimal(120), "1h", {})
    
    # Verify
    assert signal is not None
    assert signal.signal_type == SignalType.SELL # Sell Primary (SOL)
    assert signal.metadata['secondary_action'] == 'buy' # Buy Secondary (AVAX)
    assert signal.metadata['z_score'] > 1.5

@pytest.mark.asyncio
async def test_pairs_trading_long_spread_signal(strategy, mock_pricing_service):
    # Scenario: SOL (A) dumps -> Spread drops -> Z-Score Low -> Buy A / Sell B
    
    ohlcv_a = [
        [0, 100, 100, 100, 100, 1000],
        [0, 100, 100, 100, 100, 1000],
        [0, 100, 100, 100, 100, 1000],
        [0, 100, 100, 100, 100, 1000],
        [0, 80, 80, 80, 80, 1000],  # Dump
    ]
    ohlcv_b = [
        [0, 25, 25, 25, 25, 1000] for _ in range(5)
    ]
    
    mock_pricing_service.get_ohlcv.side_effect = [ohlcv_a, ohlcv_b]
    
    signal = await strategy.on_tick("SOL/USD", Decimal(80), "1h", {})
    
    assert signal is not None
    assert signal.signal_type == SignalType.BUY # Buy Primary
    assert signal.metadata['secondary_action'] == 'sell' # Sell Secondary
    assert signal.metadata['z_score'] < -1.5
