from __future__ import annotations

from datetime import datetime, timezone
from decimal import Decimal

import pytest

from src.market_data.acquisition.adapters.dzengi.mapper import (
    map_dzengi_ticker_to_quote,
)
from src.market_data.acquisition.adapters.dzengi.models import (
    DzengiTicker24hrResponse,
)
from src.market_data.acquisition.exceptions import QuoteMappingError
from src.market_data.acquisition.models.quote import Quote


def _response(**overrides: object) -> DzengiTicker24hrResponse:
    values: dict[str, object] = {
        "symbol": "BTC/USD_LEVERAGE",
        "last_price": "64159.45",
        "bid_price": "64159.45",
        "ask_price": "64159.55",
        "close_time": 1783887270312,
    }
    values.update(overrides)
    return DzengiTicker24hrResponse(**values)  # type: ignore[arg-type]


def test_mapper_returns_canonical_quote() -> None:
    received_at = datetime(2026, 7, 12, 18, 0, tzinfo=timezone.utc)

    result = map_dzengi_ticker_to_quote(
        _response(),
        received_at=received_at,
    )

    assert result == Quote(
        symbol="BTC/USD_LEVERAGE",
        last_price=Decimal("64159.45"),
        bid_price=Decimal("64159.45"),
        ask_price=Decimal("64159.55"),
        exchange_timestamp=datetime.fromtimestamp(
            1783887270312 / 1000,
            tz=timezone.utc,
        ),
        received_at=received_at,
        source="dzengi",
    )


def test_mapper_preserves_decimal_precision() -> None:
    result = map_dzengi_ticker_to_quote(
        _response(
            last_price="0.123456789123456789",
            bid_price="0.123456789123456788",
            ask_price="0.123456789123456790",
        ),
        received_at=datetime.now(timezone.utc),
    )

    assert result.last_price == Decimal("0.123456789123456789")
    assert result.bid_price == Decimal("0.123456789123456788")
    assert result.ask_price == Decimal("0.123456789123456790")


def test_mapper_strips_symbol_outer_spaces() -> None:
    result = map_dzengi_ticker_to_quote(
        _response(symbol="  BTC/USD_LEVERAGE  "),
        received_at=datetime.now(timezone.utc),
    )

    assert result.symbol == "BTC/USD_LEVERAGE"


def test_mapper_rejects_non_finite_price() -> None:
    with pytest.raises(
        QuoteMappingError,
        match=r"lastPrice.*конечным числом",
    ):
        map_dzengi_ticker_to_quote(
            _response(last_price="NaN"),
            received_at=datetime.now(timezone.utc),
        )


def test_mapper_wraps_invalid_price_conversion() -> None:
    with pytest.raises(
        QuoteMappingError,
        match=r"bidPrice.*Decimal",
    ):
        map_dzengi_ticker_to_quote(
            _response(bid_price="not-a-number"),
            received_at=datetime.now(timezone.utc),
        )


def test_mapper_rejects_naive_received_at() -> None:
    with pytest.raises(
        QuoteMappingError,
        match=r"received_at.*timezone-aware",
    ):
        map_dzengi_ticker_to_quote(
            _response(),
            received_at=datetime(2026, 7, 12, 18, 0),
        )


def test_mapper_preserves_received_at_timezone() -> None:
    received_at = datetime.fromisoformat("2026-07-12T21:00:00+03:00")

    result = map_dzengi_ticker_to_quote(
        _response(),
        received_at=received_at,
    )

    assert result.received_at is received_at


def test_mapper_wraps_invalid_close_time() -> None:
    with pytest.raises(
        QuoteMappingError,
        match=r"closeTime.*UTC datetime",
    ):
        map_dzengi_ticker_to_quote(
            _response(close_time=10**30),
            received_at=datetime.now(timezone.utc),
        )
