# app/tests/unit/market_data/acquisition/handlers/test_quotes_handler.py

from __future__ import annotations

from datetime import timezone
from decimal import Decimal
from typing import TypeAlias

import pytest

import src.market_data.acquisition.handlers.quotes_handler as handler_module
from src.market_data.acquisition.adapters.dzengi.models import (
    DzengiTicker24hrResponse,
)
from src.market_data.acquisition.exceptions import QuoteSchemaError
from src.market_data.acquisition.handlers.quotes_handler import (
    DzengiQuoteDocumentHandler,
)
from src.market_data.acquisition.models.quote import Quote
from src.market_data.acquisition.protocol import QuoteDocumentHandler


PipelineCall: TypeAlias = (
    tuple[str, object]
    | tuple[str, object, object]
)


def _document() -> dict[str, object]:
    return {
        "symbol": "BTC/USD_LEVERAGE",
        "lastPrice": "64159.45",
        "bidPrice": "64159.45",
        "askPrice": "64159.55",
        "closeTime": 1783887270312,
        "volume": "9.6002",
    }


def test_handler_implements_quote_document_handler_protocol() -> None:
    handler = DzengiQuoteDocumentHandler()

    assert isinstance(handler, QuoteDocumentHandler)


def test_handler_returns_canonical_quote() -> None:
    before = handler_module.datetime.now(timezone.utc)

    result = DzengiQuoteDocumentHandler().handle_quote_document(
        _document()
    )

    after = handler_module.datetime.now(timezone.utc)

    assert isinstance(result, Quote)
    assert result.symbol == "BTC/USD_LEVERAGE"
    assert result.last_price == Decimal("64159.45")
    assert result.bid_price == Decimal("64159.45")
    assert result.ask_price == Decimal("64159.55")
    assert result.source == "dzengi"
    assert result.exchange_timestamp is not None
    assert result.exchange_timestamp.tzinfo is timezone.utc
    assert before <= result.received_at <= after


def test_handler_executes_pipeline_in_order(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    calls: list[PipelineCall] = []
    validated = object()
    response = DzengiTicker24hrResponse(
        symbol="BTC/USD_LEVERAGE",
        last_price="64159.45",
        bid_price="64159.45",
        ask_price="64159.55",
        close_time=1783887270312,
    )
    quote = Quote(
        symbol="BTC/USD_LEVERAGE",
        last_price=Decimal("64159.45"),
        bid_price=Decimal("64159.45"),
        ask_price=Decimal("64159.55"),
        exchange_timestamp=None,
        received_at=handler_module.datetime.now(timezone.utc),
        source="dzengi",
    )

    def validate_schema(document: object) -> object:
        calls.append(("schema", document))
        return validated

    def parse(document: object) -> DzengiTicker24hrResponse:
        calls.append(("parser", document))
        return response

    def validate_values(
        value: DzengiTicker24hrResponse,
    ) -> None:
        calls.append(("values", value))

    def map_quote(
        value: DzengiTicker24hrResponse,
        *,
        received_at: object,
    ) -> Quote:
        calls.append(
            (
                "mapper",
                value,
                received_at,
            )
        )
        return quote

    monkeypatch.setattr(
        handler_module,
        "validate_quote_schema",
        validate_schema,
    )
    monkeypatch.setattr(
        handler_module,
        "parse_quote",
        parse,
    )
    monkeypatch.setattr(
        handler_module,
        "validate_quote_values",
        validate_values,
    )
    monkeypatch.setattr(
        handler_module,
        "map_dzengi_ticker_to_quote",
        map_quote,
    )

    document = _document()
    result = (
        DzengiQuoteDocumentHandler()
        .handle_quote_document(document)
    )

    assert result is quote
    assert calls[0] == ("schema", document)
    assert calls[1] == ("parser", validated)
    assert calls[2] == ("values", response)

    mapper_call = calls[3]

    assert len(mapper_call) == 3
    assert mapper_call[0] == "mapper"
    assert mapper_call[1] is response


def test_handler_propagates_schema_error() -> None:
    with pytest.raises(QuoteSchemaError):
        DzengiQuoteDocumentHandler().handle_quote_document({})