"""Tests for vr session commands."""

import json
import pytest
from unittest.mock import patch, Mock

from typer.testing import CliRunner

from vr_cli.cli import app
from vr_cli.entities.agent import Agent
from vr_cli.entities.agent_session import AgentSession
from vr_cli.utils.utils import is_agent


runner = CliRunner()


@pytest.fixture
def mock_agent():
    return Agent(
        id="test-agent-id-123",
        name="Test Agent",
        description="A test agent",
        created_at="2024-01-01T00:00:00Z",
        updated_at="2024-01-02T00:00:00Z",
    )


@pytest.fixture
def mock_sessions():
    return [
        AgentSession(
            id="session-1",
            agent_id="test-agent-id-123",
            agent_environment_id="env-1",
            deployment_id="deploy-1",
            status="ended",
            direction="inbound",
            origin="phone",
            usage={"sessionDuration": 125000, "ttsDuration": 30000},
            tags=["sales", "demo"],
            telephony_call_id="call-abc",
            created_at="2024-01-01T00:00:00Z",
            updated_at="2024-01-01T00:02:05Z",
        ),
        AgentSession(
            id="session-2",
            agent_id="test-agent-id-123",
            status="active",
            direction="outbound",
            origin="api",
            usage={"sessionDuration": 5500},
            tags=[],
            created_at="2024-01-02T00:00:00Z",
            updated_at="2024-01-02T00:00:05Z",
        ),
    ]


@pytest.fixture
def mock_trace_data():
    return {
        "trace": {
            "traceId": "trace-123",
            "durationMs": 125000,
        },
        "spans": [
            {
                "spanId": "span-root",
                "name": "session",
                "parentId": None,
                "startTime": "2024-01-01T00:00:00Z",
                "endTime": "2024-01-01T00:02:05Z",
                "durationMs": 125000,
                "statusCode": "OK",
                "preview": None,
            },
            {
                "spanId": "span-child-1",
                "name": "tts",
                "parentId": "span-root",
                "startTime": "2024-01-01T00:00:01Z",
                "endTime": "2024-01-01T00:00:04Z",
                "durationMs": 3000,
                "statusCode": "OK",
                "preview": "Hello, how can I help?",
            },
            {
                "spanId": "span-child-2",
                "name": "module",
                "parentId": "span-root",
                "startTime": "2024-01-01T00:00:05Z",
                "endTime": "2024-01-01T00:00:06Z",
                "durationMs": 1000,
                "statusCode": "ERROR",
                "preview": "process_order",
            },
        ],
    }


@pytest.fixture
def mock_transcript_events():
    return [
        {
            "timestamp": "2024-01-01T00:00:01Z",
            "role": "agent",
            "content": "Hello, how can I help you today?",
        },
        {
            "timestamp": "2024-01-01T00:00:05Z",
            "role": "user",
            "content": "I need help with my order.",
        },
        {
            "timestamp": "2024-01-01T00:00:08Z",
            "role": "agent",
            "content": "Sure, let me look that up for you.",
        },
    ]


@pytest.fixture
def mock_events_response():
    return {
        "data": [
            {
                "timestamp": "2024-01-01T00:00:01Z",
                "name": "session.start",
                "data": {"direction": "inbound"},
            },
            {
                "timestamp": "2024-01-01T00:00:02Z",
                "name": "tts.start",
                "data": {"text": "Hello, how can I help?"},
            },
            {
                "timestamp": "2024-01-01T00:00:05Z",
                "name": "session.end",
                "data": {},
            },
        ],
        "metadata": {
            "page": 1,
            "limit": 10,
            "total": 3,
        },
    }


@pytest.fixture
def mock_span_data():
    return {
        "spanId": "span-child-1",
        "name": "tts",
        "spanKind": "INTERNAL",
        "eventName": "Hello, how can I help?",
        "turnNumber": 1,
        "parentId": "span-root",
        "startTime": "2024-01-01T00:00:01Z",
        "endTime": "2024-01-01T00:00:04Z",
        "durationMs": 3000,
        "statusCode": "OK",
        "statusMessage": "Success",
        "attributes": {"voice": "alloy", "model": "tts-1"},
    }


