# app/tests/unit/market_data/acquisition/test_symbols.py

from __future__ import annotations

import pytest

from src.market_data.acquisition.symbols import (
    normalize_symbol,
    resolve_symbol_index,
    symbol_candidates,
)


@pytest.mark.parametrize(
    ("raw_symbol", "expected"),
    [
        (
            "BTC/USD",
            "BTC/USD",
        ),
        (
            "btc/usd",
            "BTC/USD",
        ),
        (
            "  btc/usd  ",
            "BTC/USD",
        ),
        (
            "",
            "",
        ),
        (
            "   ",
            "",
        ),
        (
            "btc / usd",
            "BTC / USD",
        ),
        (
            "btc%2fusd",
            "BTC%2FUSD",
        ),
        (
            "eth/usd_leverage",
            "ETH/USD_LEVERAGE",
        ),
    ],
)
def test_normalize_symbol_preserves_existing_contract(
    raw_symbol: str,
    expected: str,
) -> None:
    assert normalize_symbol(raw_symbol) == expected


def test_normalize_symbol_does_not_decode_encoded_separator() -> None:
    result = normalize_symbol(
        "btc%2fusd"
    )

    assert result == "BTC%2FUSD"


def test_normalize_symbol_does_not_remove_internal_spaces() -> None:
    result = normalize_symbol(
        " btc / usd "
    )

    assert result == "BTC / USD"


def test_normalize_symbol_does_not_add_leverage_suffix() -> None:
    result = normalize_symbol(
        "btc/usd"
    )

    assert result == "BTC/USD"


def test_normalize_symbol_preserves_existing_leverage_suffix() -> None:
    result = normalize_symbol(
        "btc/usd_leverage"
    )

    assert result == "BTC/USD_LEVERAGE"


@pytest.mark.parametrize(
    "raw_symbol",
    [
        "",
        " ",
        "   ",
        "\t",
        "\n",
    ],
)
def test_symbol_candidates_returns_empty_list_for_empty_value(
    raw_symbol: str,
) -> None:
    assert symbol_candidates(raw_symbol) == []


def test_symbol_candidates_returns_single_normalized_candidate() -> None:
    result = symbol_candidates(
        " btc/usd "
    )

    assert result == [
        "BTC/USD",
    ]


def test_symbol_candidates_adds_decoded_separator_candidate() -> None:
    result = symbol_candidates(
        "btc%2fusd"
    )

    assert result == [
        "BTC%2FUSD",
        "BTC/USD",
    ]


def test_symbol_candidates_adds_no_spaces_candidate() -> None:
    result = symbol_candidates(
        "btc / usd"
    )

    assert result == [
        "BTC / USD",
        "BTC/USD",
    ]


def test_symbol_candidates_preserves_transformation_order() -> None:
    result = symbol_candidates(
        " btc%2f / usd "
    )

    assert result == [
        "BTC%2F / USD",
        "BTC/ / USD",
        "BTC//USD",
    ]


def test_symbol_candidates_does_not_add_duplicate_after_separator_decode() -> None:
    result = symbol_candidates(
        "btc/usd"
    )

    assert result == [
        "BTC/USD",
    ]


def test_symbol_candidates_does_not_add_duplicate_after_space_removal() -> None:
    result = symbol_candidates(
        "btc%2fusd"
    )

    assert result == [
        "BTC%2FUSD",
        "BTC/USD",
    ]


def test_symbol_candidates_returns_new_list_for_each_call() -> None:
    first = symbol_candidates(
        "btc/usd"
    )
    second = symbol_candidates(
        "btc/usd"
    )

    assert first == second
    assert first is not second


def test_symbol_candidates_does_not_modify_source_string() -> None:
    raw_symbol = " btc%2f / usd "

    symbol_candidates(raw_symbol)

    assert raw_symbol == " btc%2f / usd "


