from __future__ import annotations

import pytest

from kyrodb.filters import exact
from kyrodb.models import InsertItem, MetadataFilter, SearchQuery


def test_insert_item_rejects_uint64_overflow_doc_id() -> None:
    with pytest.raises(ValueError, match="uint64 range"):
        InsertItem.from_parts(doc_id=0x1_0000_0000_0000_0000, embedding=[0.1, 0.2])


def test_insert_item_rejects_empty_embedding() -> None:
    with pytest.raises(ValueError, match="non-empty"):
        InsertItem.from_parts(doc_id=1, embedding=[])


def test_insert_item_rejects_non_finite_embedding_values() -> None:
    with pytest.raises(ValueError, match="non-finite"):
        InsertItem.from_parts(doc_id=1, embedding=[0.1, float("inf")])
    with pytest.raises(ValueError, match="non-finite"):
        InsertItem.from_parts(doc_id=1, embedding=[float("nan"), 0.2])


def test_metadata_filter_from_proto_rejects_wrong_type() -> None:
    with pytest.raises(TypeError, match="MetadataFilter"):
        MetadataFilter.from_proto(object())


def test_metadata_filter_round_trip() -> None:
    filt = exact("tier", "hot")
    proto = filt.to_proto()
    restored = MetadataFilter.from_proto(proto)
    assert restored.to_proto().SerializeToString(deterministic=True) == proto.SerializeToString(
        deterministic=True
    )


def test_search_query_rejects_non_metadata_filter() -> None:
    with pytest.raises(TypeError, match="MetadataFilter"):
        SearchQuery.from_parts(query_embedding=[0.1], k=1, filter="bad-filter-type")  # type: ignore[arg-type]


@pytest.mark.parametrize("value", [float("nan"), float("inf"), float("-inf")])
def test_search_query_rejects_non_finite_min_score(value: float) -> None:
    with pytest.raises(ValueError, match="min_score must be a finite number"):
        SearchQuery.from_parts(query_embedding=[0.1], k=1, min_score=value)
