"""原生 claude-code 执行车道(claude_code_native)单测。

覆盖:命令构造、stream-json 事件 → 平台事件映射、usage/session 抽取、401 识别。
"""
from __future__ import annotations

import pytest

from lambdagent.providers import claude_code_native as ncc
from lambdagent.providers.claude_code_native import (
    build_cmd, _handle_event, NativeResult,
)


class TestBuildCmd:
    def test_native_enables_tools_no_react_neuter(self):
        cmd = build_cmd("claude", "task", cwd="/w", model="sonnet")
        assert "--tools" not in cmd                 # 不阉割原生工具
        assert "--allowedTools" in cmd
        for t in ("Read", "Write", "Edit", "Bash", "Glob", "Grep"):
            assert t in cmd
        assert cmd[cmd.index("--add-dir") + 1] == "/w"
        assert "--strict-mcp-config" in cmd          # 默认隔离 MCP
        assert "bypassPermissions" in cmd
        assert "stream-json" in cmd

    def test_resume_adds_flag(self):
        cmd = build_cmd("claude", "t", cwd="/w", model="sonnet", session_id="s-1")
        assert "--resume" in cmd and "s-1" in cmd

    def test_no_resume_when_none(self):
        cmd = build_cmd("claude", "t", cwd="/w", model="sonnet", session_id=None)
        assert "--resume" not in cmd

    def test_allow_mcp_drops_strict(self):
        cmd = build_cmd("claude", "t", cwd="/w", model="sonnet", strict_mcp=False)
        assert "--strict-mcp-config" not in cmd


class TestHandleEvent:
    def _collect(self, events):
        out = []
        res = NativeResult()
        for e in events:
            _handle_event(e, lambda ev: out.append(ev), res)
        return out, res

    def test_session_init_captured(self):
        _, res = self._collect([{"type": "system", "subtype": "init", "session_id": "sess-X"}])
        assert res.session_id == "sess-X"

    def test_text_and_tool_use_mapped(self):
        events = [
            {"type": "assistant", "message": {"content": [
                {"type": "text", "text": "thinking…"},
                {"type": "tool_use", "name": "Write",
                 "input": {"file_path": "/w/out.md", "content": "x"}},
            ]}},
        ]
        out, res = self._collect(events)
        kinds = [e["event"] for e in out]
        assert "think_chunk" in kinds and "tool_call" in kinds
        tc = next(e for e in out if e["event"] == "tool_call")
        assert tc["tool"] == "Write"
        assert any(t["tool"] == "Write" for t in res.trace)

    def test_tool_result_mapped(self):
        events = [{"type": "user", "message": {"content": [
            {"type": "tool_result", "content": "wrote 1 file"}]}}]
        out, _ = self._collect(events)
        assert out and out[0]["event"] == "tool_result"
        assert "wrote 1 file" in out[0]["output"]

    def test_result_extracts_usage_cost_session(self):
        ev = {"type": "result", "subtype": "success", "is_error": False,
              "result": "done, wrote paper.md", "session_id": "sess-Y",
              "num_turns": 12, "total_cost_usd": 0.34,
              "usage": {"input_tokens": 1000, "output_tokens": 500,
                        "cache_read_input_tokens": 200,
                        "cache_creation_input_tokens": 50}}
        _, res = self._collect([ev])
        assert res.text == "done, wrote paper.md"
        assert res.session_id == "sess-Y"
        assert res.num_turns == 12
        assert res.cost_usd == pytest.approx(0.34)
        assert res.input_tokens == 1000 and res.output_tokens == 500
        assert res.cache_read_tokens == 200 and res.cache_creation_tokens == 50
        assert res.is_error is False

    def test_result_is_error_401_detectable(self):
        ev = {"type": "result", "is_error": True,
              "result": "Failed to authenticate. API Error: 401 Invalid authentication credentials"}
        _, res = self._collect([ev])
        assert res.is_error is True
        assert ncc._is_auth_error(res.text)   # run_native 会据此抛 _AUTH_HINT


class TestPendingToolsGate:
    """工具在飞时禁 idle 超时(防长 Bash/编译被误杀)。"""

    def test_tool_use_increments_result_decrements(self):
        res = NativeResult()
        _handle_event({"type": "assistant", "message": {"content": [
            {"type": "tool_use", "name": "Bash", "input": {"command": "python sim.py"}}]}},
            None, res)
        assert res.pending_tools == 1            # Bash 开飞 → 禁 idle
        _handle_event({"type": "user", "message": {"content": [
            {"type": "tool_result", "content": "done"}]}}, None, res)
        assert res.pending_tools == 0            # 结果回来 → 恢复 idle

    def test_parallel_tools_counted(self):
        res = NativeResult()
        _handle_event({"type": "assistant", "message": {"content": [
            {"type": "tool_use", "name": "Bash", "input": {}},
            {"type": "tool_use", "name": "Read", "input": {}}]}}, None, res)
        assert res.pending_tools == 2
        _handle_event({"type": "user", "message": {"content": [
            {"type": "tool_result", "content": "a"}]}}, None, res)
        assert res.pending_tools == 1            # 还有一个在飞,仍禁 idle
