"""Tests for streaming render functions in talk client.

Tests the _render_stream_tool_call function with human and JSON modes.
"""

import io
import json
import sys
from unittest.mock import MagicMock

import pytest
from rich.console import Console

from anima.shell.cli_commands.talk_streaming import SSEEvent, _render_stream_tool_call


class TestRenderStreamToolCall:
    """Tests for _render_stream_tool_call function."""

    def test_tool_call_json_output(self):
        """JSON mode outputs machine-readable JSON to console.file."""
        # Create a StringIO to capture output
        output = io.StringIO()

        # Create a console that writes to our StringIO
        console = Console(file=output, force_terminal=False)

        # Create a tool_call event
        event = SSEEvent(
            event="tool_call",
            data={
                "turn": 1,
                "tool_name": "bash",
                "tool_arg_preview": "ls -la",
            },
        )

        # Render in JSON mode
        _render_stream_tool_call(
            event=event,
            agent_id="test-agent",
            json_output=True,
            console=console,
        )

        # Parse the JSON output
        output.seek(0)
        result = json.loads(output.read())

        assert result["event"] == "tool_call"
        assert result["agent_id"] == "test-agent"
        assert result["turn"] == 1
        assert result["tool_name"] == "bash"
        assert result["tool_arg_preview"] == "ls -la"

    def test_tool_call_human_output(self):
        """Human mode outputs styled text to stdout."""
        # Create a StringIO to capture stdout
        original_stdout = sys.stdout
        sys.stdout = output = io.StringIO()

        try:
            # Create a console that writes to stdout
            console = Console(file=output, force_terminal=True)

            # Create a tool_call event
            event = SSEEvent(
                event="tool_call",
                data={
                    "turn": 2,
                    "tool_name": "read_file",
                    "tool_arg_preview": "path/to/file.txt",
                },
            )

            # Render in human mode
            _render_stream_tool_call(
                event=event,
                agent_id="test-agent",
                json_output=False,
                console=console,
            )

            # Get the output
            output.flush()
            result = output.getvalue()

            # Check that the tool name appears in output
            assert "read_file" in result
            assert "path/to/file.txt" in result
            # Human mode should have the dim yellow styling marker
            assert "◆" in result or "tool_call" in result.lower()
        finally:
            sys.stdout = original_stdout

    def test_tool_call_json_output_with_missing_fields(self):
        """JSON mode handles missing optional fields gracefully."""
        output = io.StringIO()
        console = Console(file=output, force_terminal=False)

        # Event with only required fields
        event = SSEEvent(
            event="tool_call",
            data={"turn": 3},
        )

        _render_stream_tool_call(
            event=event,
            agent_id="test-agent",
            json_output=True,
            console=console,
        )

        output.seek(0)
        result = json.loads(output.read())

        assert result["event"] == "tool_call"
        assert result["turn"] == 3
        assert result["tool_name"] == ""
        assert result["tool_arg_preview"] == ""

    def test_tool_call_human_output_with_empty_preview(self):
        """Human mode handles empty preview gracefully."""
        original_stdout = sys.stdout
        sys.stdout = output = io.StringIO()

        try:
            console = Console(file=output, force_terminal=True)

            event = SSEEvent(
                event="tool_call",
                data={
                    "turn": 4,
                    "tool_name": "search",
                    "tool_arg_preview": "",
                },
            )

            _render_stream_tool_call(
                event=event,
                agent_id="test-agent",
                json_output=False,
                console=console,
            )

            output.flush()
            result = output.getvalue()

            # Should still show the tool name
            assert "search" in result
        finally:
            sys.stdout = original_stdout

    def test_tool_call_with_long_arg_preview(self):
        """JSON mode handles very long arg previews."""
        output = io.StringIO()
        console = Console(file=output, force_terminal=False)

        long_preview = "x" * 500
        event = SSEEvent(
            event="tool_call",
            data={
                "turn": 5,
                "tool_name": "bash",
                "tool_arg_preview": long_preview,
            },
        )

        _render_stream_tool_call(
            event=event,
            agent_id="test-agent",
            json_output=True,
            console=console,
        )

        output.seek(0)
        result = json.loads(output.read())

        # JSON should contain the full preview (no truncation in JSON mode)
        assert len(result["tool_arg_preview"]) == 500

    def test_tool_call_human_output_clears_line(self):
        """Human mode clears the line before rendering."""
        original_stdout = sys.stdout
        sys.stdout = output = io.StringIO()

        try:
            console = Console(file=output, force_terminal=True)

            event = SSEEvent(
                event="tool_call",
                data={
                    "turn": 6,
                    "tool_name": "test",
                    "tool_arg_preview": "args",
                },
            )

            _render_stream_tool_call(
                event=event,
                agent_id="test-agent",
                json_output=False,
                console=console,
            )

            output.flush()
            result = output.getvalue()

            # Should have carriage return and clear escape sequence
            assert "\r" in result
            assert "\033[K" in result
        finally:
            sys.stdout = original_stdout
