"""Tests for BaseRepository search/convenience methods
(search_find, search_first, search_get, search_count)."""

from __future__ import annotations

from unittest.mock import AsyncMock, MagicMock

import pytest

from repo_core import BaseRepository


class SearchRepo(BaseRepository[dict]):
    _backends = set()


@pytest.fixture
def repo(search_query):
    return SearchRepo(search=search_query)


# ===========================================================================
# search_find
# ===========================================================================

async def test_search_find_returns_documents(repo, search_query):
    qb_mock = MagicMock()
    qb_mock.where = MagicMock(return_value=qb_mock)
    qb_mock.order_by = MagicMock(return_value=qb_mock)
    qb_mock.limit = MagicMock(return_value=qb_mock)
    qb_mock.offset = MagicMock(return_value=qb_mock)
    qb_mock.all = AsyncMock(return_value=[{"_id": "1"}, {"_id": "2"}])
    repo.search_table = MagicMock(return_value=qb_mock)

    result = await repo.search_find("articles", {"title": "hello"})
    assert len(result) == 2
    qb_mock.where.assert_called_once()


async def test_search_find_no_conditions(repo, search_query):
    qb_mock = MagicMock()
    qb_mock.all = AsyncMock(return_value=[{"_id": "1"}, {"_id": "2"}, {"_id": "3"}])
    repo.search_table = MagicMock(return_value=qb_mock)

    result = await repo.search_find("articles")
    assert len(result) == 3
    qb_mock.where.assert_not_called()


async def test_search_find_with_pagination(repo, search_query):
    qb_mock = MagicMock()
    qb_mock.where = MagicMock(return_value=qb_mock)
    qb_mock.order_by = MagicMock(return_value=qb_mock)
    qb_mock.limit = MagicMock(return_value=qb_mock)
    qb_mock.offset = MagicMock(return_value=qb_mock)
    qb_mock.all = AsyncMock(return_value=[])
    repo.search_table = MagicMock(return_value=qb_mock)

    await repo.search_find("articles", {"status": "pub"}, limit=10, offset=0, order_by=["-date"])
    qb_mock.limit.assert_called_once_with(10)
    qb_mock.offset.assert_called_once_with(0)
    qb_mock.order_by.assert_called_once_with("-date")


async def test_search_find_with_model_conversion(search_query):
    from repo_core import DomainModel

    class FakeModel(DomainModel):
        id: int
        title: str

    repo = SearchRepo(model=FakeModel, search=search_query)
    qb_mock = MagicMock()
    qb_mock.where = MagicMock(return_value=qb_mock)
    qb_mock.order_by = MagicMock(return_value=qb_mock)
    qb_mock.limit = MagicMock(return_value=qb_mock)
    qb_mock.offset = MagicMock(return_value=qb_mock)
    qb_mock.all = AsyncMock(return_value=[{"id": 1, "title": "Hello"}])
    repo.search_table = MagicMock(return_value=qb_mock)

    result = await repo.search_find("articles")
    assert len(result) == 1
    assert result[0].id == 1
    assert result[0].title == "Hello"


# ===========================================================================
# search_first
# ===========================================================================

async def test_search_first_returns_document(repo, search_query):
    qb_mock = MagicMock()
    qb_mock.where = MagicMock(return_value=qb_mock)
    qb_mock.first = AsyncMock(return_value={"_id": "abc"})
    repo.search_table = MagicMock(return_value=qb_mock)

    result = await repo.search_first("articles", {"_id": "abc"})
    assert result == {"_id": "abc"}


async def test_search_first_no_conditions(repo, search_query):
    qb_mock = MagicMock()
    qb_mock.first = AsyncMock(return_value={"_id": "first"})
    repo.search_table = MagicMock(return_value=qb_mock)

    result = await repo.search_first("articles")
    assert result == {"_id": "first"}
    qb_mock.where.assert_not_called()


async def test_search_first_returns_none_when_empty(repo, search_query):
    qb_mock = MagicMock()
    qb_mock.where = MagicMock(return_value=qb_mock)
    qb_mock.first = AsyncMock(return_value=None)
    repo.search_table = MagicMock(return_value=qb_mock)

    result = await repo.search_first("articles", {"status": "missing"})
    assert result is None


# ===========================================================================
# search_get
# ===========================================================================

async def test_search_get_returns_document(repo, search_query):
    qb_mock = MagicMock()
    qb_mock.where = MagicMock(return_value=qb_mock)
    qb_mock.first = AsyncMock(return_value={"_id": "doc-1"})
    repo.search_table = MagicMock(return_value=qb_mock)

    result = await repo.search_get("articles", "doc-1")
    assert result == {"_id": "doc-1"}


async def test_search_get_custom_id_field(repo, search_query):
    qb_mock = MagicMock()
    qb_mock.where = MagicMock(return_value=qb_mock)
    qb_mock.first = AsyncMock(return_value={"_key": "my-key"})
    repo.search_table = MagicMock(return_value=qb_mock)

    result = await repo.search_get("articles", "my-key", id_field="_key")
    assert result == {"_key": "my-key"}


async def test_search_get_returns_none_when_not_found(repo, search_query):
    qb_mock = MagicMock()
    qb_mock.where = MagicMock(return_value=qb_mock)
    qb_mock.first = AsyncMock(return_value=None)
    repo.search_table = MagicMock(return_value=qb_mock)

    result = await repo.search_get("articles", "missing")
    assert result is None


# ===========================================================================
# search_count
# ===========================================================================

async def test_search_count_returns_count(repo, search_query):
    qb_mock = MagicMock()
    qb_mock.where = MagicMock(return_value=qb_mock)
    qb_mock.count = AsyncMock(return_value=99)
    repo.search_table = MagicMock(return_value=qb_mock)

    result = await repo.search_count("articles", {"status": "active"})
    assert result == 99


async def test_search_count_no_conditions(repo, search_query):
    qb_mock = MagicMock()
    qb_mock.count = AsyncMock(return_value=0)
    repo.search_table = MagicMock(return_value=qb_mock)

    result = await repo.search_count("articles")
    assert result == 0
