from dot_search import SearchResult
from dot_search.fusion import reciprocal_rank_fusion


def make_results(ids: list[str]) -> list[SearchResult]:
    return [SearchResult(id=id_, score=1.0, row={}) for id_ in ids]


def test_single_list_preserves_order():
    results = make_results(["a", "b", "c"])
    fused = reciprocal_rank_fusion([results], weights=[1.0])
    assert [r.id for r in fused] == ["a", "b", "c"]


def test_two_identical_lists_preserves_order():
    results = make_results(["a", "b", "c"])
    fused = reciprocal_rank_fusion([results, results], weights=[1.0, 1.0])
    assert [r.id for r in fused] == ["a", "b", "c"]


def test_two_lists_agreement_boosts_score():
    list1 = make_results(["a", "b", "c"])
    list2 = make_results(["b", "a", "c"])
    fused = reciprocal_rank_fusion([list1, list2], weights=[1.0, 1.0])
    ids = [r.id for r in fused]
    # "a" is rank 0 in list1, rank 1 in list2
    # "b" is rank 1 in list1, rank 0 in list2
    # Both should score higher than "c" which is rank 2 in both
    assert ids.index("c") > ids.index("a")
    assert ids.index("c") > ids.index("b")


def test_higher_weight_boosts_list():
    list1 = make_results(["a", "b"])  # "a" is top in list1
    list2 = make_results(["b", "a"])  # "b" is top in list2
    # Give list2 much higher weight — "b" should win
    fused = reciprocal_rank_fusion([list1, list2], weights=[1.0, 10.0])
    assert fused[0].id == "b"


def test_item_only_in_one_list_still_appears():
    list1 = make_results(["a", "b"])
    list2 = make_results(["c", "d"])
    fused = reciprocal_rank_fusion([list1, list2], weights=[1.0, 1.0])
    ids = [r.id for r in fused]
    assert set(ids) == {"a", "b", "c", "d"}


def test_empty_lists():
    fused = reciprocal_rank_fusion([[], []], weights=[1.0, 1.0])
    assert fused == []


def test_scores_are_positive():
    results = make_results(["a", "b", "c"])
    fused = reciprocal_rank_fusion([results], weights=[1.0])
    assert all(r.score > 0 for r in fused)


def test_scores_are_descending():
    results = make_results(["a", "b", "c", "d"])
    fused = reciprocal_rank_fusion([results], weights=[1.0])
    scores = [r.score for r in fused]
    assert scores == sorted(scores, reverse=True)
