"""
测试 CLI 行为：
1. 不指定目录时默认当前目录（is_cli_mode）
2. 直接运行 main.py 时保持时间戳目录默认
3. aacode 无子命令时默认等同于 aacode run
4. 未知子命令视为 task，等同于 aacode run
"""
import os
import sys
from pathlib import Path
from unittest.mock import patch, MagicMock

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

import pytest


class TestCliDispatch:
    """测试 cli.main() 分发逻辑"""

    def test_bare_aacode_defaults_to_run(self):
        """aacode（无参数）应等价于 aacode run"""
        from aacode import cli

        with patch.object(cli, "run_main_async") as mock_run:
            with patch.object(sys, "argv", ["aacode"]):
                cli.main()
            mock_run.assert_called_once_with([])

    def test_unknown_command_treated_as_run(self):
        """未知子命令应视为 task，等价于 aacode run <task>"""
        from aacode import cli

        with patch.object(cli, "run_main_async") as mock_run:
            with patch.object(sys, "argv", ["aacode", "hello", "world"]):
                cli.main()
            mock_run.assert_called_once_with(["hello", "world"])

    def test_run_command_delegates_properly(self):
        """aacode run <args> 正确传递参数"""
        from aacode import cli

        with patch.object(cli, "run_main_async") as mock_run:
            with patch.object(sys, "argv", ["aacode", "run", "-p", "/tmp/test", "task desc"]):
                cli.main()
            mock_run.assert_called_once_with(["-p", "/tmp/test", "task desc"])

    def test_help_flag_prints_without_running(self):
        """--help 应打印帮助信息而不是执行 run"""
        from aacode import cli

        with patch.object(cli, "run_main_async") as mock_run:
            with patch("builtins.print") as mock_print:
                with patch.object(sys, "argv", ["aacode", "--help"]):
                    cli.main()
            mock_run.assert_not_called()
            mock_print.assert_called()

    def test_init_command_not_treated_as_run(self):
        """aacode init 应执行初始化而不是 run"""
        from aacode import cli

        with patch.object(cli, "run_main_async") as mock_run:
            with patch.object(cli, "is_pip_installed_mode", return_value=True):
                with patch.object(cli, "run_init_pip") as mock_init:
                    with patch.object(sys, "argv", ["aacode", "init"]):
                        cli.main()
            mock_run.assert_not_called()
            mock_init.assert_called_once()


class TestProjectDefaultCliMode:
    """测试 is_cli_mode 检测下 -p 的默认值"""

    def test_cli_mode_default_is_dot(self):
        """CLI 模式（sys.argv[0] == 'aacode'）默认 -p 为 '.'"""
        import aacode.main as main_mod
        import argparse

        with patch.object(sys, "argv", ["aacode"]):
            parser = argparse.ArgumentParser()
            parser.add_argument("-p", "--project", default=".")
            parser.add_argument("task", nargs="*")
            args = parser.parse_args([])
            assert args.project == "."

    def test_cli_mode_is_detected_from_argv0(self):
        """is_cli_mode 在 sys.argv[0]=='aacode' 时为 True"""
        with patch.object(sys, "argv", ["aacode"]):
            is_cli = Path(sys.argv[0]).name == "aacode"
            assert is_cli is True

    def test_direct_mode_is_detected_from_argv0(self):
        """is_cli_mode 在 sys.argv[0]=='main.py' 时为 False"""
        with patch.object(sys, "argv", ["main.py"]):
            is_cli = Path(sys.argv[0]).name == "aacode"
            assert is_cli is False

    def test_direct_mode_path_with_directory(self):
        """sys.argv[0]=='aacode/main.py' 时 is_cli_mode 为 False"""
        with patch.object(sys, "argv", ["aacode/main.py"]):
            is_cli = Path(sys.argv[0]).name == "aacode"
            assert is_cli is False

    def test_absolute_path_not_treated_as_cli(self):
        """绝对路径 /path/to/main.py 时 is_cli_mode 为 False"""
        with patch.object(sys, "argv", ["/Users/test/aacode/main.py"]):
            is_cli = Path(sys.argv[0]).name == "aacode"
            assert is_cli is False


class TestRunMainAsyncSetsArgv:
    """测试 run_main_async 正确设置 sys.argv[0]"""

    def test_run_main_async_sets_argv0_to_aacode(self):
        """run_main_async 将 sys.argv[0] 设为 'aacode'"""
        from aacode.cli import run_main_async

        original_argv = sys.argv[:]
        try:
            assert "aacode" not in sys.argv[0]
        except AssertionError:
            pass

        with patch("aacode.cli.asyncio.run") as mock_asyncio_run:
            mock_asyncio_run.side_effect = lambda coro: coro.close()  # 关闭未 await 的协程，避免 RuntimeWarning
            run_main_async(["some", "args"])
            assert mock_asyncio_run.called


class TestInteractiveMode:
    """测试无任务时直接进入交互会话模式"""

    def test_empty_task_enters_continue_session(self):
        """bare aacode 无任务时，应进入 continue_session 而非 run task"""
        from aacode import main as main_mod

        mock_coder = MagicMock()
        mock_coder.run = MagicMock()

        with patch.object(main_mod, "AICoder", return_value=mock_coder) as mock_ai:
            with patch.object(main_mod, "continue_session") as mock_continue:
                with patch.object(main_mod, "_setup_httpx_logging"):
                    with patch.object(sys, "argv", ["aacode", "-p", "/tmp/test"]):
                        with patch("builtins.input", return_value=""):
                            import asyncio
                            loop = asyncio.new_event_loop()
                            try:
                                loop.run_until_complete(main_mod.main())
                            finally:
                                loop.close()

            mock_continue.assert_called_once()
            mock_coder.run.assert_not_called()

    def test_with_task_runs_normally(self):
        """有任务时正常执行 coder.run，不跳过"""
        from aacode import main as main_mod

        mock_coder = MagicMock()
        mock_coder.run = AsyncMock(return_value={"session_id": "test", "iterations": 1, "status": "ok", "execution_time": 1.0})

        with patch.object(main_mod, "AICoder", return_value=mock_coder):
            with patch.object(main_mod, "continue_session") as mock_continue:
                with patch.object(main_mod, "_setup_httpx_logging"):
                    with patch.object(sys, "argv", ["aacode", "-p", "/tmp/test", "a task"]):
                        with patch("builtins.input", side_effect=["n"]):
                            import asyncio
                            loop = asyncio.new_event_loop()
                            try:
                                loop.run_until_complete(main_mod.main())
                            finally:
                                loop.close()

            mock_coder.run.assert_called_once_with("a task")


class AsyncMock(MagicMock):
    async def __call__(self, *args, **kwargs):
        return super(AsyncMock, self).__call__(*args, **kwargs)
