# tests/test_run_shell_stdin.py
"""
run_shell stdin_input 功能测试 + DEVNULL 防阻塞测试
"""

import asyncio
import pytest
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent.parent))

from aacode.tools.atomic_tools import AtomicTools


class MockSafetyGuard:
    """测试用安全护栏"""
    def check_command(self, command):
        return {"allowed": True}

    def is_safe_path(self, path):
        return True


@pytest.fixture
def tools(tmp_path):
    """创建 AtomicTools 实例"""
    return AtomicTools(tmp_path, MockSafetyGuard())


@pytest.fixture
def script_with_input(tmp_path):
    """创建一个需要用户输入的 Python 脚本"""
    script = tmp_path / "ask_input.py"
    script.write_text(
        'name = input("What is your name? ")\n'
        'print(f"Hello, {name}!")\n',
        encoding="utf-8",
    )
    return script


@pytest.fixture
def script_multi_input(tmp_path):
    """创建一个需要多次输入的 Python 脚本"""
    script = tmp_path / "calculator.py"
    script.write_text(
        'a = int(input("Enter a: "))\n'
        'b = int(input("Enter b: "))\n'
        'print(f"{a} + {b} = {a + b}")\n',
        encoding="utf-8",
    )
    return script


@pytest.fixture
def script_no_input(tmp_path):
    """创建一个不需要输入的 Python 脚本"""
    script = tmp_path / "hello.py"
    script.write_text('print("Hello, World!")\n', encoding="utf-8")
    return script


class TestStdinInput:
    """测试 stdin_input 参数"""

    @pytest.mark.asyncio
    async def test_single_input(self, tools, script_with_input):
        """单次输入"""
        result = await tools.run_shell(
            command=f"python3 {script_with_input}",
            stdin_input="Alice\n",
        )
        assert result["success"] is True
        assert result["returncode"] == 0
        assert "Hello, Alice!" in result["stdout"]

    @pytest.mark.asyncio
    async def test_multi_input(self, tools, script_multi_input):
        """多次输入"""
        result = await tools.run_shell(
            command=f"python3 {script_multi_input}",
            stdin_input="5\n3\n",
        )
        assert result["success"] is True
        assert result["returncode"] == 0
        assert "5 + 3 = 8" in result["stdout"]

    @pytest.mark.asyncio
    async def test_no_input_normal_script(self, tools, script_no_input):
        """不传 stdin_input 时，普通脚本正常运行"""
        result = await tools.run_shell(
            command=f"python3 {script_no_input}",
        )
        assert result["success"] is True
        assert result["returncode"] == 0
        assert "Hello, World!" in result["stdout"]

    @pytest.mark.asyncio
    async def test_empty_string_input(self, tools, script_with_input):
        """传空字符串 stdin_input 不应阻塞（空字符串是 falsy，走 DEVNULL）"""
        result = await tools.run_shell(
            command=f"python3 {script_with_input}",
            stdin_input="",
            timeout=5,
        )
        assert result["success"] is True
        # 空 stdin → DEVNULL → EOFError → 非零退出码
        assert result["returncode"] != 0


class TestDevnullPreventsBlocking:
    """测试 DEVNULL 防阻塞"""

    @pytest.mark.asyncio
    async def test_input_without_stdin_input_does_not_block(self, tools, script_with_input):
        """不传 stdin_input 时，有 input() 的程序不应阻塞（应快速 EOF）"""
        result = await tools.run_shell(
            command=f"python3 {script_with_input}",
            timeout=5,
        )
        assert result["success"] is True
        # DEVNULL → input() 收到 EOF → EOFError → 非零退出码
        assert result["returncode"] != 0
        assert "EOFError" in result["stderr"] or "EOF" in result["stderr"]

    @pytest.mark.asyncio
    async def test_no_timeout_on_devnull(self, tools, script_with_input):
        """DEVNULL 应该让进程快速退出，不触发超时"""
        import time
        start = time.time()
        result = await tools.run_shell(
            command=f"python3 {script_with_input}",
            timeout=10,
        )
        elapsed = time.time() - start
        # 应该在 1 秒内完成，而不是等 10 秒超时
        assert elapsed < 3.0
        assert result["success"] is True
        assert "timeout" not in result


class TestBackwardCompatibility:
    """向后兼容测试"""

    @pytest.mark.asyncio
    async def test_kwargs_ignored(self, tools, script_no_input):
        """额外的 kwargs 应被忽略"""
        result = await tools.run_shell(
            command=f"python3 {script_no_input}",
            some_random_param="ignored",
        )
        assert result["success"] is True
        assert "Hello, World!" in result["stdout"]

    @pytest.mark.asyncio
    async def test_echo_pipe_still_works(self, tools):
        """管道方式传输入仍然有效"""
        result = await tools.run_shell(
            command='echo "hello" | cat',
        )
        assert result["success"] is True
        assert "hello" in result["stdout"]

    @pytest.mark.asyncio
    async def test_timeout_still_works(self, tools):
        """超时机制仍然有效"""
        result = await tools.run_shell(
            command="sleep 10",
            timeout=2,
        )
        assert result["success"] is True
        assert result.get("timeout") is True
