import { describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../../config/config.js";

async function loadToolsHarness(options?: {
  resolveToolsMock?: ReturnType<typeof vi.fn>;
  resolveTools?: () => {
    agentId: string;
    profile: string;
    groups: Array<{
      id: "core" | "plugin" | "channel";
      label: string;
      source: "core" | "plugin" | "channel";
      pluginId?: string;
      channelId?: string;
      tools: Array<{
        id: string;
        label: string;
        description: string;
        source: "core" | "plugin" | "channel";
        pluginId?: string;
        channelId?: string;
      }>;
    }>;
  };
}) {
  vi.resetModules();
  vi.doMock("../../agents/agent-scope.js", async (importOriginal) => {
    const actual = await importOriginal<typeof import("../../agents/agent-scope.js")>();
    return {
      ...actual,
      resolveSessionAgentId: () => "main",
    };
  });
  const resolveToolsMock =
    options?.resolveToolsMock ??
    vi.fn(
      options?.resolveTools ??
        (() => ({
          agentId: "main",
          profile: "coding",
          groups: [
            {
              id: "core" as const,
              label: "Built-in tools",
              source: "core" as const,
              tools: [
                {
                  id: "exec",
                  label: "Exec",
                  description: "Run shell commands",
                  source: "core" as const,
                },
              ],
            },
            {
              id: "plugin" as const,
              label: "Connected tools",
              source: "plugin" as const,
              tools: [
                {
                  id: "docs_lookup",
                  label: "Docs Lookup",
                  description: "Search internal documentation",
                  source: "plugin" as const,
                  pluginId: "docs",
                },
              ],
            },
          ],
        })),
    );
  vi.doMock("../../agents/tools-effective-inventory.js", () => ({
    resolveEffectiveToolInventory: resolveToolsMock,
  }));
  vi.doMock("./agent-runner-utils.js", () => ({
    buildThreadingToolContext: () => ({
      currentChannelId: "channel-123",
      currentMessageId: "message-456",
    }),
  }));
  vi.doMock("./reply-threading.js", () => ({
    resolveReplyToMode: () => "all",
  }));

  const { buildCommandTestParams } = await import("./commands.test-harness.js");
  const { handleToolsCommand } = await import("./commands-info.js");
  return { buildCommandTestParams, handleToolsCommand, resolveToolsMock };
}

function buildConfig() {
  return {
    commands: { text: true },
    channels: { whatsapp: { allowFrom: ["*"] } },
  } as OpenClawConfig;
}

describe("handleToolsCommand", () => {
  it("renders a product-facing tool list", async () => {
    const { buildCommandTestParams, handleToolsCommand, resolveToolsMock } =
      await loadToolsHarness();
    const params = buildCommandTestParams("/tools", buildConfig(), undefined, {
      workspaceDir: "/tmp",
    });
    params.agentId = "main";
    params.provider = "openai";
    params.model = "gpt-4.1";
    params.ctx = {
      ...params.ctx,
      From: "telegram:group:abc123",
      GroupChannel: "#ops",
      GroupSpace: "workspace-1",
      SenderName: "User Name",
      SenderUsername: "user_name",
      SenderE164: "+1000",
      MessageThreadId: 99,
      AccountId: "acct-1",
      Provider: "telegram",
      ChatType: "group",
    };

    const result = await handleToolsCommand(params, true);

    expect(result?.reply?.text).toContain("Available tools");
    expect(result?.reply?.text).toContain("Profile: coding");
    expect(result?.reply?.text).toContain("Built-in tools");
    expect(result?.reply?.text).toContain("exec");
    expect(result?.reply?.text).toContain("Connected tools");
    expect(result?.reply?.text).toContain("docs_lookup (docs)");
    expect(result?.reply?.text).not.toContain("unavailable right now");
    expect(resolveToolsMock).toHaveBeenCalledWith(
      expect.objectContaining({
        senderIsOwner: false,
        senderId: undefined,
        senderName: "User Name",
        senderUsername: "user_name",
        senderE164: "+1000",
        accountId: "acct-1",
        currentChannelId: "channel-123",
        currentThreadTs: "99",
        currentMessageId: "message-456",
        groupId: "abc123",
        groupChannel: "#ops",
        groupSpace: "workspace-1",
        replyToMode: "all",
      }),
    );
  });

  it("returns usage when arguments are provided", async () => {
    const { buildCommandTestParams, handleToolsCommand } = await loadToolsHarness();
    const result = await handleToolsCommand(
      buildCommandTestParams("/tools extra", buildConfig(), undefined, { workspaceDir: "/tmp" }),
      true,
    );

    expect(result).toEqual({
      shouldContinue: false,
      reply: { text: "Usage: /tools [compact|verbose]" },
    });
  });

  it("does not synthesize group ids for direct-chat sender ids", async () => {
    const { buildCommandTestParams, handleToolsCommand, resolveToolsMock } =
      await loadToolsHarness();
    const params = buildCommandTestParams("/tools", buildConfig(), undefined, {
      workspaceDir: "/tmp",
    });
    params.ctx = {
      ...params.ctx,
      From: "telegram:8231046597",
      Provider: "telegram",
      ChatType: "dm",
    };

    await handleToolsCommand(params, true);

    expect(resolveToolsMock).toHaveBeenCalledWith(expect.objectContaining({ groupId: undefined }));
  });

  it("renders the detailed tool list in verbose mode", async () => {
    const { buildCommandTestParams, handleToolsCommand } = await loadToolsHarness();
    const result = await handleToolsCommand(
      buildCommandTestParams("/tools verbose", buildConfig(), undefined, { workspaceDir: "/tmp" }),
      true,
    );

    expect(result?.reply?.text).toContain("What this agent can use right now:");
    expect(result?.reply?.text).toContain("Profile: coding");
    expect(result?.reply?.text).toContain("Exec - Run shell commands");
    expect(result?.reply?.text).toContain("Docs Lookup - Search internal documentation");
  });

  it("accepts explicit compact mode", async () => {
    const { buildCommandTestParams, handleToolsCommand } = await loadToolsHarness();
    const result = await handleToolsCommand(
      buildCommandTestParams("/tools compact", buildConfig(), undefined, { workspaceDir: "/tmp" }),
      true,
    );

    expect(result?.reply?.text).toContain("exec");
    expect(result?.reply?.text).toContain("Use /tools verbose for descriptions.");
  });

  it("ignores unauthorized senders", async () => {
    const { buildCommandTestParams, handleToolsCommand } = await loadToolsHarness();
    const params = buildCommandTestParams("/tools", buildConfig(), undefined, {
      workspaceDir: "/tmp",
    });
    params.command = {
      ...params.command,
      isAuthorizedSender: false,
      senderId: "unauthorized",
    };

    const result = await handleToolsCommand(params, true);

    expect(result).toEqual({ shouldContinue: false });
  });

  it("returns a concise fallback error on effective inventory failures", async () => {
    const { buildCommandTestParams, handleToolsCommand } = await loadToolsHarness({
      resolveTools: () => {
        throw new Error("boom");
      },
    });

    const result = await handleToolsCommand(
      buildCommandTestParams("/tools", buildConfig(), undefined, { workspaceDir: "/tmp" }),
      true,
    );

    expect(result).toEqual({
      shouldContinue: false,
      reply: { text: "Couldn't load available tools right now. Try again in a moment." },
    });
  });
});
