# app/tests/unit/integrations/exchange/test_service_execution_quote.py

from __future__ import annotations

from datetime import datetime, timedelta, timezone
from decimal import Decimal
from types import SimpleNamespace
from typing import cast

import pytest

from src.core.config import Settings
import src.integrations.exchange.service as service_module
from src.integrations.exchange.service import ExchangeService
from src.market_data.acquisition.models.quote import Quote
from src.storage.quote_store import QUOTE_RUNTIME_SOURCE_NAME


def _service() -> ExchangeService:
    service = object.__new__(ExchangeService)
    service.settings = cast(
        Settings,
        SimpleNamespace(
            exchange_enabled=True,
            default_symbol="BTC/USD_LEVERAGE",
            tz="Europe/Minsk",
        ),
    )
    return service


def _quote(*, received_at: datetime | None = None) -> Quote:
    return Quote(
        symbol="BTC/USD_LEVERAGE",
        last_price=Decimal("100.5"),
        bid_price=Decimal("100"),
        ask_price=Decimal("101"),
        exchange_timestamp=datetime(2026, 7, 13, 12, 0, tzinfo=timezone.utc),
        received_at=received_at or datetime.now(timezone.utc),
        source="dzengi",
    )


def _valid_validation() -> SimpleNamespace:
    return SimpleNamespace(
        is_valid=True,
        normalized_symbol="BTC/USD_LEVERAGE",
        message="ok",
    )


def test_execution_snapshot_reads_canonical_cached_quote(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    service = _service()
    quote = _quote()

    monkeypatch.setattr(service, "validate_symbol", lambda _: _valid_validation())
    class Store:
        def get(self, source_name, symbol, *, runtime_key="default"):
            assert source_name == QUOTE_RUNTIME_SOURCE_NAME
            del symbol, runtime_key
            return quote

    monkeypatch.setattr(service_module, "get_quote_store", lambda: Store())
    monkeypatch.setattr(
        service,
        "_get_fresh_quote",
        lambda _: (_ for _ in ()).throw(
            AssertionError("REST fallback must not run for fresh quote")
        ),
    )

    result = service.get_execution_snapshot(
        "BTC/USD_LEVERAGE",
        runtime_key="auto",
    )

    assert result.symbol == quote.symbol
    assert result.last_price == 100.5
    assert result.bid_price == 100.0
    assert result.ask_price == 101.0
    assert result.source == "dzengi:fresh_cache"
    assert result.is_fresh is True
    assert result.age_seconds is not None


def test_execution_snapshot_uses_fresh_quote_for_stale_cache(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    service = _service()
    stale = _quote(
        received_at=datetime.now(timezone.utc) - timedelta(seconds=10),
    )
    fresh = _quote()
    stored: list[tuple[Quote, str]] = []

    monkeypatch.setattr(service, "validate_symbol", lambda _: _valid_validation())
    class Store:
        def get(self, source_name, symbol, *, runtime_key="default"):
            assert source_name == QUOTE_RUNTIME_SOURCE_NAME
            del symbol, runtime_key
            return stale

        def set(self, source_name, quote, *, runtime_key="default"):
            assert source_name == QUOTE_RUNTIME_SOURCE_NAME
            stored.append((quote, runtime_key))

    monkeypatch.setattr(service_module, "get_quote_store", lambda: Store())
    monkeypatch.setattr(service, "_get_fresh_quote", lambda _: fresh)

    result = service.get_execution_snapshot(
        "BTC/USD_LEVERAGE",
        runtime_key="auto",
    )

    assert stored == [(fresh, "auto")]
    assert result.source == "rest_fallback"
    assert result.last_price == 100.5
    assert result.is_fresh is True