def test_symbol_candidates_does_not_remove_internal_tab() -> None:
    result = symbol_candidates(
        "btc\t/usd"
    )

    assert result == [
        "BTC\t/USD",
    ]


def test_symbol_candidates_does_not_remove_internal_newline() -> None:
    result = symbol_candidates(
        "btc\n/usd"
    )

    assert result == [
        "BTC\n/USD",
    ]


def test_symbol_candidates_preserves_leverage_suffix() -> None:
    result = symbol_candidates(
        " btc / usd_leverage "
    )

    assert result == [
        "BTC / USD_LEVERAGE",
        "BTC/USD_LEVERAGE",
    ]


def test_symbol_candidates_returns_list() -> None:
    result = symbol_candidates(
        "btc/usd"
    )

    assert isinstance(result, list)


def test_resolve_symbol_index_finds_exact_match() -> None:
    result = resolve_symbol_index(
        "BTC/USD_LEVERAGE",
        (
            "ETH/USD_LEVERAGE",
            "BTC/USD_LEVERAGE",
        ),
    )

    assert result == 1


def test_resolve_symbol_index_is_case_insensitive() -> None:
    result = resolve_symbol_index(
        "btc/usd_leverage",
        (
            "BTC/USD_LEVERAGE",
        ),
    )

    assert result == 0


def test_resolve_symbol_index_ignores_outer_spaces() -> None:
    result = resolve_symbol_index(
        "  btc/usd_leverage  ",
        (
            "BTC/USD_LEVERAGE",
        ),
    )

    assert result == 0


def test_resolve_symbol_index_supports_encoded_separator() -> None:
    result = resolve_symbol_index(
        "btc%2fusd_leverage",
        (
            "BTC/USD_LEVERAGE",
        ),
    )

    assert result == 0


def test_resolve_symbol_index_supports_internal_spaces() -> None:
    result = resolve_symbol_index(
        "btc / usd_leverage",
        (
            "BTC/USD_LEVERAGE",
        ),
    )

    assert result == 0


def test_resolve_symbol_index_returns_none_for_missing_symbol() -> None:
    result = resolve_symbol_index(
        "XRP/USD_LEVERAGE",
        (
            "BTC/USD_LEVERAGE",
            "ETH/USD_LEVERAGE",
        ),
    )

    assert result is None


def test_resolve_symbol_index_returns_none_for_empty_request() -> None:
    result = resolve_symbol_index(
        "   ",
        (
            "BTC/USD_LEVERAGE",
        ),
    )

    assert result is None


def test_resolve_symbol_index_returns_none_for_empty_available_symbols() -> None:
    result = resolve_symbol_index(
        "BTC/USD_LEVERAGE",
        (),
    )

    assert result is None


def test_resolve_symbol_index_preserves_candidate_priority() -> None:
    result = resolve_symbol_index(
        "BTC%2FUSD_LEVERAGE",
        (
            "BTC/USD_LEVERAGE",
            "BTC%2FUSD_LEVERAGE",
        ),
    )

    assert result == 1


def test_resolve_symbol_index_preserves_available_symbol_order() -> None:
    result = resolve_symbol_index(
        "BTC/USD_LEVERAGE",
        (
            "btc/usd_leverage",
            "BTC/USD_LEVERAGE",
        ),
    )

    assert result == 0


def test_resolve_symbol_index_returns_first_duplicate() -> None:
    result = resolve_symbol_index(
        "BTC/USD_LEVERAGE",
        (
            "BTC/USD_LEVERAGE",
            "BTC/USD_LEVERAGE",
        ),
    )

    assert result == 0


def test_resolve_symbol_index_does_not_modify_available_symbols() -> None:
    available_symbols = [
        "BTC/USD_LEVERAGE",
        "ETH/USD_LEVERAGE",
    ]
    original_symbols = list(available_symbols)

    resolve_symbol_index(
        "BTC/USD_LEVERAGE",
        available_symbols,
    )

    assert available_symbols == original_symbols