"""Tests for data layer: preprocessing and normalization."""

from __future__ import annotations

import sys
import os
import pytest

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from data.preprocessing import (
    normalize_candle,
    normalize_candles,
    remove_duplicates,
    fill_missing_candles,
    validate_price_range,
    resample_candles,
)
from core.trend_analysis import Candle


class TestNormalizeCandle:
    def test_valid_raw_candle(self):
        raw = {
            "timestamp": 1000000,
            "Open": "50000.00000000",
            "High": "50100.50000000",
            "Low": "49900.25000000",
            "Close": "50050.75000000",
            "Volume": "1.5",
        }
        candle = normalize_candle(raw)
        assert candle is not None
        assert candle.open == 50000.0
        assert candle.high == 50100.5
        assert candle.low == 49900.25
        assert candle.close == 50050.75

    def test_invalid_zero_price(self):
        raw = {
            "timestamp": 1000000,
            "Open": "0",
            "High": "50100.5",
            "Low": "49900.25",
            "Close": "50050.75",
            "Volume": "1.5",
        }
        candle = normalize_candle(raw)
        assert candle is None

    def test_invalid_high_below_low(self):
        raw = {
            "timestamp": 1000000,
            "Open": "50000",
            "High": "49900",
            "Low": "50100",
            "Close": "50050",
            "Volume": "1.5",
        }
        candle = normalize_candle(raw)
        assert candle is None

    def test_invalid_high_below_close(self):
        raw = {
            "timestamp": 1000000,
            "Open": "50000",
            "High": "49999",
            "Low": "49900",
            "Close": "50050",
            "Volume": "1.5",
        }
        candle = normalize_candle(raw)
        assert candle is None

    def test_missing_fields(self):
        raw = {"Open": "50000"}
        candle = normalize_candle(raw)
        assert candle is None


class TestNormalizeCandles:
    def test_multiple_raw_candles(self):
        raw_data = [
            {"timestamp": 2, "Open": "100", "High": "102", "Low": "98", "Close": "101", "Volume": "100"},
            {"timestamp": 1, "Open": "98", "High": "101", "Low": "97", "Close": "100", "Volume": "200"},
            {"timestamp": 3, "Open": "101", "High": "103", "Low": "100", "Close": "102", "Volume": "150"},
        ]
        candles = normalize_candles(raw_data)
        assert len(candles) == 3
        # Should be sorted by timestamp ascending
        assert candles[0].timestamp == 1
        assert candles[1].timestamp == 2
        assert candles[2].timestamp == 3

    def test_filters_invalid(self):
        raw_data = [
            {"timestamp": 1, "Open": "100", "High": "102", "Low": "98", "Close": "101", "Volume": "100"},
            {"timestamp": 2, "Open": "0", "High": "0", "Low": "0", "Close": "0", "Volume": "0"},
        ]
        candles = normalize_candles(raw_data)
        assert len(candles) == 1


class TestRemoveDuplicates:
    def test_removes_duplicate_timestamps(self):
        candles = [
            Candle(timestamp=1, open=100, high=102, low=98, close=101, volume=100),
            Candle(timestamp=1, open=101, high=103, low=99, close=102, volume=150),
            Candle(timestamp=2, open=102, high=104, low=100, close=103, volume=200),
        ]
        result = remove_duplicates(candles)
        assert len(result) == 2
        assert result[0].close == 102  # Last one kept for timestamp=1

    def test_no_duplicates(self):
        candles = [
            Candle(timestamp=1, open=100, high=102, low=98, close=101, volume=100),
            Candle(timestamp=2, open=102, high=104, low=100, close=103, volume=200),
        ]
        result = remove_duplicates(candles)
        assert len(result) == 2


class TestFillMissingCandles:
    def test_detects_gaps(self):
        candles = [
            Candle(timestamp=100, open=100, high=102, low=98, close=101, volume=100),
            Candle(timestamp=400, open=102, high=104, low=100, close=103, volume=200),
        ]
        result = fill_missing_candles(candles, expected_interval=100, max_gap=10)
        assert len(result) == 2  # Doesn't actually fill, just logs

    def test_no_gaps(self):
        candles = [
            Candle(timestamp=100, open=100, high=102, low=98, close=101, volume=100),
            Candle(timestamp=200, open=102, high=104, low=100, close=103, volume=200),
        ]
        result = fill_missing_candles(candles, expected_interval=100)
        assert len(result) == 2


class TestValidatePriceRange:
    def test_filters_outside_range(self):
        candles = [
            Candle(timestamp=1, open=100, high=102, low=98, close=101, volume=100),
            Candle(timestamp=2, open=999999, high=1000000, low=999998, close=999999, volume=1),
        ]
        result = validate_price_range(candles, max_price=200000)
        assert len(result) == 1

    def test_empty_list(self):
        result = validate_price_range([])
        assert result == []


class TestResampleCandles:
    def test_resample_m5_to_m15(self):
        candles = []
        base_ts = 1000000
        for i in range(9):
            ts = base_ts + i * 300  # 5-min intervals
            candles.append(Candle(
                timestamp=ts,
                open=100 + i * 0.5,
                high=101 + i * 0.5,
                low=99 + i * 0.5,
                close=100.5 + i * 0.5,
                volume=100,
            ))
        result = resample_candles(candles, 300, 900)
        assert len(result) == 3
        assert result[0].open == 100
        assert result[0].high == 102.0  # max of 101, 101.5, 102

    def test_target_smaller_than_source(self):
        candles = [
            Candle(timestamp=100, open=100, high=102, low=98, close=101, volume=100),
        ]
        result = resample_candles(candles, 3600, 1800)
        assert len(result) == 1  # Returns as-is

    def test_empty_input(self):
        result = resample_candles([], 300, 900)
        assert result == []