from __future__ import annotations

import json
from types import SimpleNamespace
from unittest.mock import AsyncMock

import pytest

from cortex.capture.episode_adapter import _build_episode_payload, log_episode_event


def test_build_episode_payload_tracks_roles_and_tools() -> None:
    conversation = [
        {"role": "user", "content": "remember this"},
        {"role": "assistant", "content": [{"text": "okay"}]},
        {
            "role": "tool",
            "content": [{"text": "search done"}],
            "tool_name": "web_search",
            "tool_call_id": "call-123",
            "outcome": "success",
            "source_uri": "https://example.com",
        },
    ]

    raw_text, actor_id, tool_trace_ref, source_uri, metadata_json = _build_episode_payload(
        conversation,
        "sess-123",
    )

    payload = json.loads(raw_text)
    metadata = json.loads(metadata_json)

    assert actor_id == "conversation"
    assert tool_trace_ref == "call-123"
    assert source_uri == "https://example.com"
    assert payload[1]["content"] == "okay"
    assert payload[2]["tool_name"] == "web_search"
    assert metadata["session_id"] == "sess-123"
    assert metadata["message_count"] == 3
    assert metadata["role_counts"] == {"user": 1, "assistant": 1, "tool": 1}
    assert metadata["has_tool_calls"] is True
    assert metadata["has_tool_results"] is True


def test_build_episode_payload_single_actor_stays_specific() -> None:
    raw_text, actor_id, tool_trace_ref, source_uri, metadata_json = _build_episode_payload(
        [{"role": "user", "content": "just me"}],
        "sess-456",
    )

    assert json.loads(raw_text)[0]["content"] == "just me"
    assert actor_id == "user"
    assert tool_trace_ref is None
    assert source_uri is None
    assert json.loads(metadata_json)["role_counts"] == {"user": 1}


@pytest.mark.asyncio
async def test_log_episode_event_passes_normalized_metadata() -> None:
    storage = SimpleNamespace()
    storage.log_episode = AsyncMock(return_value=SimpleNamespace(id="evt-001"))

    episode_id = await log_episode_event(
        storage=storage,
        session_id_pk="db-sess-1",
        conversation=[
            {"role": "user", "content": "hello"},
            {"role": "assistant", "content": [{"text": "hi"}]},
        ],
        owner_id="default",
        sid="sess-789",
    )

    assert episode_id == "evt-001"
    call = storage.log_episode.await_args.kwargs
    assert call["session_id"] == "db-sess-1"
    assert call["owner_id"] == "default"
    assert call["kind"] == "conversation"
    assert call["actor_id"] == "conversation"

    metadata = json.loads(call["metadata_json"])
    assert metadata["session_id"] == "sess-789"
    assert metadata["message_count"] == 2
    assert metadata["role_counts"] == {"user": 1, "assistant": 1}


@pytest.mark.asyncio
async def test_log_episode_event_degrades_safely_on_storage_failure() -> None:
    storage = SimpleNamespace()
    storage.log_episode = AsyncMock(side_effect=RuntimeError("db down"))

    episode_id = await log_episode_event(
        storage=storage,
        session_id_pk="db-sess-1",
        conversation=[{"role": "user", "content": "hello"}],
        owner_id="default",
        sid="sess-999",
    )

    assert episode_id is None