# ============================================================================
# Session List Tests
# ============================================================================


class TestSessionList:
    """Tests for session list command."""

    def test_session_list_success(self, mock_agent, mock_sessions):
        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.session.AgentSessionRepository") as mock_session_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_session_repo = Mock()
            mock_session_repo.get.return_value = mock_sessions
            mock_session_repo_class.return_value = mock_session_repo

            result = runner.invoke(app, ["session", "list", "Test Agent", "--table"])

        assert result.exit_code == 0
        assert "session-1" in result.output
        assert "session-2" in result.output
        assert "ended" in result.output
        assert "inbound" in result.output
        assert "2m 5s" in result.output

    def test_session_list_empty(self, mock_agent):
        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.session.AgentSessionRepository") as mock_session_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_session_repo = Mock()
            mock_session_repo.get.return_value = []
            mock_session_repo_class.return_value = mock_session_repo

            result = runner.invoke(app, ["session", "list", "Test Agent", "--table"])

        assert result.exit_code == 0
        assert "No sessions found" in result.output

    def test_session_list_agent_not_found(self):
        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class:
            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = None
            mock_agent_repo_class.return_value = mock_agent_repo

            result = runner.invoke(app, ["session", "list", "nonexistent"])

        assert result.exit_code != 0
        assert "Agent not found" in result.output

    def test_session_list_with_filters(self, mock_agent, mock_sessions):
        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.session.AgentSessionRepository") as mock_session_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_session_repo = Mock()
            mock_session_repo.get.return_value = mock_sessions
            mock_session_repo_class.return_value = mock_session_repo

            result = runner.invoke(app, ["session", "list", "Test Agent", "--status", "ended", "--direction", "inbound", "--table"])

        assert result.exit_code == 0
        mock_session_repo.get.assert_called_once_with({"status": "ended", "direction": "inbound"}, page=None, limit=None)

    def test_session_list_json_output(self, mock_agent, mock_sessions):
        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.session.AgentSessionRepository") as mock_session_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_session_repo = Mock()
            mock_session_repo.get.return_value = mock_sessions
            mock_session_repo_class.return_value = mock_session_repo

            result = runner.invoke(app, ["session", "list", "Test Agent", "--json"])

        assert result.exit_code == 0
        output = json.loads(result.output)
        assert len(output) == 2
        assert output[0]["id"] == "session-1"

    def test_session_list_json_empty(self, mock_agent):
        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.session.AgentSessionRepository") as mock_session_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_session_repo = Mock()
            mock_session_repo.get.return_value = []
            mock_session_repo_class.return_value = mock_session_repo

            result = runner.invoke(app, ["session", "list", "Test Agent", "--json"])

        assert result.exit_code == 0
        output = json.loads(result.output)
        assert output == []


# ============================================================================
# Session Info Tests
# ============================================================================


