"""Tests for SessionStore implementations: updated_at stamping and list_by_user."""

from __future__ import annotations

import pytest

from agentbyte.context import AgentContext
from agentbyte.session_store import (
    CachedFileSessionStore,
    FileSessionStore,
    InMemorySessionStore,
)


# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------


@pytest.fixture
def in_memory_store() -> InMemorySessionStore:
    return InMemorySessionStore()


@pytest.fixture
def file_store(tmp_path) -> FileSessionStore:
    return FileSessionStore(storage_dir=str(tmp_path / "sessions"))


@pytest.fixture
def cached_store(tmp_path) -> CachedFileSessionStore:
    return CachedFileSessionStore(storage_dir=str(tmp_path / "cached"))


ALL_STORE_FIXTURES = ["in_memory_store", "file_store", "cached_store"]


# ---------------------------------------------------------------------------
# updated_at stamping
# ---------------------------------------------------------------------------


@pytest.mark.asyncio
@pytest.mark.parametrize("store_fixture", ALL_STORE_FIXTURES)
async def test_save_stamps_updated_at(store_fixture, request) -> None:
    store = request.getfixturevalue(store_fixture)
    ctx = AgentContext(session_id="s1")
    assert ctx.updated_at is None

    await store.save("s1", ctx)

    loaded = await store.get("s1")
    assert loaded is not None
    assert loaded.updated_at is not None


@pytest.mark.asyncio
@pytest.mark.parametrize("store_fixture", ALL_STORE_FIXTURES)
async def test_updated_at_advances_on_second_save(store_fixture, request) -> None:
    store = request.getfixturevalue(store_fixture)
    ctx = AgentContext(session_id="s2")

    await store.save("s2", ctx)
    first = (await store.get("s2")).updated_at

    await store.save("s2", ctx)
    second = (await store.get("s2")).updated_at

    assert second >= first


@pytest.mark.asyncio
@pytest.mark.parametrize("store_fixture", ALL_STORE_FIXTURES)
async def test_cache_and_file_share_same_updated_at(store_fixture, request) -> None:
    """CachedFileSessionStore must not double-stamp (cache and file stay in sync)."""
    store = request.getfixturevalue(store_fixture)
    ctx = AgentContext(session_id="s3")
    await store.save("s3", ctx)

    loaded = await store.get("s3")
    assert loaded.updated_at is not None


# ---------------------------------------------------------------------------
# list_by_user
# ---------------------------------------------------------------------------


@pytest.mark.asyncio
@pytest.mark.parametrize("store_fixture", ALL_STORE_FIXTURES)
async def test_list_by_user_returns_matching_sessions(store_fixture, request) -> None:
    store = request.getfixturevalue(store_fixture)

    ctx_a1 = AgentContext(session_id="a1", user_id="alice")
    ctx_a2 = AgentContext(session_id="a2", user_id="alice")
    ctx_b1 = AgentContext(session_id="b1", user_id="bob")

    await store.save("a1", ctx_a1)
    await store.save("a2", ctx_a2)
    await store.save("b1", ctx_b1)

    alice_sessions = list(await store.list_by_user("alice"))
    assert len(alice_sessions) == 2
    assert {sid for sid, _ in alice_sessions} == {"a1", "a2"}


@pytest.mark.asyncio
@pytest.mark.parametrize("store_fixture", ALL_STORE_FIXTURES)
async def test_list_by_user_empty_when_no_match(store_fixture, request) -> None:
    store = request.getfixturevalue(store_fixture)
    ctx = AgentContext(session_id="x1", user_id="alice")
    await store.save("x1", ctx)

    result = list(await store.list_by_user("nobody"))
    assert result == []


@pytest.mark.asyncio
@pytest.mark.parametrize("store_fixture", ALL_STORE_FIXTURES)
async def test_list_by_user_excludes_sessions_without_user_id(store_fixture, request) -> None:
    store = request.getfixturevalue(store_fixture)

    ctx_owned = AgentContext(session_id="owned", user_id="carol")
    ctx_anon = AgentContext(session_id="anon")  # user_id=None

    await store.save("owned", ctx_owned)
    await store.save("anon", ctx_anon)

    carol_sessions = list(await store.list_by_user("carol"))
    assert len(carol_sessions) == 1
    assert carol_sessions[0][0] == "owned"
