from unittest.mock import patch

import pytest

from decepticon.sandbox_kernel.tmux import TmuxCommandError, TmuxSessionManager


@pytest.mark.parametrize("late_setup_prompt", [False, True])
def test_first_command_returns_its_output_when_initial_export_is_delayed(
    late_setup_prompt: bool,
) -> None:
    # Given: a shell processes one queued command per poll, including the export.
    manager = TmuxSessionManager("init-env-regression", "ctn")
    pending: list[str] = []
    screen = "[DCPTN:0:/workspace] "

    def send(command: str, enter: bool = True) -> None:
        pending.append(command)

    def advance_shell(seconds: float) -> None:
        nonlocal screen, late_setup_prompt
        if late_setup_prompt:
            screen += "[DCPTN:0:/workspace] "
            late_setup_prompt = False
            return
        if pending:
            command = pending.pop(0)
            output = (
                "first-command-result\n" if "run-probe" in command else f"\n{command.split()[-1]}\n"
            )
            screen += f"{command}\n{output}[DCPTN:0:/workspace] "

    def native_state(args: list[str], timeout: int = 10) -> str:
        if args[-1] == "#{pane_pid}":
            return "123"
        record = f"{manager._generation}:123:0" if not pending else ""
        return f"123\t{record}:/workspace"

    with (
        patch.object(manager, "_tmux", side_effect=native_state),
        patch.object(manager, "_cached_pane_is_alive", return_value=False),
        patch.object(manager, "_ensure_session", return_value=True),
        patch.object(manager, "_inject_ps1_marker"),
        patch.object(manager, "_clear_screen"),
        patch.object(manager, "_capture", side_effect=lambda: screen),
        patch.object(manager, "_send", side_effect=send),
        patch("decepticon.sandbox_kernel.tmux.time.sleep", side_effect=advance_shell),
        patch(
            "decepticon.sandbox_kernel.tmux._allowed_passthrough_env",
            return_value={"DECEPTICON_BROWSER_TEST": "fixture"},
        ),
    ):
        try:
            # When: the first command runs immediately after initialization.
            result = manager.execute("run-probe", is_input=False, timeout=10)
        finally:
            TmuxSessionManager._initialized.discard(manager.session)

    # Then: completion belongs to the user command, with no export echo.
    assert result == "first-command-result\n[cwd: /workspace]\n[DCPTN_COMPLETE exit=0]"


def test_first_command_is_not_sent_when_initial_export_times_out() -> None:
    manager = TmuxSessionManager("init-env-timeout", "ctn")
    with (
        patch.object(manager, "_cached_pane_is_alive", return_value=False),
        patch.object(manager, "_ensure_session", return_value=True),
        patch.object(manager, "_inject_ps1_marker"),
        patch.object(manager, "_capture", return_value="[DCPTN:0:/workspace] "),
        patch.object(manager, "_send") as send,
        patch("decepticon.sandbox_kernel.tmux.time.monotonic", side_effect=[0.0, 10.0]),
        patch(
            "decepticon.sandbox_kernel.tmux._allowed_passthrough_env",
            return_value={"DECEPTICON_BROWSER_TEST": "fixture"},
        ),
    ):
        with pytest.raises(TmuxCommandError):
            manager.execute("run-probe", is_input=False, timeout=10)

    assert all(call.args[0] != "run-probe" for call in send.call_args_list)
    assert manager.session not in TmuxSessionManager._initialized