class TestSessionInfo:
    """Tests for session info command."""

    def test_session_info_success(self, mock_agent):
        session = AgentSession(
            id="session-1",
            agent_id="test-agent-id-123",
            agent_environment_id="env-1",
            deployment_id="deploy-1",
            status="ended",
            direction="inbound",
            origin="phone",
            usage={"sessionDuration": 125000, "ttsDuration": 30000, "cachedTtsDuration": 5000, "moduleDuration": 10000},
            tags=["sales", "demo"],
            telephony_call_id="call-abc",
            created_at="2024-01-01T00:00:00Z",
            updated_at="2024-01-01T00:02:05Z",
        )

        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.session.AgentSessionRepository") as mock_session_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_session_repo = Mock()
            mock_session_repo.get_by_id.return_value = session
            mock_session_repo_class.return_value = mock_session_repo

            result = runner.invoke(app, ["session", "info", "Test Agent", "session-1", "--table"])

        assert result.exit_code == 0
        assert "session-1" in result.output
        assert "ended" in result.output
        assert "inbound" in result.output
        assert "phone" in result.output
        assert "Session Info" in result.output
        assert "Usage" in result.output
        assert "Tags" in result.output
        assert "sales, demo" in result.output

    def test_session_info_not_found(self, mock_agent):
        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.session.AgentSessionRepository") as mock_session_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_session_repo = Mock()
            mock_session_repo.get_by_id.return_value = None
            mock_session_repo_class.return_value = mock_session_repo

            result = runner.invoke(app, ["session", "info", "Test Agent", "nonexistent"])

        assert result.exit_code != 0
        assert "Session not found" in result.output

    def test_session_info_agent_not_found(self):
        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class:
            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = None
            mock_agent_repo_class.return_value = mock_agent_repo

            result = runner.invoke(app, ["session", "info", "nonexistent", "session-1"])

        assert result.exit_code != 0
        assert "Agent not found" in result.output

    def test_session_info_json_output(self, mock_agent):
        session = AgentSession(
            id="session-1",
            agent_id="test-agent-id-123",
            status="ended",
            direction="inbound",
            origin="phone",
            usage={"sessionDuration": 125000},
            tags=["sales"],
            created_at="2024-01-01T00:00:00Z",
            updated_at="2024-01-01T00:02:05Z",
        )

        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.session.AgentSessionRepository") as mock_session_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_session_repo = Mock()
            mock_session_repo.get_by_id.return_value = session
            mock_session_repo_class.return_value = mock_session_repo

            result = runner.invoke(app, ["session", "info", "Test Agent", "session-1", "--json"])

        assert result.exit_code == 0
        output = json.loads(result.output)
        assert output["id"] == "session-1"
        assert output["status"] == "ended"

    def test_session_info_no_tags(self, mock_agent):
        session = AgentSession(
            id="session-1",
            agent_id="test-agent-id-123",
            status="ended",
            tags=None,
            created_at="2024-01-01T00:00:00Z",
            updated_at="2024-01-01T00:02:05Z",
        )

        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.session.AgentSessionRepository") as mock_session_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_session_repo = Mock()
            mock_session_repo.get_by_id.return_value = session
            mock_session_repo_class.return_value = mock_session_repo

            result = runner.invoke(app, ["session", "info", "Test Agent", "session-1", "--table"])

        assert result.exit_code == 0
        assert "None" in result.output


# ============================================================================
# Session Trace Tests
# ============================================================================


class TestSessionTrace:
    """Tests for session trace command."""

    def test_session_trace_success(self, mock_agent, mock_trace_data):
        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.session.AgentSessionRepository") as mock_session_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_session_repo = Mock()
            mock_session_repo.get_trace.return_value = mock_trace_data
            mock_session_repo_class.return_value = mock_session_repo

            result = runner.invoke(app, ["session", "trace", "Test Agent", "session-1", "--table"])

        assert result.exit_code == 0
        assert "session" in result.output
        assert "tts" in result.output
        assert "module" in result.output
        assert "Spans: 3" in result.output
        assert "Errors: 1" in result.output
        # Span IDs should be visible in the tree
        assert "span-root" in result.output
        assert "span-child-1" in result.output
        assert "span-child-2" in result.output

    def test_session_trace_not_found(self, mock_agent):
        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.session.AgentSessionRepository") as mock_session_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_session_repo = Mock()
            mock_session_repo.get_trace.return_value = None
            mock_session_repo_class.return_value = mock_session_repo

            result = runner.invoke(app, ["session", "trace", "Test Agent", "nonexistent"])

        assert result.exit_code != 0
        assert "Trace not found" in result.output

    def test_session_trace_agent_not_found(self):
        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class:
            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = None
            mock_agent_repo_class.return_value = mock_agent_repo

            result = runner.invoke(app, ["session", "trace", "nonexistent", "session-1"])

        assert result.exit_code != 0
        assert "Agent not found" in result.output

    def test_session_trace_json_output(self, mock_agent, mock_trace_data):
        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.session.AgentSessionRepository") as mock_session_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_session_repo = Mock()
            mock_session_repo.get_trace.return_value = mock_trace_data
            mock_session_repo_class.return_value = mock_session_repo

            result = runner.invoke(app, ["session", "trace", "Test Agent", "session-1", "--json"])

        assert result.exit_code == 0
        output = json.loads(result.output)
        assert "trace" in output
        assert "spans" in output
        assert len(output["spans"]) == 3

    def test_session_trace_shows_preview(self, mock_agent, mock_trace_data):
        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.session.AgentSessionRepository") as mock_session_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_session_repo = Mock()
            mock_session_repo.get_trace.return_value = mock_trace_data
            mock_session_repo_class.return_value = mock_session_repo

            result = runner.invoke(app, ["session", "trace", "Test Agent", "session-1", "--table"])

        assert result.exit_code == 0
        assert "session" in result.output
        assert "tts" in result.output
        assert "Hello, how can I help?" in result.output


