"""Tests for the local persistent shell backend."""

import glob as glob_mod

import pytest

from tools.environments.local import LocalEnvironment
from tools.environments.persistent_shell import PersistentShellMixin


class TestLocalConfig:
    def test_local_persistent_default_false(self, monkeypatch):
        monkeypatch.delenv("TERMINAL_LOCAL_PERSISTENT", raising=False)
        from tools.terminal_tool import _get_env_config
        assert _get_env_config()["local_persistent"] is False

    def test_local_persistent_true(self, monkeypatch):
        monkeypatch.setenv("TERMINAL_LOCAL_PERSISTENT", "true")
        from tools.terminal_tool import _get_env_config
        assert _get_env_config()["local_persistent"] is True

    def test_local_persistent_yes(self, monkeypatch):
        monkeypatch.setenv("TERMINAL_LOCAL_PERSISTENT", "yes")
        from tools.terminal_tool import _get_env_config
        assert _get_env_config()["local_persistent"] is True


class TestMergeOutput:
    def test_stdout_only(self):
        assert PersistentShellMixin._merge_output("out", "") == "out"

    def test_stderr_only(self):
        assert PersistentShellMixin._merge_output("", "err") == "err"

    def test_both(self):
        assert PersistentShellMixin._merge_output("out", "err") == "out\nerr"

    def test_empty(self):
        assert PersistentShellMixin._merge_output("", "") == ""

    def test_strips_trailing_newlines(self):
        assert PersistentShellMixin._merge_output("out\n\n", "err\n") == "out\nerr"


class TestLocalOneShotRegression:
    def test_echo(self):
        env = LocalEnvironment(persistent=False)
        r = env.execute("echo hello")
        assert r["returncode"] == 0
        assert "hello" in r["output"]
        env.cleanup()

    def test_exit_code(self):
        env = LocalEnvironment(persistent=False)
        r = env.execute("exit 42")
        assert r["returncode"] == 42
        env.cleanup()

    def test_state_does_not_persist(self):
        env = LocalEnvironment(persistent=False)
        env.execute("export HERMES_ONESHOT_LOCAL=yes")
        r = env.execute("echo $HERMES_ONESHOT_LOCAL")
        assert r["output"].strip() == ""
        env.cleanup()

    def test_oneshot_heredoc_does_not_leak_fence_wrapper(self):
        """Heredoc closing line must not be merged with the fence wrapper tail."""
        env = LocalEnvironment(persistent=False)
        cmd = "cat <<'H_EOF'\nheredoc body line\nH_EOF"
        r = env.execute(cmd)
        env.cleanup()
        assert r["returncode"] == 0
        assert "heredoc body line" in r["output"]
        assert "__hermes_rc" not in r["output"]
        assert "printf '" not in r["output"]
        assert "exit $" not in r["output"]


class TestLocalPersistent:
    @pytest.fixture
    def env(self):
        e = LocalEnvironment(persistent=True)
        yield e
        e.cleanup()

    def test_echo(self, env):
        r = env.execute("echo hello-persistent")
        assert r["returncode"] == 0
        assert "hello-persistent" in r["output"]

    def test_env_var_persists(self, env):
        env.execute("export HERMES_LOCAL_PERSIST_TEST=works")
        r = env.execute("echo $HERMES_LOCAL_PERSIST_TEST")
        assert r["output"].strip() == "works"

    def test_cwd_persists(self, env):
        env.execute("cd /tmp")
        r = env.execute("pwd")
        assert r["output"].strip() == "/tmp"

    def test_exit_code(self, env):
        r = env.execute("(exit 42)")
        assert r["returncode"] == 42

    def test_stderr(self, env):
        r = env.execute("echo oops >&2")
        assert r["returncode"] == 0
        assert "oops" in r["output"]

    def test_multiline_output(self, env):
        r = env.execute("echo a; echo b; echo c")
        lines = r["output"].strip().splitlines()
        assert lines == ["a", "b", "c"]

    def test_timeout_then_recovery(self, env):
        r = env.execute("sleep 999", timeout=2)
        assert r["returncode"] in (124, 130)
        r = env.execute("echo alive")
        assert r["returncode"] == 0
        assert "alive" in r["output"]

    def test_large_output(self, env):
        r = env.execute("seq 1 1000")
        assert r["returncode"] == 0
        lines = r["output"].strip().splitlines()
        assert len(lines) == 1000
        assert lines[0] == "1"
        assert lines[-1] == "1000"

    def test_shell_variable_persists(self, env):
        env.execute("MY_LOCAL_VAR=hello123")
        r = env.execute("echo $MY_LOCAL_VAR")
        assert r["output"].strip() == "hello123"

    def test_cleanup_removes_temp_files(self, env):
        env.execute("echo warmup")
        prefix = env._temp_prefix
        assert len(glob_mod.glob(f"{prefix}-*")) > 0
        env.cleanup()
        remaining = glob_mod.glob(f"{prefix}-*")
        assert remaining == []

    def test_state_does_not_leak_between_instances(self):
        env1 = LocalEnvironment(persistent=True)
        env2 = LocalEnvironment(persistent=True)
        try:
            env1.execute("export LEAK_TEST=from_env1")
            r = env2.execute("echo $LEAK_TEST")
            assert r["output"].strip() == ""
        finally:
            env1.cleanup()
            env2.cleanup()

    def test_special_characters_in_command(self, env):
        r = env.execute("echo 'hello world'")
        assert r["output"].strip() == "hello world"

    def test_pipe_command(self, env):
        r = env.execute("echo hello | tr 'h' 'H'")
        assert r["output"].strip() == "Hello"

    def test_multiple_commands_semicolon(self, env):
        r = env.execute("X=42; echo $X")
        assert r["output"].strip() == "42"
