"""Tests for BaseRepository."""
from __future__ import annotations

import pytest

from repo_core import BaseRepository, hook


class ArticleRepository(BaseRepository["dict"]):
    _backends = set()  # tests control backend wiring explicitly


class HookedRepository(BaseRepository["dict"]):
    _backends = set()  # tests control backend wiring explicitly

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.before_create_called = False
        self.after_create_called = False

    @hook("before_create")
    async def on_before_create(self, data: dict) -> None:
        self.before_create_called = True

    @hook("after_create")
    async def on_after_create(self, record: dict) -> None:
        self.after_create_called = True


# ---------------------------------------------------------------------------

async def test_repository_stores_querybuilders(db_query, search_query):
    repo = ArticleRepository(db=db_query, search=search_query)
    assert repo.db is db_query
    assert repo.search is search_query


async def test_repository_raises_when_no_db():
    repo = ArticleRepository()
    with pytest.raises(RuntimeError, match="DB QueryBuilder"):
        _ = repo.db


async def test_repository_raises_when_no_search():
    repo = ArticleRepository()
    with pytest.raises(RuntimeError, match="Search QueryBuilder"):
        _ = repo.search


async def test_repository_connect_delegates_to_clients(db_query, search_query):
    repo = ArticleRepository(db=db_query, search=search_query)
    await repo.connect()
    db_query.client.connect.assert_awaited_once()
    search_query.client.connect.assert_awaited_once()


async def test_repository_disconnect_delegates_to_clients(db_query, search_query):
    repo = ArticleRepository(db=db_query, search=search_query)
    await repo.disconnect()
    db_query.client.disconnect.assert_awaited_once()
    search_query.client.disconnect.assert_awaited_once()


async def test_repository_context_manager(db_query):
    repo = ArticleRepository(db=db_query)
    async with repo as r:
        assert r is repo
    db_query.client.connect.assert_awaited_once()
    db_query.client.disconnect.assert_awaited_once()


async def test_repository_no_clients_connect_is_noop():
    repo = ArticleRepository()
    await repo.connect()
    await repo.disconnect()


async def test_hook_auto_registration():
    repo = HookedRepository()
    assert len(repo.hooks._handlers["before_create"]) == 1
    assert len(repo.hooks._handlers["after_create"]) == 1


async def test_hook_emit_triggers_handler():
    repo = HookedRepository()
    await repo.emit("before_create", {"title": "Hello"})
    assert repo.before_create_called is True
    await repo.emit("after_create", {"id": 1, "title": "Hello"})
    assert repo.after_create_called is True


async def test_hook_emit_unknown_event_is_noop():
    repo = HookedRepository()
    await repo.emit("nonexistent_event", {})


async def test_repository_multiple_hooks_on_one_event():
    calls: list[str] = []

    class MultiHookRepo(BaseRepository["dict"]):
        @hook("before_delete")
        async def first(self, *_):
            calls.append("first")

        @hook("before_delete")
        async def second(self, *_):
            calls.append("second")

    repo = MultiHookRepo()
    await repo.emit("before_delete", {"id": 1})
    assert calls == ["first", "second"]


async def test_table_method_returns_querybuilder(db_query):
    repo = ArticleRepository(db=db_query)
    qb = repo.table("users")
    assert qb is not None


async def test_search_table_method_returns_querybuilder(search_query):
    repo = ArticleRepository(search=search_query)
    qb = repo.search_table("articles")
    assert qb is not None