# ============================================================================
# Session Span Tests
# ============================================================================


class TestSessionSpan:
    """Tests for session span command."""

    def test_session_span_success(self, mock_agent, mock_span_data):
        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.session.AgentSessionRepository") as mock_session_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_session_repo = Mock()
            mock_session_repo.get_span.return_value = mock_span_data
            mock_session_repo_class.return_value = mock_session_repo

            result = runner.invoke(app, ["session", "span", "Test Agent", "session-1", "span-child-1", "--table"])

        assert result.exit_code == 0
        assert "tts" in result.output
        assert "span-child-1" in result.output
        assert "INTERNAL" in result.output
        assert "Hello, how can I help?" in result.output
        assert "Span Info" in result.output
        assert "Timing" in result.output
        assert "Status" in result.output
        assert "Attributes" in result.output
        assert "alloy" in result.output

    def test_session_span_not_found(self, mock_agent):
        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.session.AgentSessionRepository") as mock_session_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_session_repo = Mock()
            mock_session_repo.get_span.return_value = None
            mock_session_repo_class.return_value = mock_session_repo

            result = runner.invoke(app, ["session", "span", "Test Agent", "session-1", "nonexistent"])

        assert result.exit_code != 0
        assert "Span not found" in result.output

    def test_session_span_agent_not_found(self):
        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class:
            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = None
            mock_agent_repo_class.return_value = mock_agent_repo

            result = runner.invoke(app, ["session", "span", "nonexistent", "session-1", "span-1"])

        assert result.exit_code != 0
        assert "Agent not found" in result.output

    def test_session_span_json_output(self, mock_agent, mock_span_data):
        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.session.AgentSessionRepository") as mock_session_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_session_repo = Mock()
            mock_session_repo.get_span.return_value = mock_span_data
            mock_session_repo_class.return_value = mock_session_repo

            result = runner.invoke(app, ["session", "span", "Test Agent", "session-1", "span-child-1", "--json"])

        assert result.exit_code == 0
        output = json.loads(result.output)
        assert output["spanId"] == "span-child-1"
        assert output["name"] == "tts"
        assert output["attributes"]["voice"] == "alloy"

    def test_session_span_no_attributes(self, mock_agent):
        span_data = {
            "spanId": "span-1",
            "name": "simple-span",
            "spanKind": "INTERNAL",
            "startTime": "2024-01-01T00:00:01Z",
            "endTime": "2024-01-01T00:00:02Z",
            "durationMs": 1000,
            "statusCode": "OK",
        }

        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.session.AgentSessionRepository") as mock_session_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_session_repo = Mock()
            mock_session_repo.get_span.return_value = span_data
            mock_session_repo_class.return_value = mock_session_repo

            result = runner.invoke(app, ["session", "span", "Test Agent", "session-1", "span-1", "--table"])

        assert result.exit_code == 0
        assert "simple-span" in result.output


# ============================================================================
# Session Transcript Tests
# ============================================================================


