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

// Mock the runtime package so tests don't need a real remote.
vi.mock("@module-federation/enhanced/runtime", () => {
  return {
    init: vi.fn(),
    loadRemote: vi.fn(),
    registerRemotes: vi.fn(),
  };
});

import { init, loadRemote, registerRemotes } from "@module-federation/enhanced/runtime";

describe("remote-loader", () => {
  beforeEach(async () => {
    const mod = await import("./remote-loader.js");
    mod.__resetRemoteLoaderForTests();
    vi.mocked(init).mockClear();
    vi.mocked(loadRemote).mockClear();
    vi.mocked(registerRemotes).mockClear();
  });

  afterEach(() => {
    vi.resetModules();
  });

  it("registerRemote calls init once for the first remote, then registerRemotes for subsequent", async () => {
    const { registerRemote } = await import("./remote-loader.js");
    registerRemote({ name: "customer-ui", entry: "https://host.example/mf-manifest.json" });
    expect(init).toHaveBeenCalledTimes(1);
    expect(init).toHaveBeenCalledWith({
      name: "aeroflot_flights_host",
      remotes: [{ name: "customer-ui", entry: "https://host.example/mf-manifest.json" }],
    });

    registerRemote({ name: "customer-analytics", entry: "https://host.example/analytics/mf-manifest.json" });
    expect(init).toHaveBeenCalledTimes(1); // still only once
    expect(registerRemotes).toHaveBeenCalledTimes(1);
    expect(registerRemotes).toHaveBeenCalledWith([
      { name: "customer-analytics", entry: "https://host.example/analytics/mf-manifest.json" },
    ]);
  });

  it("loadRemoteModule delegates to loadRemote with the concatenated key", async () => {
    const fakeModule = { default: "hello" };
    vi.mocked(loadRemote).mockResolvedValueOnce(fakeModule);

    const { loadRemoteModule } = await import("./remote-loader.js");
    const result = await loadRemoteModule<{ default: string }>({
      name: "customer-ui",
      module: "./Header",
    });

    expect(loadRemote).toHaveBeenCalledWith("customer-ui/Header");
    expect(result).toBe(fakeModule);
  });

  it("loadRemoteModule rejects with a typed error if the module returns null/undefined", async () => {
    vi.mocked(loadRemote).mockResolvedValueOnce(null);
    const { loadRemoteModule } = await import("./remote-loader.js");
    await expect(
      loadRemoteModule({ name: "customer-ui", module: "./NonExistent" }),
    ).rejects.toThrow(/customer-ui\/NonExistent/);
  });

  it("loadRemoteModule propagates errors from loadRemote", async () => {
    vi.mocked(loadRemote).mockRejectedValueOnce(new Error("boom"));
    const { loadRemoteModule } = await import("./remote-loader.js");
    await expect(
      loadRemoteModule({ name: "customer-ui", module: "./Broken" }),
    ).rejects.toThrow("boom");
  });
});
