# app/tests/unit/market_data/acquisition/validation/test_schema.py

from __future__ import annotations

from types import MappingProxyType

import pytest

from src.market_data.acquisition.exceptions import (
    InstrumentReferenceSchemaError,
)
from src.market_data.acquisition.validation.schema import (
    validate_exchange_info_schema,
)


def test_validate_unwrapped_exchange_info_document() -> None:
    document = {
        "timezone": "UTC",
        "serverTime": 1783537921471,
        "rateLimits": [],
        "exchangeFilters": [],
        "symbols": [
            {
                "symbol": "BTC/USD_LEVERAGE",
                "filters": [
                    {
                        "filterType": "LOT_SIZE",
                        "minQty": "0.0001",
                        "maxQty": "1000",
                        "stepSize": "0.0001",
                    }
                ],
                "marketModes": ["REGULAR"],
                "orderTypes": ["LIMIT", "MARKET", "STOP"],
            }
        ],
    }

    validated = validate_exchange_info_schema(document)

    assert validated.is_wrapped is False
    assert validated.status is None
    assert validated.correlation_id is None
    assert validated.payload["symbols"] == document["symbols"]
    assert isinstance(validated.payload, MappingProxyType)


def test_validate_wrapped_exchange_info_document() -> None:
    document = {
        "status": "OK",
        "correlationId": "2",
        "payload": {
            "timezone": "UTC",
            "serverTime": 1628193845310,
            "rateLimits": [],
            "exchangeFilters": [],
            "symbols": [],
        },
    }

    validated = validate_exchange_info_schema(document)

    assert validated.is_wrapped is True
    assert validated.status == "OK"
    assert validated.correlation_id == "2"
    assert validated.payload["symbols"] == []


@pytest.mark.parametrize(
    "document",
    [
        None,
        [],
        "invalid",
        123,
    ],
)
def test_reject_non_object_root(document: object) -> None:
    with pytest.raises(
        InstrumentReferenceSchemaError,
        match=r"\$ должен быть JSON-объектом",
    ):
        validate_exchange_info_schema(document)


def test_reject_non_object_wrapped_payload() -> None:
    document = {
        "status": "OK",
        "payload": [],
    }

    with pytest.raises(
        InstrumentReferenceSchemaError,
        match=r"\$\.payload должен быть JSON-объектом",
    ):
        validate_exchange_info_schema(document)


def test_reject_missing_symbols() -> None:
    document = {
        "timezone": "UTC",
        "serverTime": 1783537921471,
    }

    with pytest.raises(
        InstrumentReferenceSchemaError,
        match=r"\$\.payload\.symbols должен быть JSON-массивом",
    ):
        validate_exchange_info_schema(document)


def test_reject_non_list_symbols() -> None:
    document = {
        "symbols": {},
    }

    with pytest.raises(
        InstrumentReferenceSchemaError,
        match=r"\$\.payload\.symbols должен быть JSON-массивом",
    ):
        validate_exchange_info_schema(document)


def test_reject_non_object_symbol_item() -> None:
    document = {
        "symbols": [
            "BTC/USD",
        ],
    }

    with pytest.raises(
        InstrumentReferenceSchemaError,
        match=r"\$\.payload\.symbols\[0\] должен быть JSON-объектом",
    ):
        validate_exchange_info_schema(document)


def test_reject_non_list_filters() -> None:
    document = {
        "symbols": [
            {
                "symbol": "BTC/USD",
                "filters": {},
            }
        ],
    }

    with pytest.raises(
        InstrumentReferenceSchemaError,
        match=r"\.filters должен быть JSON-массивом",
    ):
        validate_exchange_info_schema(document)


def test_reject_non_object_filter_item() -> None:
    document = {
        "symbols": [
            {
                "symbol": "BTC/USD",
                "filters": [
                    "LOT_SIZE",
                ],
            }
        ],
    }

    with pytest.raises(
        InstrumentReferenceSchemaError,
        match=r"\.filters\[0\] должен быть JSON-объектом",
    ):
        validate_exchange_info_schema(document)


@pytest.mark.parametrize(
    ("key", "invalid_value"),
    [
        ("marketModes", {}),
        ("orderTypes", "MARKET"),
    ],
)
def test_reject_non_list_string_collections(
    key: str,
    invalid_value: object,
) -> None:
    document = {
        "symbols": [
            {
                "symbol": "BTC/USD",
                key: invalid_value,
            }
        ],
    }

    with pytest.raises(
        InstrumentReferenceSchemaError,
        match=rf"\.{key} должен быть JSON-массивом",
    ):
        validate_exchange_info_schema(document)


@pytest.mark.parametrize(
    "key",
    [
        "marketModes",
        "orderTypes",
    ],
)
def test_reject_non_string_collection_item(key: str) -> None:
    document = {
        "symbols": [
            {
                "symbol": "BTC/USD",
                key: [
                    "REGULAR",
                    123,
                ],
            }
        ],
    }

    with pytest.raises(
        InstrumentReferenceSchemaError,
        match=rf"\.{key}\[1\] должен быть строкой",
    ):
        validate_exchange_info_schema(document)


@pytest.mark.parametrize(
    "key",
    [
        "rateLimits",
        "exchangeFilters",
    ],
)
def test_reject_non_list_payload_collections(key: str) -> None:
    document = {
        "symbols": [],
        key: {},
    }

    with pytest.raises(
        InstrumentReferenceSchemaError,
        match=rf"\.{key} должен быть JSON-массивом",
    ):
        validate_exchange_info_schema(document)


@pytest.mark.parametrize(
    "key",
    [
        "rateLimits",
        "exchangeFilters",
    ],
)
def test_reject_non_object_payload_collection_item(key: str) -> None:
    document = {
        "symbols": [],
        key: [
            "invalid",
        ],
    }

    with pytest.raises(
        InstrumentReferenceSchemaError,
        match=rf"\.{key}\[0\] должен быть JSON-объектом",
    ):
        validate_exchange_info_schema(document)