class TestSessionTranscript:
    """Tests for session transcript command."""

    def test_session_transcript_success(self, mock_agent, mock_transcript_events):
        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.session.AgentSessionRepository") as mock_session_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_session_repo = Mock()
            mock_session_repo.get_transcript.return_value = mock_transcript_events
            mock_session_repo_class.return_value = mock_session_repo

            result = runner.invoke(app, ["session", "transcript", "Test Agent", "session-1", "--table"])

        assert result.exit_code == 0
        assert "agent" in result.output
        assert "user" in result.output
        assert "Hello, how can I help you today?" in result.output
        assert "I need help with my order." in result.output

    def test_session_transcript_json_output(self, mock_agent, mock_transcript_events):
        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.session.AgentSessionRepository") as mock_session_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_session_repo = Mock()
            mock_session_repo.get_transcript.return_value = mock_transcript_events
            mock_session_repo_class.return_value = mock_session_repo

            result = runner.invoke(app, ["session", "transcript", "Test Agent", "session-1", "--json"])

        assert result.exit_code == 0
        output = json.loads(result.output)
        assert len(output) == 3
        assert output[0]["role"] == "agent"
        assert output[1]["role"] == "user"

    def test_session_transcript_agent_not_found(self):
        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class:
            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = None
            mock_agent_repo_class.return_value = mock_agent_repo

            result = runner.invoke(app, ["session", "transcript", "nonexistent", "session-1"])

        assert result.exit_code != 0
        assert "Agent not found" in result.output

    def test_session_transcript_not_found(self, mock_agent):
        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.session.AgentSessionRepository") as mock_session_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_session_repo = Mock()
            mock_session_repo.get_transcript.return_value = None
            mock_session_repo_class.return_value = mock_session_repo

            result = runner.invoke(app, ["session", "transcript", "Test Agent", "nonexistent"])

        assert result.exit_code != 0
        assert "Transcript not found" in result.output


# ============================================================================
# Session Events Tests
# ============================================================================


class TestSessionEvents:
    """Tests for session events command."""

    def test_session_events_success(self, mock_agent, mock_events_response):
        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.session.AgentSessionRepository") as mock_session_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_session_repo = Mock()
            mock_session_repo.get_events.return_value = mock_events_response
            mock_session_repo_class.return_value = mock_session_repo

            result = runner.invoke(app, ["session", "events", "Test Agent", "session-1", "--table"])

        assert result.exit_code == 0
        assert "session.start" in result.output
        assert "tts.start" in result.output
        assert "session.end" in result.output
        assert "Page 1" in result.output
        assert "Total 3" in result.output

    def test_session_events_json_output(self, mock_agent, mock_events_response):
        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.session.AgentSessionRepository") as mock_session_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_session_repo = Mock()
            mock_session_repo.get_events.return_value = mock_events_response
            mock_session_repo_class.return_value = mock_session_repo

            result = runner.invoke(app, ["session", "events", "Test Agent", "session-1", "--json"])

        assert result.exit_code == 0
        output = json.loads(result.output)
        assert "data" in output
        assert "metadata" in output
        assert len(output["data"]) == 3

    def test_session_events_agent_not_found(self):
        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class:
            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = None
            mock_agent_repo_class.return_value = mock_agent_repo

            result = runner.invoke(app, ["session", "events", "nonexistent", "session-1"])

        assert result.exit_code != 0
        assert "Agent not found" in result.output

    def test_session_events_not_found(self, mock_agent):
        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.session.AgentSessionRepository") as mock_session_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_session_repo = Mock()
            mock_session_repo.get_events.return_value = None
            mock_session_repo_class.return_value = mock_session_repo

            result = runner.invoke(app, ["session", "events", "Test Agent", "nonexistent"])

        assert result.exit_code != 0
        assert "Events not found" in result.output


# ============================================================================
# Auto-detect Output Tests
# ============================================================================


