"""BaseRepository coverage — concrete subclass over AgentTable."""

from __future__ import annotations

from typing import TYPE_CHECKING
from uuid import uuid4

import pytest
import pytest_asyncio
from roboco.db.tables import AgentTable
from roboco.models import AgentRole, AgentStatus, Team
from roboco.services.base import NotFoundError
from roboco.services.repositories.base import BaseRepository
from sqlalchemy import func, select

if TYPE_CHECKING:
    from collections.abc import AsyncIterator

    from sqlalchemy.ext.asyncio import AsyncSession


class _AgentRepo(BaseRepository[AgentTable]):
    model = AgentTable
    model_name = "Agent"


@pytest_asyncio.fixture
async def repo_setup(
    db_session: AsyncSession,
) -> AsyncIterator[dict]:
    repo = _AgentRepo(db_session)
    a = AgentTable(
        id=uuid4(),
        name="Dev1",
        slug=f"dev-{uuid4().hex[:8]}",
        role=AgentRole.DEVELOPER,
        team=Team.BACKEND,
        status=AgentStatus.ACTIVE,
        model_config={},
        system_prompt="dev",
        capabilities=[],
        permissions={},
        metrics={},
    )
    b = AgentTable(
        id=uuid4(),
        name="Dev2",
        slug=f"dev-{uuid4().hex[:8]}",
        role=AgentRole.DEVELOPER,
        team=Team.FRONTEND,
        status=AgentStatus.ACTIVE,
        model_config={},
        system_prompt="dev",
        capabilities=[],
        permissions={},
        metrics={},
    )
    db_session.add_all([a, b])
    await db_session.flush()
    yield {"repo": repo, "a": a, "b": b}


@pytest.mark.asyncio
async def test_get_returns_entity(repo_setup: dict) -> None:
    repo = repo_setup["repo"]
    found = await repo.get(repo_setup["a"].id)
    assert found is not None
    assert found.id == repo_setup["a"].id


@pytest.mark.asyncio
async def test_get_returns_none_for_missing(repo_setup: dict) -> None:
    repo = repo_setup["repo"]
    assert await repo.get(uuid4()) is None


@pytest.mark.asyncio
async def test_get_or_raise_raises(repo_setup: dict) -> None:
    repo = repo_setup["repo"]
    with pytest.raises(NotFoundError):
        await repo.get_or_raise(uuid4())


@pytest.mark.asyncio
async def test_get_or_raise_returns_entity(repo_setup: dict) -> None:
    repo = repo_setup["repo"]
    fetched = await repo.get_or_raise(repo_setup["a"].id)
    assert fetched.id == repo_setup["a"].id


@pytest.mark.asyncio
async def test_get_all_with_default_ordering(repo_setup: dict) -> None:
    repo = repo_setup["repo"]
    rows = await repo.get_all(limit=100)
    ids = {r.id for r in rows}
    assert repo_setup["a"].id in ids
    assert repo_setup["b"].id in ids


@pytest.mark.asyncio
async def test_get_all_with_explicit_order(repo_setup: dict) -> None:
    repo = repo_setup["repo"]
    rows = await repo.get_all(limit=100, order_by=AgentTable.name.asc())
    assert isinstance(rows, list)


@pytest.mark.asyncio
async def test_find_by(repo_setup: dict) -> None:
    repo = repo_setup["repo"]
    rows = await repo.find_by(AgentTable.team == Team.BACKEND)
    assert any(r.id == repo_setup["a"].id for r in rows)


@pytest.mark.asyncio
async def test_find_by_with_explicit_order(repo_setup: dict) -> None:
    repo = repo_setup["repo"]
    rows = await repo.find_by(
        AgentTable.team == Team.BACKEND, order_by=AgentTable.name.asc()
    )
    assert isinstance(rows, list)


@pytest.mark.asyncio
async def test_find_one_returns_entity(repo_setup: dict) -> None:
    repo = repo_setup["repo"]
    row = await repo.find_one(AgentTable.id == repo_setup["a"].id)
    assert row is not None
    assert row.id == repo_setup["a"].id


@pytest.mark.asyncio
async def test_find_one_returns_none(repo_setup: dict) -> None:
    repo = repo_setup["repo"]
    assert await repo.find_one(AgentTable.id == uuid4()) is None


@pytest.mark.asyncio
async def test_exists_true(repo_setup: dict) -> None:
    repo = repo_setup["repo"]
    assert await repo.exists(repo_setup["a"].id) is True


@pytest.mark.asyncio
async def test_exists_false(repo_setup: dict) -> None:
    repo = repo_setup["repo"]
    assert await repo.exists(uuid4()) is False


@pytest.mark.asyncio
async def test_count_with_conditions(repo_setup: dict) -> None:
    repo = repo_setup["repo"]
    count = await repo.count(AgentTable.team == Team.BACKEND)
    assert count >= 1


@pytest.mark.asyncio
async def test_count_without_conditions(repo_setup: dict) -> None:
    repo = repo_setup["repo"]
    count = await repo.count()
    _TWO = 2
    assert count >= _TWO


@pytest.mark.asyncio
async def test_add(repo_setup: dict) -> None:
    repo = repo_setup["repo"]
    new_agent = AgentTable(
        id=uuid4(),
        name="New",
        slug=f"new-{uuid4().hex[:8]}",
        role=AgentRole.QA,
        team=Team.BACKEND,
        status=AgentStatus.ACTIVE,
        model_config={},
        system_prompt="x",
        capabilities=[],
        permissions={},
        metrics={},
    )
    added = await repo.add(new_agent)
    assert added.id == new_agent.id


@pytest.mark.asyncio
async def test_delete(repo_setup: dict) -> None:
    repo = repo_setup["repo"]
    await repo.delete(repo_setup["a"])
    assert await repo.get(repo_setup["a"].id) is None


@pytest.mark.asyncio
async def test_delete_by_id_returns_true(repo_setup: dict) -> None:
    repo = repo_setup["repo"]
    deleted = await repo.delete_by_id(repo_setup["a"].id)
    assert deleted is True


@pytest.mark.asyncio
async def test_delete_by_id_returns_false(repo_setup: dict) -> None:
    repo = repo_setup["repo"]
    assert await repo.delete_by_id(uuid4()) is False


@pytest.mark.asyncio
async def test_query_returns_select(repo_setup: dict) -> None:
    repo = repo_setup["repo"]
    query = repo.query()
    rows = await repo.execute_query(query)
    assert isinstance(rows, list)


@pytest.mark.asyncio
async def test_execute_scalar(repo_setup: dict) -> None:
    repo = repo_setup["repo"]
    query = select(func.count(AgentTable.id))
    result = await repo.execute_scalar(query)
    assert isinstance(result, int)
