import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../config/config.js";
import { createEmptyPluginRegistry } from "../plugins/registry.js";

const { resolveRuntimePluginRegistryMock } = vi.hoisted(() => ({
  resolveRuntimePluginRegistryMock: vi.fn<
    (params?: unknown) => ReturnType<typeof createEmptyPluginRegistry> | undefined
  >(() => undefined),
}));

vi.mock("../plugins/loader.js", () => ({
  resolveRuntimePluginRegistry: resolveRuntimePluginRegistryMock,
}));

let generateImage: typeof import("./runtime.js").generateImage;
let listRuntimeImageGenerationProviders: typeof import("./runtime.js").listRuntimeImageGenerationProviders;

describe("image-generation runtime helpers", () => {
  afterEach(() => {
    resolveRuntimePluginRegistryMock.mockReset();
    resolveRuntimePluginRegistryMock.mockReturnValue(undefined);
  });

  beforeEach(async () => {
    vi.resetModules();
    ({ generateImage, listRuntimeImageGenerationProviders } = await import("./runtime.js"));
  });

  it("generates images through the active image-generation registry", async () => {
    const pluginRegistry = createEmptyPluginRegistry();
    const authStore = { version: 1, profiles: {} } as const;
    let seenAuthStore: unknown;
    pluginRegistry.imageGenerationProviders.push({
      pluginId: "image-plugin",
      pluginName: "Image Plugin",
      source: "test",
      provider: {
        id: "image-plugin",
        capabilities: {
          generate: {},
          edit: { enabled: false },
        },
        async generateImage(req) {
          seenAuthStore = req.authStore;
          return {
            images: [
              {
                buffer: Buffer.from("png-bytes"),
                mimeType: "image/png",
                fileName: "sample.png",
              },
            ],
            model: "img-v1",
          };
        },
      },
    });
    resolveRuntimePluginRegistryMock.mockReturnValue(pluginRegistry);

    const cfg = {
      agents: {
        defaults: {
          imageGenerationModel: {
            primary: "image-plugin/img-v1",
          },
        },
      },
    } as OpenClawConfig;

    const result = await generateImage({
      cfg,
      prompt: "draw a cat",
      agentDir: "/tmp/agent",
      authStore,
    });

    expect(result.provider).toBe("image-plugin");
    expect(result.model).toBe("img-v1");
    expect(result.attempts).toEqual([]);
    expect(seenAuthStore).toEqual(authStore);
    expect(result.images).toEqual([
      {
        buffer: Buffer.from("png-bytes"),
        mimeType: "image/png",
        fileName: "sample.png",
      },
    ]);
  });

  it("lists runtime image-generation providers from the active registry", () => {
    const pluginRegistry = createEmptyPluginRegistry();
    pluginRegistry.imageGenerationProviders.push({
      pluginId: "image-plugin",
      pluginName: "Image Plugin",
      source: "test",
      provider: {
        id: "image-plugin",
        defaultModel: "img-v1",
        models: ["img-v1", "img-v2"],
        capabilities: {
          generate: {
            supportsResolution: true,
          },
          edit: {
            enabled: true,
            maxInputImages: 3,
          },
          geometry: {
            resolutions: ["1K", "2K"],
          },
        },
        generateImage: async () => ({
          images: [{ buffer: Buffer.from("x"), mimeType: "image/png" }],
        }),
      },
    });
    resolveRuntimePluginRegistryMock.mockReturnValue(pluginRegistry);

    expect(listRuntimeImageGenerationProviders()).toMatchObject([
      {
        id: "image-plugin",
        defaultModel: "img-v1",
        models: ["img-v1", "img-v2"],
        capabilities: {
          generate: {
            supportsResolution: true,
          },
          edit: {
            enabled: true,
            maxInputImages: 3,
          },
          geometry: {
            resolutions: ["1K", "2K"],
          },
        },
      },
    ]);
  });

  it("explains native image-generation config and provider auth when no model is configured", async () => {
    const pluginRegistry = createEmptyPluginRegistry();
    pluginRegistry.imageGenerationProviders.push(
      {
        pluginId: "google",
        pluginName: "Google",
        source: "test",
        provider: {
          id: "google",
          defaultModel: "gemini-3-pro-image-preview",
          capabilities: {
            generate: {},
            edit: { enabled: false },
          },
          generateImage: async () => ({
            images: [{ buffer: Buffer.from("x"), mimeType: "image/png" }],
          }),
        },
      },
      {
        pluginId: "openai",
        pluginName: "OpenAI",
        source: "test",
        provider: {
          id: "openai",
          defaultModel: "gpt-image-1",
          capabilities: {
            generate: {},
            edit: { enabled: false },
          },
          generateImage: async () => ({
            images: [{ buffer: Buffer.from("x"), mimeType: "image/png" }],
          }),
        },
      },
    );
    resolveRuntimePluginRegistryMock.mockReturnValue(pluginRegistry);

    const promise = generateImage({ cfg: {} as OpenClawConfig, prompt: "draw a cat" });

    await expect(promise).rejects.toThrow("No image-generation model configured.");
    await expect(promise).rejects.toThrow(
      'Set agents.defaults.imageGenerationModel.primary to a provider/model like "',
    );
    await expect(promise).rejects.toThrow("google: GEMINI_API_KEY / GOOGLE_API_KEY");
    await expect(promise).rejects.toThrow("openai: OPENAI_API_KEY");
  });

  it("does not crash on prototype-like provider ids in auth hints", async () => {
    const pluginRegistry = createEmptyPluginRegistry();
    pluginRegistry.imageGenerationProviders.push({
      pluginId: "proto-provider",
      pluginName: "Proto Provider",
      source: "test",
      provider: {
        id: "__proto__",
        defaultModel: "proto-v1",
        capabilities: {
          generate: {},
          edit: { enabled: false },
        },
        generateImage: async () => ({
          images: [{ buffer: Buffer.from("x"), mimeType: "image/png" }],
        }),
      },
    });
    resolveRuntimePluginRegistryMock.mockReturnValue(pluginRegistry);

    await expect(
      generateImage({ cfg: {} as OpenClawConfig, prompt: "draw a cat" }),
    ).rejects.toThrow("No image-generation model configured.");
  });
});
