"""Unit Tests for Feature Engineering & Look-Ahead Leakage Prevention."""
from __future__ import annotations

import unittest
from decimal import Decimal

from backend.app.features.indicators import (
    calculate_atr,
    calculate_bollinger_bands,
    calculate_ema,
    calculate_macd,
    calculate_rsi,
    calculate_sma,
)
from backend.app.features.pipeline import FeaturePipeline
from backend.app.features.regime import RegimeClassifier
from backend.app.market_data.mock_client import MockBybitClient
from backend.app.schemas.signal import MarketRegime


class TestFeaturePipeline(unittest.TestCase):
    def setUp(self) -> None:
        self.mock_client = MockBybitClient()
        self.candles = self.mock_client.get_klines("BTCUSDT", "15m", 100)
        self.pipeline = FeaturePipeline()
        self.regime_classifier = RegimeClassifier()

    def test_zero_lookahead_leakage_invariance(self) -> None:
        """Prove that future candles do NOT alter features computed on past history."""
        eval_bar = 50
        is_causal = self.pipeline.verify_zero_lookahead(self.candles, eval_bar)
        self.assertTrue(is_causal, "Feature computation breached causality! Look-ahead leakage detected.")

    def test_causal_indicators_calculation(self) -> None:
        """Verify standard technical indicators compute valid non-NaN outputs on sufficient history."""
        closes = [float(c.close) for c in self.candles]
        sma = calculate_sma(closes, 20)
        ema = calculate_ema(closes, 21)
        rsi = calculate_rsi(closes, 14)
        macd, signal, hist = calculate_macd(closes, 12, 26, 9)
        atr = calculate_atr(self.candles, 14)
        mid, upper, lower = calculate_bollinger_bands(closes, 20, 2.0)

        self.assertFalse(any(v is None for v in closes))
        self.assertGreater(rsi[-1], 0.0)
        self.assertLess(rsi[-1], 100.0)
        self.assertGreater(atr[-1], 0.0)
        self.assertGreater(upper[-1], lower[-1])

    def test_market_regime_classification(self) -> None:
        """Verify regime classifier segregates trending vs ranging vs liquidity stress."""
        # 1. Trending Bull test
        prices = [100.0 + i * 2.0 for i in range(40)]
        f_ema = [p - 1.0 for p in prices]
        s_ema = [p - 3.0 for p in prices]
        atr = [1.0] * 40
        regime = self.regime_classifier.classify(prices, f_ema, s_ema, atr)
        self.assertEqual(regime, MarketRegime.TRENDING_BULL)

        # 2. Liquidity stress test (wide spread > 0.15%)
        regime_stress = self.regime_classifier.classify(prices, f_ema, s_ema, atr, spread_pct=0.0020)
        self.assertEqual(regime_stress, MarketRegime.LIQUIDITY_STRESS)


if __name__ == "__main__":
    unittest.main()
