import { mkdtempSync, mkdirSync, rmSync, symlinkSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { createDefaultRuntimePolicy } from "./runtime-policy.js";

describe("runtime policy", () => {
  it("allows reads inside the configured cwd and asks for reads outside it", () => {
    const policy = createDefaultRuntimePolicy({
      cwd: "/workspace",
    });

    expect(
      policy.evaluate({
        group: "filesystem",
        action: "read",
        path: "/workspace/package.json",
      }),
    ).toMatchObject({ verdict: "allow", sandboxProfile: "no-network-no-write" });

    expect(
      policy.evaluate({
        group: "filesystem",
        action: "read",
        path: "/tmp/secrets.txt",
      }),
    ).toMatchObject({
      verdict: "ask",
      reviewMode: "direct",
      riskClass: "medium",
      sandboxProfile: "no-network-no-write",
    });
  });

  it("allows writes inside explicit scratch roots and cwd, and asks for writes outside", () => {
    const policy = createDefaultRuntimePolicy({
      cwd: "/workspace",
    });

    expect(
      policy.evaluate({
        group: "filesystem",
        action: "write",
        path: "/storage/threads/thread_1/scratch/log.txt",
        roots: ["/storage/threads/thread_1/scratch"],
      }),
    ).toMatchObject({ verdict: "allow", sandboxProfile: "no-network-no-write" });

    expect(
      policy.evaluate({
        group: "filesystem",
        action: "write",
        path: "/workspace/output.txt",
      }),
    ).toMatchObject({ verdict: "allow", sandboxProfile: "no-network-no-write" });

    expect(
      policy.evaluate({
        group: "filesystem",
        action: "write",
        path: "/tmp/output.txt",
      }),
    ).toMatchObject({
      verdict: "ask",
      reviewMode: "guardian",
      riskClass: "high",
      sandboxProfile: "no-network",
    });
  });

  it("allows workspace tool writes inside their declared workspace root", () => {
    const policy = createDefaultRuntimePolicy({
      cwd: "/workspace",
    });

    expect(
      policy.evaluate({
        group: "filesystem",
        action: "write",
        path: "/workspace/output.txt",
        roots: ["/workspace"],
      }),
    ).toMatchObject({ verdict: "allow", sandboxProfile: "no-network-no-write" });
  });

  it("allows configured shell prefixes and asks for the rest", () => {
    const policy = createDefaultRuntimePolicy({
      cwd: "/workspace",
    });

    expect(
      policy.evaluate({
        group: "shell",
        argv: ["pwd"],
        cwd: "/workspace",
      }),
    ).toMatchObject({ verdict: "allow", sandboxProfile: "no-network-no-write" });

    expect(
      policy.evaluate({
        group: "shell",
        argv: ["date", "+%u", "%A"],
        cwd: "/workspace",
      }),
    ).toMatchObject({ verdict: "allow", sandboxProfile: "no-network-no-write" });

    expect(
      policy.evaluate({
        group: "shell",
        argv: ["git", "status"],
        cwd: "/workspace",
      }),
    ).toMatchObject({ verdict: "allow", sandboxProfile: "no-network-no-write" });

    expect(
      policy.evaluate({
        group: "shell",
        argv: ["git", "rev-parse", "--show-toplevel"],
        cwd: "/workspace",
      }),
    ).toMatchObject({
      verdict: "ask",
      reviewMode: "guardian",
      riskClass: "high",
      sandboxProfile: "none",
    });
  });

  it("allows read-only shell command strings and asks for shell writes", () => {
    const policy = createDefaultRuntimePolicy({
      cwd: "/workspace",
    });

    expect(
      policy.evaluate({
        group: "shell",
        argv: ["/bin/sh", "-lc", "cd /workspace && git status"],
        cwd: "/workspace",
      }),
    ).toMatchObject({ verdict: "allow", sandboxProfile: "no-network-no-write" });

    expect(
      policy.evaluate({
        group: "shell",
        argv: ["/bin/sh", "-lc", "cd /workspace && git diff --stat | head -20"],
        cwd: "/workspace",
      }),
    ).toMatchObject({ verdict: "allow", sandboxProfile: "no-network-no-write" });

    expect(
      policy.evaluate({
        group: "shell",
        argv: ["/bin/bash", "-lc", "set -o pipefail; cd /workspace && git diff --stat | head -20"],
        cwd: "/workspace",
      }),
    ).toMatchObject({ verdict: "allow", sandboxProfile: "no-network-no-write" });

    expect(
      policy.evaluate({
        group: "shell",
        argv: ["/bin/sh", "-lc", "cd /workspace && cat feature.ts 2>/dev/null | head -30"],
        cwd: "/workspace",
      }),
    ).toMatchObject({ verdict: "allow", sandboxProfile: "no-network-no-write" });

    expect(
      policy.evaluate({
        group: "shell",
        argv: ["/bin/sh", "-lc", "cd /workspace && git add package.json"],
        cwd: "/workspace",
      }),
    ).toMatchObject({
      verdict: "ask",
      reviewMode: "guardian",
      riskClass: "high",
      sandboxProfile: "none",
    });
  });

  it("denies sensitive file reads before cwd or shell read-only allows", () => {
    const policy = createDefaultRuntimePolicy({
      cwd: "/workspace",
    });

    expect(
      policy.evaluate({
        group: "filesystem",
        action: "read",
        path: "/workspace/.env",
      }),
    ).toMatchObject({
      verdict: "deny",
      reviewMode: "direct",
      riskClass: "high",
      sandboxProfile: "no-network-no-write",
    });

    expect(
      policy.evaluate({
        group: "shell",
        argv: ["cat", ".env"],
        cwd: "/workspace",
      }),
    ).toMatchObject({
      verdict: "deny",
      reviewMode: "direct",
      riskClass: "high",
      sandboxProfile: "no-network-no-write",
    });

    expect(
      policy.evaluate({
        group: "shell",
        argv: ["/bin/sh", "-lc", "cd /workspace && grep OPENAI .env"],
        cwd: "/workspace",
      }),
    ).toMatchObject({
      verdict: "deny",
      reviewMode: "direct",
      riskClass: "high",
      sandboxProfile: "no-network-no-write",
    });

    expect(
      policy.evaluate({
        group: "shell",
        argv: [
          "/bin/sh",
          "-lc",
          'cat .env 2>/dev/null | grep -v "^#" | grep -v "^$" | sed "s/=.*/=***/" | head -20',
        ],
        cwd: "/workspace",
      }),
    ).toMatchObject({
      verdict: "deny",
      reviewMode: "direct",
      riskClass: "high",
      sandboxProfile: "no-network-no-write",
    });

    expect(
      policy.evaluate({
        group: "filesystem",
        action: "read",
        path: "/workspace/.best-agent/cli-state.json",
      }),
    ).toMatchObject({
      verdict: "deny",
      reviewMode: "direct",
      riskClass: "high",
      sandboxProfile: "no-network-no-write",
    });

    expect(
      policy.evaluate({
        group: "shell",
        argv: ["/bin/sh", "-lc", "cd /workspace && cat .best-agent/cli-state.json"],
        cwd: "/workspace",
      }),
    ).toMatchObject({
      verdict: "deny",
      reviewMode: "direct",
      riskClass: "high",
      sandboxProfile: "no-network-no-write",
    });

    expect(
      policy.evaluate({
        group: "shell",
        argv: [
          "/bin/sh",
          "-lc",
          "cd /workspace && python3 -m json.tool .best-agent/cli-state.json",
        ],
        cwd: "/workspace",
      }),
    ).toMatchObject({
      verdict: "deny",
      reviewMode: "direct",
      riskClass: "high",
      sandboxProfile: "no-network-no-write",
    });

    expect(
      policy.evaluate({
        group: "shell",
        argv: ["/bin/sh", "-lc", "cd /workspace && jq . .best-agent/cli-state.json"],
        cwd: "/workspace",
      }),
    ).toMatchObject({
      verdict: "deny",
      reviewMode: "direct",
      riskClass: "high",
      sandboxProfile: "no-network-no-write",
    });

    expect(
      policy.evaluate({
        group: "filesystem",
        action: "read",
        path: "/workspace/.dimcode/state/tool-results/41215.txt",
      }),
    ).toMatchObject({
      verdict: "deny",
      reviewMode: "direct",
      riskClass: "high",
      sandboxProfile: "no-network-no-write",
    });

    expect(
      policy.evaluate({
        group: "shell",
        argv: ["/bin/sh", "-lc", "cat ~/.docker/config.json 2>/dev/null | head -20"],
        cwd: "/workspace",
      }),
    ).toMatchObject({
      verdict: "deny",
      reviewMode: "direct",
      riskClass: "high",
      sandboxProfile: "no-network-no-write",
    });

    expect(
      policy.evaluate({
        group: "shell",
        argv: ["/bin/sh", "-lc", "jq . ~/.docker/config.json"],
        cwd: "/workspace",
      }),
    ).toMatchObject({
      verdict: "deny",
      reviewMode: "direct",
      riskClass: "high",
      sandboxProfile: "no-network-no-write",
    });

    expect(
      policy.evaluate({
        group: "shell",
        argv: ["/bin/sh", "-lc", "grep auth ~/.docker/config.json"],
        cwd: "/workspace",
      }),
    ).toMatchObject({
      verdict: "deny",
      reviewMode: "direct",
      riskClass: "high",
      sandboxProfile: "no-network-no-write",
    });

    expect(
      policy.evaluate({
        group: "shell",
        argv: ["/bin/sh", "-lc", "python3 -m json.tool ~/.docker/contexts/current/meta.json"],
        cwd: "/workspace",
      }),
    ).toMatchObject({
      verdict: "deny",
      reviewMode: "direct",
      riskClass: "high",
      sandboxProfile: "no-network-no-write",
    });
  });

  it("denies env dumps even when all shell is otherwise allowed", () => {
    const policy = createDefaultRuntimePolicy({
      allowAllShell: true,
      cwd: "/workspace",
    });

    expect(
      policy.evaluate({
        group: "shell",
        argv: ["/bin/sh", "-lc", "env"],
        cwd: "/workspace",
      }),
    ).toMatchObject({
      verdict: "deny",
      reviewMode: "direct",
      riskClass: "high",
      sandboxProfile: "no-network-no-write",
    });

    expect(
      policy.evaluate({
        group: "shell",
        argv: ["node", "-e", 'require("fs").readFileSync(".env", "utf8")'],
        cwd: "/workspace",
      }),
    ).toMatchObject({
      verdict: "deny",
      reviewMode: "direct",
      riskClass: "high",
      sandboxProfile: "no-network-no-write",
    });

    expect(
      policy.evaluate({
        group: "shell",
        argv: ["node", "-e", "console.log(process.env.OPENAI_API_KEY)"],
        cwd: "/workspace",
      }),
    ).toMatchObject({
      verdict: "deny",
      reviewMode: "direct",
      riskClass: "high",
      sandboxProfile: "no-network-no-write",
    });
  });

  it("requires high-risk approval for Docker Desktop restarts even when shell is otherwise allowed", () => {
    const policy = createDefaultRuntimePolicy({
      allowAllShell: true,
      cwd: "/workspace",
    });

    expect(
      policy.evaluate({
        group: "shell",
        argv: ["/bin/sh", "-lc", "killall Docker && sleep 2 && open -a Docker"],
        cwd: "/workspace",
      }),
    ).toMatchObject({
      verdict: "ask",
      reviewMode: "guardian",
      riskClass: "high",
      sandboxProfile: "no-network",
    });
  });

  it("omits multiline shell argv from approval reasons", () => {
    const policy = createDefaultRuntimePolicy({
      cwd: "/workspace",
    });
    const secret = "runtime policy secret";
    const command = `python3 - <<'PY'\nprint(${JSON.stringify(secret)})\nPY`;

    const decision = policy.evaluate({
      group: "shell",
      argv: ["sh", "-c", command],
      cwd: "/workspace",
    });

    expect(decision.reason).toContain("shell command omitted");
    expect(decision.reason).not.toContain(secret);
  });

  it("tracks network allowlists for approval-gated network tools", () => {
    const policy = createDefaultRuntimePolicy({
      cwd: "/workspace",
      allowedNetworkHosts: ["example.com"],
    });

    expect(
      policy.evaluate({
        group: "network",
        host: "example.com",
      }),
    ).toMatchObject({ verdict: "allow", sandboxProfile: "none" });

    expect(
      policy.evaluate({
        group: "network",
        host: "openai.com",
      }),
    ).toMatchObject({
      verdict: "ask",
      reviewMode: "guardian",
      riskClass: "high",
      sandboxProfile: "none",
    });
  });

  it("supports overlay rules for per-agent runtime permissions", () => {
    const base = createDefaultRuntimePolicy({
      cwd: "/workspace",
    });
    const overlay = base.overlay({
      allowedShellPrefixes: [["node"]],
      deniedTools: ["shell"],
      writableRoots: ["/workspace/generated"],
    });

    expect(
      overlay.evaluate({
        group: "shell",
        argv: ["node", "-p", "40+2"],
        cwd: "/workspace",
      }),
    ).toMatchObject({ verdict: "allow", sandboxProfile: "no-network-no-write" });

    expect(
      overlay.evaluate({
        group: "tool",
        toolName: "shell",
      }),
    ).toMatchObject({ verdict: "deny", sandboxProfile: "none" });

    expect(
      overlay.evaluate({
        group: "filesystem",
        action: "write",
        path: "/workspace/generated/out.txt",
        roots: ["/workspace"],
      }),
    ).toMatchObject({ verdict: "allow", sandboxProfile: "no-network-no-write" });
  });

  it("rejects symlinked paths that escape the allowed root", () => {
    if (process.platform === "win32") {
      return;
    }

    const root = mkdtempSync(join(tmpdir(), "best-agent-policy-root-"));
    const outside = mkdtempSync(join(tmpdir(), "best-agent-policy-outside-"));
    const symlinkPath = join(root, "escape");
    mkdirSync(root, { recursive: true });
    symlinkSync(outside, symlinkPath);

    try {
      const policy = createDefaultRuntimePolicy({
        cwd: root,
        writableRoots: [root],
      });

      expect(
        policy.evaluate({
          group: "filesystem",
          action: "write",
          path: join(root, "safe.txt"),
        }),
      ).toMatchObject({ verdict: "allow" });

      expect(
        policy.evaluate({
          group: "filesystem",
          action: "write",
          path: join(symlinkPath, "nested.txt"),
        }),
      ).toMatchObject({ verdict: "deny" });
    } finally {
      rmSync(root, { force: true, recursive: true });
      rmSync(outside, { force: true, recursive: true });
    }
  });

  it("can allow all shell requests for sandboxed benchmark runs", () => {
    const policy = createDefaultRuntimePolicy({
      allowAllShell: true,
      cwd: "/workspace",
    });

    expect(
      policy.evaluate({
        group: "shell",
        argv: ["python3", "--version"],
        cwd: "/workspace",
      }),
    ).toMatchObject({ verdict: "allow", reviewMode: "direct", sandboxProfile: "none" });

    expect(
      policy.evaluate({
        group: "shell",
        argv: ["pip", "install", "torch"],
        cwd: "/workspace",
      }),
    ).toMatchObject({ verdict: "allow", reviewMode: "direct", sandboxProfile: "none" });
  });
});
