"""Tests for list_tags.

The aggregation helper is unit-tested directly; the full tool is exercised
with a mocked async_session to keep tests DB-free.
"""
from unittest.mock import AsyncMock, MagicMock, patch

import pytest

from scribe.mcp.tools.tags import list_tags, _aggregate_tag_counts
from tests.helpers import make_mock_session


pytestmark = pytest.mark.usefixtures("_bind_user")


def test_aggregate_tag_counts_basic():
    assert _aggregate_tag_counts([["ops", "kafka"], ["ops"]]) == {"ops": 2, "kafka": 1}


def test_aggregate_tag_counts_handles_none_rows():
    """A note with NULL tags must not blow up the aggregator."""
    assert _aggregate_tag_counts([["a"], None, ["a", "b"]]) == {"a": 2, "b": 1}


def test_aggregate_tag_counts_empty_input():
    assert _aggregate_tag_counts([]) == {}


@pytest.mark.asyncio
async def test_fable_list_tags_returns_sorted_by_count_desc():
    """End-to-end: query returns three rows, top tag wins."""
    mock_result = MagicMock()
    mock_result.all.return_value = [(["a"],), (["a", "b"],), (["a"],)]
    mock_session = make_mock_session()
    mock_session.execute = AsyncMock(return_value=mock_result)
    mock_ctx = MagicMock(return_value=mock_session)
    with patch("scribe.mcp.tools.tags.async_session", mock_ctx):
        out = await list_tags()
    assert out["tags"][0] == {"tag": "a", "count": 3}
    assert out["tags"][1] == {"tag": "b", "count": 1}
    assert out["total"] == 2


@pytest.mark.asyncio
async def test_fable_list_tags_clamps_limit():
    mock_result = MagicMock()
    mock_result.all.return_value = []
    mock_session = make_mock_session()
    mock_session.execute = AsyncMock(return_value=mock_result)
    mock_ctx = MagicMock(return_value=mock_session)
    with patch("scribe.mcp.tools.tags.async_session", mock_ctx):
        # No exception — just exercises the clamping branch
        out = await list_tags(limit=99999)
    assert out["total"] == 0
