"""Regression tests for Issue #278.

Bug: Claude runtime — command status stuck 'running' and tool output
toggle always empty.

Two root causes:
1. evt_type check was "assistant" but Claude CLI emits tool_result blocks in "user"
   role messages. The wrong type meant result events were never emitted, leaving the
   command indicator in "running" state permanently.
2. The result event was missing "status" and "output" fields, so the expand toggle
   showed nothing even if the event were somehow emitted.
"""

import json
import os
import sys
import unittest

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)) + "/..")
os.environ.setdefault("API_SHARED_KEY", "test_key_123")

from agent_manager import _parse_claude_stream_json_line  # noqa: E402


def _make_stream_event(inner: dict) -> str:
    """Wrap an inner event in the Claude CLI stream_event envelope."""
    return json.dumps({"type": "stream_event", "event": inner})


def _make_user_tool_result(tool_use_id: str, content, is_error: bool = False) -> str:
    """Emit a Claude CLI user message containing a tool_result block."""
    return json.dumps(
        {
            "type": "user",
            "message": {
                "role": "user",
                "content": [
                    {
                        "type": "tool_result",
                        "tool_use_id": tool_use_id,
                        "content": content,
                        "is_error": is_error,
                    }
                ],
            },
        }
    )


def _make_assistant_tool_result(tool_use_id: str, content) -> str:
    """Old (buggy) envelope — tool_result inside an 'assistant' message."""
    return json.dumps(
        {
            "type": "assistant",
            "message": {
                "role": "assistant",
                "content": [
                    {
                        "type": "tool_result",
                        "tool_use_id": tool_use_id,
                        "content": content,
                    }
                ],
            },
        }
    )


def _parse_lines(lines: list) -> list:
    """Call the real agent_manager parser on a sequence of raw lines.

    Uses _parse_claude_stream_json_line from agent_manager.py directly so
    tests exercise the production code path and will fail if the fix is
    reverted.
    """
    active_tool_calls: dict = {}
    events = []
    for raw_line in lines:
        for _ch, ev in _parse_claude_stream_json_line(raw_line, active_tool_calls):
            if _ch in ("tool_call",):
                events.append(ev)
    return events


