import { beforeEach, describe, expect, it, vi } from "vitest";

describe("command secret targets module import", () => {
  beforeEach(() => {
    vi.resetModules();
  });

  it("does not touch the registry during module import", async () => {
    const listSecretTargetRegistryEntries = vi.fn(() => {
      throw new Error("registry touched too early");
    });

    vi.doMock("../secrets/target-registry.js", () => ({
      discoverConfigSecretTargetsByIds: vi.fn(() => []),
      listSecretTargetRegistryEntries,
    }));

    const mod = await import("./command-secret-targets.js");

    expect(listSecretTargetRegistryEntries).not.toHaveBeenCalled();
    expect(mod.getModelsCommandSecretTargetIds().has("models.providers.*.apiKey")).toBe(true);
    expect(mod.getQrRemoteCommandSecretTargetIds().has("gateway.remote.token")).toBe(true);
    expect(listSecretTargetRegistryEntries).not.toHaveBeenCalled();
    expect(() => mod.getChannelsCommandSecretTargetIds()).toThrow("registry touched too early");
    expect(listSecretTargetRegistryEntries).toHaveBeenCalledTimes(1);
  });

  it("loads registry lazily for agent runtime plugin credential targets", async () => {
    const listSecretTargetRegistryEntries = vi.fn(() => [
      { id: "plugins.entries.example.config.webSearch.apiKey" },
      { id: "plugins.entries.example.config.other.apiKey" },
      { id: "channels.telegram.botToken" },
    ]);

    vi.doMock("../secrets/target-registry.js", () => ({
      discoverConfigSecretTargetsByIds: vi.fn(() => []),
      listSecretTargetRegistryEntries,
    }));

    const mod = await import("./command-secret-targets.js");

    expect(listSecretTargetRegistryEntries).not.toHaveBeenCalled();
    const ids = mod.getAgentRuntimeCommandSecretTargetIds();
    expect(ids.has("agents.defaults.memorySearch.remote.apiKey")).toBe(true);
    expect(ids.has("plugins.entries.example.config.webSearch.apiKey")).toBe(true);
    expect(ids.has("plugins.entries.example.config.other.apiKey")).toBe(false);
    expect(ids.has("channels.telegram.botToken")).toBe(false);
    expect(listSecretTargetRegistryEntries).toHaveBeenCalledTimes(1);
  });

  it("can resolve configured-channel status targets without the full registry", async () => {
    const listSecretTargetRegistryEntries = vi.fn(() => {
      throw new Error("registry touched too early");
    });
    const listReadOnlyChannelPluginsForConfig = vi.fn(() => [
      {
        id: "telegram",
        secrets: {
          secretTargetRegistryEntries: [
            {
              id: "channels.telegram.botToken",
              targetType: "channels.telegram.botToken",
              configFile: "openclaw.json",
              pathPattern: "channels.telegram.botToken",
              secretShape: "secret_input",
              expectedResolvedValue: "string",
              includeInPlan: true,
              includeInConfigure: true,
              includeInAudit: true,
            },
            {
              id: "channels.telegram.gatewayToken",
              targetType: "gateway.auth.token",
              configFile: "openclaw.json",
              pathPattern: "gateway.auth.token",
              secretShape: "secret_input",
              expectedResolvedValue: "string",
              includeInPlan: true,
              includeInConfigure: true,
              includeInAudit: true,
            },
            {
              id: "channels.telegram.gatewayTokenRef",
              targetType: "channels.telegram.gatewayTokenRef",
              configFile: "openclaw.json",
              pathPattern: "channels.telegram.gatewayToken",
              refPathPattern: "gateway.auth.token",
              secretShape: "sibling_ref",
              expectedResolvedValue: "string",
              includeInPlan: true,
              includeInConfigure: true,
              includeInAudit: true,
            },
            {
              id: "channels.discord.token",
              targetType: "channels.discord.token",
              configFile: "openclaw.json",
              pathPattern: "channels.discord.token",
              secretShape: "secret_input",
              expectedResolvedValue: "string",
              includeInPlan: true,
              includeInConfigure: true,
              includeInAudit: true,
            },
          ],
        },
      },
      {
        id: "external-chat",
        secrets: {
          secretTargetRegistryEntries: [
            {
              id: "channels.external-chat.token",
              targetType: "channels.external-chat.token",
              configFile: "openclaw.json",
              pathPattern: "channels.external-chat.token",
              secretShape: "secret_input",
              expectedResolvedValue: "string",
              includeInPlan: true,
              includeInConfigure: true,
              includeInAudit: true,
            },
          ],
        },
      },
    ]);

    vi.doMock("../secrets/target-registry.js", () => ({
      discoverConfigSecretTargetsByIds: vi.fn(() => []),
      listSecretTargetRegistryEntries,
    }));
    vi.doMock("../channels/plugins/read-only.js", () => ({
      listReadOnlyChannelPluginsForConfig,
    }));

    const mod = await import("./command-secret-targets.js");
    const targets = mod.getStatusCommandSecretTargetIds({
      channels: {
        "external-chat": { token: "configured" },
        telegram: { botToken: "123456:ABCDEF" },
      },
    });

    expect(targets.has("channels.external-chat.token")).toBe(true);
    expect(targets.has("channels.telegram.botToken")).toBe(true);
    expect(targets.has("channels.discord.token")).toBe(false);
    expect(targets.has("channels.telegram.gatewayToken")).toBe(false);
    expect(targets.has("channels.telegram.gatewayTokenRef")).toBe(false);
    expect(targets.has("agents.defaults.memorySearch.remote.apiKey")).toBe(true);
    const pluginCall = listReadOnlyChannelPluginsForConfig.mock.calls[0] as unknown as
      | [unknown, { includePersistedAuthState?: boolean }]
      | undefined;
    expect(typeof pluginCall?.[0]).toBe("object");
    expect(pluginCall?.[1]?.includePersistedAuthState).toBe(false);
    expect(listSecretTargetRegistryEntries).not.toHaveBeenCalled();
  });

  it("can omit channel targets from status targets without plugin discovery", async () => {
    const listSecretTargetRegistryEntries = vi.fn(() => {
      throw new Error("registry touched too early");
    });
    const listReadOnlyChannelPluginsForConfig = vi.fn(() => {
      throw new Error("channel plugin metadata touched too early");
    });

    vi.doMock("../secrets/target-registry.js", () => ({
      discoverConfigSecretTargetsByIds: vi.fn(() => []),
      listSecretTargetRegistryEntries,
    }));
    vi.doMock("../channels/plugins/read-only.js", () => ({
      listReadOnlyChannelPluginsForConfig,
    }));

    const mod = await import("./command-secret-targets.js");
    const targets = mod.getStatusCommandSecretTargetIds(
      {
        channels: {
          telegram: { botToken: "123456:ABCDEF" },
        },
      },
      process.env,
      { includeChannelTargets: false },
    );

    expect(targets.has("agents.defaults.memorySearch.remote.apiKey")).toBe(true);
    expect(targets.has("channels.telegram.botToken")).toBe(false);
    expect(listReadOnlyChannelPluginsForConfig).not.toHaveBeenCalled();
    expect(listSecretTargetRegistryEntries).not.toHaveBeenCalled();
  });
});
