from __future__ import annotations

import shlex
import sys
from pathlib import Path

from evals.patchers.command_patcher import CommandPatcher, patcher_from_env


def _python_cmd(script: str) -> str:
    return f"{shlex.quote(sys.executable)} -c {shlex.quote(script)}"


def test_command_patcher_argv_mode_success(tmp_path: Path) -> None:
    context_dir = tmp_path / "ctx"
    context_dir.mkdir()
    diff_text = "diff --git a/demo.py b/demo.py\\n"
    patcher = CommandPatcher(
        cmd=_python_cmd(f"import sys; sys.stdout.write({diff_text!r})"),
        shell=False,
        timeout_sec=5.0,
    )

    result = patcher.propose_patch(prompt="ignored", context_dir=context_dir, cwd=tmp_path)

    assert result.diff == diff_text.strip()
    assert result.meta is not None
    assert result.meta.get("returncode") == 0
    assert result.meta.get("timed_out") is False
    assert float(result.meta.get("timeout_sec") or 0.0) == 5.0


def test_command_patcher_timeout_returns_structured_error(tmp_path: Path) -> None:
    context_dir = tmp_path / "ctx"
    context_dir.mkdir()
    patcher = CommandPatcher(
        cmd=_python_cmd("import time; time.sleep(0.5)"),
        shell=False,
        timeout_sec=0.05,
    )

    result = patcher.propose_patch(prompt="ignored", context_dir=context_dir, cwd=tmp_path)

    assert result.diff is None
    assert result.meta is not None
    assert result.meta.get("error") == "timeout"
    assert result.meta.get("timed_out") is True
    assert float(result.meta.get("timeout_sec") or 0.0) == 0.05


def test_command_patcher_shell_mode_expands_environment(tmp_path: Path) -> None:
    context_dir = tmp_path / "ctx"
    context_dir.mkdir()
    patcher = CommandPatcher(
        cmd="echo $LLMDEBUG_EVAL_CONTEXT_DIR",
        shell=True,
        timeout_sec=2.0,
    )

    result = patcher.propose_patch(prompt="ignored", context_dir=context_dir, cwd=tmp_path)

    assert result.meta is not None
    assert result.meta.get("returncode") == 0
    assert result.meta.get("timed_out") is False
    assert result.diff == str(context_dir)


def test_command_patcher_missing_binary_returns_structured_error(tmp_path: Path) -> None:
    context_dir = tmp_path / "ctx"
    context_dir.mkdir()
    patcher = CommandPatcher(
        cmd="definitely-not-a-real-command --version",
        shell=False,
        timeout_sec=2.0,
    )

    result = patcher.propose_patch(prompt="ignored", context_dir=context_dir, cwd=tmp_path)

    assert result.diff is None
    assert result.meta is not None
    assert result.meta.get("error") == "spawn_failed"
    assert result.meta.get("timed_out") is False
    assert "definitely-not-a-real-command" in str(result.meta.get("stderr") or "")


def test_patcher_from_env_reads_shell_and_timeout(monkeypatch) -> None:
    monkeypatch.setenv("LLMDEBUG_EVAL_PATCH_CMD", "echo hello")
    monkeypatch.setenv("LLMDEBUG_EVAL_PATCH_CMD_SHELL", "true")
    monkeypatch.setenv("LLMDEBUG_EVAL_PATCH_CMD_TIMEOUT_SEC", "12.5")

    patcher = patcher_from_env()

    assert patcher is not None
    assert patcher.cmd == "echo hello"
    assert patcher.shell is True
    assert patcher.timeout_sec == 12.5