class TestIssue278ClaudeToolCallStatus(unittest.TestCase):
    """Regression tests for Issue #278: Claude runtime tool call status bugs."""

    # ------------------------------------------------------------------ #
    # Bug 1: evt_type "user" (correct) vs "assistant" (old bug)           #
    # ------------------------------------------------------------------ #

    def test_tool_result_in_user_message_emits_result_event(self):
        """Tool result in user message → result event emitted (was broken)."""
        lines = [
            _make_stream_event(
                {
                    "type": "content_block_start",
                    "index": 0,
                    "content_block": {
                        "type": "tool_use",
                        "id": "toolu_abc123",
                        "name": "bash",
                    },
                }
            ),
            _make_stream_event({"type": "content_block_stop", "index": 0}),
            _make_user_tool_result("toolu_abc123", "hello world"),
        ]
        events = _parse_lines(lines)
        result_events = [e for e in events if e["event"] == "result"]
        self.assertEqual(len(result_events), 1, "Expected exactly one result event")

    def test_tool_result_in_assistant_message_does_not_emit_result(self):
        """Tool result in assistant message (old bug) → no result event."""
        lines = [
            _make_assistant_tool_result("toolu_abc123", "hello world"),
        ]
        events = _parse_lines(lines)
        result_events = [e for e in events if e["event"] == "result"]
        self.assertEqual(
            len(result_events),
            0,
            "assistant-wrapped tool_result should not emit a result event",
        )

    # ------------------------------------------------------------------ #
    # Bug 2: result event must include status and output fields           #
    # ------------------------------------------------------------------ #

    def test_result_event_has_status_completed(self):
        """Successful tool result → status == 'completed'."""
        lines = [
            _make_stream_event(
                {
                    "type": "content_block_start",
                    "index": 0,
                    "content_block": {
                        "type": "tool_use",
                        "id": "toolu_1",
                        "name": "bash",
                    },
                }
            ),
            _make_stream_event({"type": "content_block_stop", "index": 0}),
            _make_user_tool_result("toolu_1", "output text"),
        ]
        events = _parse_lines(lines)
        result_events = [e for e in events if e["event"] == "result"]
        self.assertEqual(result_events[0]["status"], "completed")

    def test_result_event_has_output_string(self):
        """Tool output (string content) → output field populated."""
        lines = [
            _make_stream_event(
                {
                    "type": "content_block_start",
                    "index": 0,
                    "content_block": {
                        "type": "tool_use",
                        "id": "toolu_2",
                        "name": "bash",
                    },
                }
            ),
            _make_stream_event({"type": "content_block_stop", "index": 0}),
            _make_user_tool_result("toolu_2", "file contents here"),
        ]
        events = _parse_lines(lines)
        result_events = [e for e in events if e["event"] == "result"]
        self.assertEqual(result_events[0]["output"], "file contents here")

    def test_result_event_output_extracted_from_content_array(self):
        """Tool output in content-block array format → text extracted correctly."""
        content_array = [
            {"type": "text", "text": "part1"},
            {"type": "text", "text": " part2"},
        ]
        lines = [
            _make_stream_event(
                {
                    "type": "content_block_start",
                    "index": 0,
                    "content_block": {
                        "type": "tool_use",
                        "id": "toolu_3",
                        "name": "read",
                    },
                }
            ),
            _make_stream_event({"type": "content_block_stop", "index": 0}),
            _make_user_tool_result("toolu_3", content_array),
        ]
        events = _parse_lines(lines)
        result_events = [e for e in events if e["event"] == "result"]
        self.assertIn("part1", result_events[0]["output"])
        self.assertIn("part2", result_events[0]["output"])

    def test_error_tool_result_has_status_error(self):
        """Failed tool call → status == 'error' and is_error == True."""
        lines = [
            _make_stream_event(
                {
                    "type": "content_block_start",
                    "index": 0,
                    "content_block": {
                        "type": "tool_use",
                        "id": "toolu_4",
                        "name": "bash",
                    },
                }
            ),
            _make_stream_event({"type": "content_block_stop", "index": 0}),
            _make_user_tool_result("toolu_4", "Permission denied", is_error=True),
        ]
        events = _parse_lines(lines)
        result_events = [e for e in events if e["event"] == "result"]
        self.assertEqual(result_events[0]["status"], "error")
        self.assertTrue(result_events[0]["is_error"])

    def test_result_event_id_matches_tool_use_id(self):
        """Result event id matches the tool_use_id from the tool_result block."""
        lines = [
            _make_stream_event(
                {
                    "type": "content_block_start",
                    "index": 0,
                    "content_block": {
                        "type": "tool_use",
                        "id": "toolu_xyz",
                        "name": "bash",
                    },
                }
            ),
            _make_stream_event({"type": "content_block_stop", "index": 0}),
            _make_user_tool_result("toolu_xyz", "output"),
        ]
        events = _parse_lines(lines)
        result_events = [e for e in events if e["event"] == "result"]
        self.assertEqual(result_events[0]["id"], "toolu_xyz")

    def test_result_event_output_truncated_at_500_chars(self):
        """Long tool output → output truncated to 500 chars."""
        long_output = "x" * 1000
        lines = [
            _make_stream_event(
                {
                    "type": "content_block_start",
                    "index": 0,
                    "content_block": {
                        "type": "tool_use",
                        "id": "toolu_5",
                        "name": "bash",
                    },
                }
            ),
            _make_stream_event({"type": "content_block_stop", "index": 0}),
            _make_user_tool_result("toolu_5", long_output),
        ]
        events = _parse_lines(lines)
        result_events = [e for e in events if e["event"] == "result"]
        self.assertLessEqual(len(result_events[0]["output"]), 500)

    def test_full_tool_call_lifecycle_emits_start_input_complete_result(self):
        """Full Claude tool call sequence emits start → input_complete → result."""
        lines = [
            _make_stream_event(
                {
                    "type": "content_block_start",
                    "index": 0,
                    "content_block": {
                        "type": "tool_use",
                        "id": "toolu_lifecycle",
                        "name": "bash",
                    },
                }
            ),
            _make_stream_event({"type": "content_block_stop", "index": 0}),
            _make_user_tool_result("toolu_lifecycle", "tool ran fine"),
        ]
        events = _parse_lines(lines)
        event_types = [e["event"] for e in events]
        self.assertIn("start", event_types)
        self.assertIn("input_complete", event_types)
        self.assertIn("result", event_types)
        # result must come after input_complete
        self.assertGreater(
            event_types.index("result"), event_types.index("input_complete")
        )


if __name__ == "__main__":
    unittest.main()
