"""Tests for shared crossover TID utility (XOVER-01, XOVER-02).

Tests get_crossover_tid(symbol, date) -- shared module extracted from
sidecar/midnight.py so any module can look up crossover TIDs without
importing sidecar internals.
"""

from __future__ import annotations

import datetime
import logging
from unittest.mock import MagicMock, patch

# ---------------------------------------------------------------------------
# Test 1: midnight_ms computed correctly from a given date
# ---------------------------------------------------------------------------


def test_get_crossover_tid_computes_midnight_ms_from_date():
    """date(2026, 3, 25) -> midnight_ms computed from UTC midnight of that date."""
    target_date = datetime.date(2026, 3, 25)
    expected_midnight_ms = int(
        datetime.datetime(2026, 3, 25, tzinfo=datetime.UTC).timestamp() * 1000
    )

    # Mock: Tier 2 (API-max) returns a trade at midnight
    trade = {
        "agg_trade_id": 500001,
        "timestamp": expected_midnight_ms,
        "price": 42000.0,
        "quantity": 0.5,
        "first_trade_id": 500001,
        "last_trade_id": 500001,
        "is_buyer_maker": False,
    }

    with (
        patch(
            "opendeviationbar.crossover._ch_date_last_tid",
            return_value=None,
        ),
        patch(
            "opendeviationbar._core.fetch_aggtrades_rest",
            return_value=[trade],
        ) as mock_rest,
        patch(
            "opendeviationbar._core.fetch_aggtrades_by_id",
            return_value=[],
        ),
    ):
        from opendeviationbar.crossover import get_crossover_tid

        result = get_crossover_tid("BTCUSDT", date=target_date)

    assert result == 500001
    # Verify the REST call used the correct midnight_ms
    call_args = mock_rest.call_args
    assert call_args[0][1] == expected_midnight_ms  # start_time_ms
    assert call_args[0][2] == expected_midnight_ms + 3_600_000  # end_time_ms


# ---------------------------------------------------------------------------
# Test 2: default date is today
# ---------------------------------------------------------------------------


def test_get_crossover_tid_default_date_is_today():
    """get_crossover_tid(symbol) with no date defaults to today."""
    today = datetime.datetime.now(datetime.UTC).date()
    expected_midnight_ms = int(
        datetime.datetime(today.year, today.month, today.day, tzinfo=datetime.UTC).timestamp()
        * 1000
    )

    trade = {
        "agg_trade_id": 600001,
        "timestamp": expected_midnight_ms,
        "price": 42000.0,
        "quantity": 0.5,
        "first_trade_id": 600001,
        "last_trade_id": 600001,
        "is_buyer_maker": False,
    }

    with (
        patch(
            "opendeviationbar.crossover._ch_date_last_tid",
            return_value=None,
        ),
        patch(
            "opendeviationbar._core.fetch_aggtrades_rest",
            return_value=[trade],
        ) as mock_rest,
        patch(
            "opendeviationbar._core.fetch_aggtrades_by_id",
            return_value=[],
        ),
    ):
        from opendeviationbar.crossover import get_crossover_tid

        result = get_crossover_tid("BTCUSDT")

    assert result == 600001
    call_args = mock_rest.call_args
    assert call_args[0][1] == expected_midnight_ms


# ---------------------------------------------------------------------------
# Test 3: _ch_date_last_tid queries the correct date (not hardcoded yesterday)
# ---------------------------------------------------------------------------


def test_ch_date_last_tid_queries_correct_date():
    """_ch_date_last_tid(symbol, date) queries the day BEFORE the given date."""
    target_date = datetime.date(2026, 3, 25)

    mock_result = MagicMock()
    mock_result.result_rows = [[999888]]

    mock_cache = MagicMock()
    mock_cache.client.query.return_value = mock_result
    mock_cache.__enter__ = MagicMock(return_value=mock_cache)
    mock_cache.__exit__ = MagicMock(return_value=False)

    with patch(
        "opendeviationbar.clickhouse.cache.OpenDeviationBarCache",
        return_value=mock_cache,
    ):
        from opendeviationbar.crossover import _ch_date_last_tid

        result = _ch_date_last_tid("BTCUSDT", target_date)

    assert result == 999888
    # Verify the query uses the parameterized date, not yesterday()
    query_call = mock_cache.client.query.call_args
    query_str = query_call[0][0]
    # Should reference the date parameter, not yesterday()
    assert "yesterday()" not in query_str
    # Should use the date parameter
    assert "%(target_date)s" in query_str or "2026-03-25" in str(query_call)


# ---------------------------------------------------------------------------
# Test 4: _verify_crossover_from_seed returns None when seed trade is before midnight
# ---------------------------------------------------------------------------


def test_verify_crossover_from_seed_returns_none_before_midnight():
    """Seed trade timestamp < midnight -> returns None (Tier 2 fallback)."""
    midnight_ms = 1742860800000  # 2026-03-25 00:00:00 UTC

    # Trade timestamp is BEFORE midnight
    trade = {
        "agg_trade_id": 400001,
        "timestamp": midnight_ms - 1000,  # 1 second before midnight
        "price": 42000.0,
        "quantity": 0.5,
        "first_trade_id": 400001,
        "last_trade_id": 400001,
        "is_buyer_maker": False,
    }

    with patch(
        "opendeviationbar._core.fetch_aggtrades_by_id",
        return_value=[trade],
    ):
        from opendeviationbar.crossover import _verify_crossover_from_seed

        result = _verify_crossover_from_seed("BTCUSDT", 400000, midnight_ms)

    assert result is None


# ---------------------------------------------------------------------------
# Test 5: _self_validate_crossover logs WARNING on property violations
# ---------------------------------------------------------------------------


def test_self_validate_crossover_logs_warning_on_violation(caplog):
    """Validation failure logs WARNING but does not raise."""
    midnight_ms = 1742860800000

    # Both trades are AFTER midnight (property 1 violation: pred should be < midnight)
    pred = {
        "agg_trade_id": 400000,
        "timestamp": midnight_ms + 100,  # violation: pred >= midnight
        "price": 42000.0,
        "quantity": 0.5,
        "first_trade_id": 400000,
        "last_trade_id": 400000,
        "is_buyer_maker": False,
    }
    cross = {
        "agg_trade_id": 400001,
        "timestamp": midnight_ms + 200,
        "price": 42000.0,
        "quantity": 0.5,
        "first_trade_id": 400001,
        "last_trade_id": 400001,
        "is_buyer_maker": False,
    }

    with (
        patch(
            "opendeviationbar._core.fetch_aggtrades_by_id",
            return_value=[pred, cross],
        ),
        caplog.at_level(logging.WARNING, logger="opendeviationbar.crossover"),
    ):
        from opendeviationbar.crossover import _self_validate_crossover

        # Should NOT raise
        _self_validate_crossover("BTCUSDT", 400001, midnight_ms, source="test")

    assert "VALIDATION FAILED" in caplog.text
    assert "NOT yesterday" in caplog.text
