import { describe, expect, it } from "vitest";
import type { ClawdbotConfig } from "../config/config.js";
import { createClawdbotCodingTools } from "./pi-tools.js";
import type { SandboxDockerConfig } from "./sandbox.js";

describe("Agent-specific tool filtering", () => {
  it("should apply global tool policy when no agent-specific policy exists", () => {
    const cfg: ClawdbotConfig = {
      tools: {
        allow: ["read", "write"],
        deny: ["bash"],
      },
      agents: {
        list: [
          {
            id: "main",
            workspace: "~/clawd",
          },
        ],
      },
    };

    const tools = createClawdbotCodingTools({
      config: cfg,
      sessionKey: "agent:main:main",
      workspaceDir: "/tmp/test",
      agentDir: "/tmp/agent",
    });

    const toolNames = tools.map((t) => t.name);
    expect(toolNames).toContain("Read");
    expect(toolNames).toContain("Write");
    expect(toolNames).not.toContain("Bash");
  });

  it("should keep global tool policy when agent only sets tools.elevated", () => {
    const cfg: ClawdbotConfig = {
      tools: {
        deny: ["write"],
      },
      agents: {
        list: [
          {
            id: "main",
            workspace: "~/clawd",
            tools: {
              elevated: {
                enabled: true,
                allowFrom: { whatsapp: ["+15555550123"] },
              },
            },
          },
        ],
      },
    };

    const tools = createClawdbotCodingTools({
      config: cfg,
      sessionKey: "agent:main:main",
      workspaceDir: "/tmp/test",
      agentDir: "/tmp/agent",
    });

    const toolNames = tools.map((t) => t.name);
    expect(toolNames).toContain("Bash");
    expect(toolNames).toContain("Read");
    expect(toolNames).not.toContain("Write");
  });

  it("should apply agent-specific tool policy", () => {
    const cfg: ClawdbotConfig = {
      tools: {
        allow: ["read", "write", "bash"],
        deny: [],
      },
      agents: {
        list: [
          {
            id: "restricted",
            workspace: "~/clawd-restricted",
            tools: {
              allow: ["read"], // Agent override: only read
              deny: ["bash", "write", "edit"],
            },
          },
        ],
      },
    };

    const tools = createClawdbotCodingTools({
      config: cfg,
      sessionKey: "agent:restricted:main",
      workspaceDir: "/tmp/test-restricted",
      agentDir: "/tmp/agent-restricted",
    });

    const toolNames = tools.map((t) => t.name);
    expect(toolNames).toContain("Read");
    expect(toolNames).not.toContain("Bash");
    expect(toolNames).not.toContain("Write");
    expect(toolNames).not.toContain("Edit");
  });

  it("should allow different tool policies for different agents", () => {
    const cfg: ClawdbotConfig = {
      agents: {
        list: [
          {
            id: "main",
            workspace: "~/clawd",
            // No tools restriction - all tools available
          },
          {
            id: "family",
            workspace: "~/clawd-family",
            tools: {
              allow: ["read"],
              deny: ["bash", "write", "edit", "process"],
            },
          },
        ],
      },
    };

    // main agent: all tools
    const mainTools = createClawdbotCodingTools({
      config: cfg,
      sessionKey: "agent:main:main",
      workspaceDir: "/tmp/test-main",
      agentDir: "/tmp/agent-main",
    });
    const mainToolNames = mainTools.map((t) => t.name);
    expect(mainToolNames).toContain("Bash");
    expect(mainToolNames).toContain("Write");
    expect(mainToolNames).toContain("Edit");

    // family agent: restricted
    const familyTools = createClawdbotCodingTools({
      config: cfg,
      sessionKey: "agent:family:whatsapp:group:123",
      workspaceDir: "/tmp/test-family",
      agentDir: "/tmp/agent-family",
    });
    const familyToolNames = familyTools.map((t) => t.name);
    expect(familyToolNames).toContain("Read");
    expect(familyToolNames).not.toContain("Bash");
    expect(familyToolNames).not.toContain("Write");
    expect(familyToolNames).not.toContain("Edit");
  });

  it("should prefer agent-specific tool policy over global", () => {
    const cfg: ClawdbotConfig = {
      tools: {
        deny: ["browser"], // Global deny
      },
      agents: {
        list: [
          {
            id: "work",
            workspace: "~/clawd-work",
            tools: {
              deny: ["bash", "process"], // Agent deny (override)
            },
          },
        ],
      },
    };

    const tools = createClawdbotCodingTools({
      config: cfg,
      sessionKey: "agent:work:slack:dm:user123",
      workspaceDir: "/tmp/test-work",
      agentDir: "/tmp/agent-work",
    });

    const toolNames = tools.map((t) => t.name);
    // Agent policy overrides global: browser is allowed again
    expect(toolNames).toContain("browser");
    expect(toolNames).not.toContain("Bash");
    expect(toolNames).not.toContain("process");
  });

  it("should work with sandbox tools filtering", () => {
    const cfg: ClawdbotConfig = {
      agents: {
        defaults: {
          sandbox: {
            mode: "all",
            scope: "agent",
          },
        },
        list: [
          {
            id: "restricted",
            workspace: "~/clawd-restricted",
            sandbox: {
              mode: "all",
              scope: "agent",
            },
            tools: {
              allow: ["read"], // Agent further restricts to only read
              deny: ["bash", "write"],
            },
          },
        ],
      },
      tools: {
        sandbox: {
          tools: {
            allow: ["read", "write", "bash"], // Sandbox allows these
            deny: [],
          },
        },
      },
    };

    const tools = createClawdbotCodingTools({
      config: cfg,
      sessionKey: "agent:restricted:main",
      workspaceDir: "/tmp/test-restricted",
      agentDir: "/tmp/agent-restricted",
      sandbox: {
        enabled: true,
        sessionKey: "agent:restricted:main",
        workspaceDir: "/tmp/sandbox",
        agentWorkspaceDir: "/tmp/test-restricted",
        workspaceAccess: "none",
        containerName: "test-container",
        containerWorkdir: "/workspace",
        docker: {
          image: "test-image",
          containerPrefix: "test-",
          workdir: "/workspace",
          readOnlyRoot: true,
          tmpfs: [],
          network: "none",
          capDrop: [],
        } satisfies SandboxDockerConfig,
        tools: {
          allow: ["read", "write", "bash"],
          deny: [],
        },
      },
    });

    const toolNames = tools.map((t) => t.name);
    // Agent policy should be applied first, then sandbox
    // Agent allows only "read", sandbox allows ["read", "write", "bash"]
    // Result: only "read" (most restrictive wins)
    expect(toolNames).toContain("Read");
    expect(toolNames).not.toContain("Bash");
    expect(toolNames).not.toContain("Write");
  });

  it("should run bash synchronously when process is denied", async () => {
    const cfg: ClawdbotConfig = {
      tools: {
        deny: ["process"],
      },
    };

    const tools = createClawdbotCodingTools({
      config: cfg,
      sessionKey: "agent:main:main",
      workspaceDir: "/tmp/test-main",
      agentDir: "/tmp/agent-main",
    });
    const bash = tools.find((tool) => tool.name === "Bash");
    expect(bash).toBeDefined();

    const result = await bash?.execute("call1", {
      command: "echo done",
      yieldMs: 10,
    });

    expect(result?.details.status).toBe("completed");
  });
});