class TestAutoDetectOutput:
    """Tests for automatic JSON output when not a TTY."""

    def test_session_list_auto_json_when_not_tty(self, mock_agent, mock_sessions):
        """Without --json or --table, non-TTY stdout should produce JSON."""
        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.session.AgentSessionRepository") as mock_session_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_session_repo = Mock()
            mock_session_repo.get.return_value = mock_sessions
            mock_session_repo_class.return_value = mock_session_repo

            result = runner.invoke(app, ["session", "list", "Test Agent"])

        assert result.exit_code == 0
        output = json.loads(result.output)
        assert len(output) == 2

    def test_session_list_table_overrides_auto_json(self, mock_agent, mock_sessions):
        """--table should force table output even when not a TTY."""
        with patch("vr_cli.commands.session.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.session.AgentSessionRepository") as mock_session_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_session_repo = Mock()
            mock_session_repo.get.return_value = mock_sessions
            mock_session_repo_class.return_value = mock_session_repo

            result = runner.invoke(app, ["session", "list", "Test Agent", "--table"])

        assert result.exit_code == 0
        assert "session-1" in result.output
        assert "2m 5s" in result.output
        # Should NOT be valid JSON
        with pytest.raises(json.JSONDecodeError):
            json.loads(result.output)


# ============================================================================
# Agent Detection Tests
# ============================================================================


class TestIsAgent:
    """Tests for is_agent() detection utility."""

    def test_no_agent_vars(self):
        with patch.dict("os.environ", {}, clear=True):
            assert is_agent() is False

    def test_ai_agent_var(self):
        with patch.dict("os.environ", {"AI_AGENT": "1"}, clear=True):
            assert is_agent() is True

    def test_agent_var(self):
        with patch.dict("os.environ", {"AGENT": "goose"}, clear=True):
            assert is_agent() is True

    def test_claudecode_var(self):
        with patch.dict("os.environ", {"CLAUDECODE": "1"}, clear=True):
            assert is_agent() is True

    def test_cursor_agent_var(self):
        with patch.dict("os.environ", {"CURSOR_AGENT": "1"}, clear=True):
            assert is_agent() is True

    def test_gemini_cli_var(self):
        with patch.dict("os.environ", {"GEMINI_CLI": "1"}, clear=True):
            assert is_agent() is True

    def test_codex_sandbox_var(self):
        with patch.dict("os.environ", {"CODEX_SANDBOX": "seatbelt"}, clear=True):
            assert is_agent() is True

    def test_cline_active_var(self):
        with patch.dict("os.environ", {"CLINE_ACTIVE": "true"}, clear=True):
            assert is_agent() is True

    def test_unrelated_vars_ignored(self):
        with patch.dict("os.environ", {"HOME": "/home/user", "PATH": "/usr/bin"}, clear=True):
            assert is_agent() is False


# ============================================================================
# Help Tests
# ============================================================================


class TestSessionHelp:
    """Tests for session command help output."""

    def test_session_help(self):
        result = runner.invoke(app, ["session", "--help"])
        assert result.exit_code == 0
        assert "list" in result.output
        assert "info" in result.output
        assert "trace" in result.output
        assert "span" in result.output
        assert "transcript" in result.output
        assert "events" in result.output

    def test_session_list_help(self):
        result = runner.invoke(app, ["session", "list", "--help"])
        assert result.exit_code == 0
        assert "Agent name or ID" in result.output

    def test_session_info_help(self):
        result = runner.invoke(app, ["session", "info", "--help"])
        assert result.exit_code == 0
        assert "Session ID" in result.output

    def test_session_trace_help(self):
        result = runner.invoke(app, ["session", "trace", "--help"])
        assert result.exit_code == 0
        assert "Session ID" in result.output

    def test_session_span_help(self):
        result = runner.invoke(app, ["session", "span", "--help"])
        assert result.exit_code == 0
        assert "Span ID" in result.output

    def test_session_transcript_help(self):
        result = runner.invoke(app, ["session", "transcript", "--help"])
        assert result.exit_code == 0
        assert "Session ID" in result.output

    def test_session_events_help(self):
        result = runner.invoke(app, ["session", "events", "--help"])
        assert result.exit_code == 0
        assert "Session ID" in result.output
