/**
 * F4 — `PluginLayoutSelectorSection` (carnet §10 F4 acceptance criteria:
 * "layout disabled non proposé comme nouveau choix").
 */
import { describe, expect, it } from "vitest";
import { render, screen, fireEvent } from "@testing-library/react";

import {
  PluginCommandRegistry,
  PluginLayoutRegistry,
  PluginMenuRegistry,
  PluginRuntimeRegistry,
  type LoadedPlugin,
} from "@/plugins/runtime";
import type { PluginContributionDto } from "@/domain";
import { PluginLayoutSelectorSection, listPluginLayoutChoices } from "./PluginLayoutSelectorSection";

function stubPlugin(pluginId: string, displayName: string, layoutType: string): LoadedPlugin {
  const contributes = {
    menus: [],
    menuItems: [],
    layouts: [{ type: layoutType, label: `${displayName} layout`, component: "X" }],
    mcpServers: [],
  };
  return {
    pluginId,
    displayName,
    contributes,
    commands: new PluginCommandRegistry(pluginId, new Set()),
    layouts: new PluginLayoutRegistry(pluginId, new Set([layoutType])),
    menu: new PluginMenuRegistry(pluginId),
    dispose: async () => {},
  };
}

describe("listPluginLayoutChoices / PluginLayoutSelectorSection", () => {
  it("only lists loaded (enabled) plugins' layouts — a disabled plugin is never in the registry", () => {
    const registry = new PluginRuntimeRegistry();
    registry.add(stubPlugin("dev.acme.one", "One", "dev.acme.one.layout"));
    // "dev.acme.two" is disabled ⇒ never loaded ⇒ never added to the registry
    // (carnet §1.3) — nothing to filter here beyond what's already loaded.

    const choices = listPluginLayoutChoices(registry);
    expect(choices).toHaveLength(1);
    expect(choices[0].pluginId).toBe("dev.acme.one");
  });

  it("renders nothing when there are no plugin layouts", () => {
    const { container } = render(
      <PluginLayoutSelectorSection registry={new PluginRuntimeRegistry()} onSelect={() => {}} />,
    );
    expect(container.innerHTML).toBe("");
  });

  it("calls onSelect with the chosen plugin layout", () => {
    const registry = new PluginRuntimeRegistry();
    registry.add(stubPlugin("dev.acme.one", "One", "dev.acme.one.layout"));
    let selected: string | null = null;
    render(
      <PluginLayoutSelectorSection
        registry={registry}
        onSelect={(choice) => {
          selected = choice.layout.type;
        }}
      />,
    );
    fireEvent.click(screen.getByRole("menuitem", { name: /One layout/ }));
    expect(selected).toBe("dev.acme.one.layout");
  });

  it("skips malformed layout contributions instead of throwing during sorting", () => {
    const registry = new PluginRuntimeRegistry();
    const contributes = {
      menus: [],
      menuItems: [],
      layouts: [
        { type: "dev.acme.good", label: "Good layout", component: "X" },
        null,
        { type: "dev.acme.no-label", label: undefined, component: "X" },
        { type: undefined, label: "No type", component: "X" },
      ] as unknown as PluginContributionDto["layouts"],
      mcpServers: [],
    };
    registry.add({
      pluginId: "dev.acme.bad",
      displayName: undefined as unknown as string,
      contributes,
      commands: new PluginCommandRegistry("dev.acme.bad", new Set()),
      layouts: new PluginLayoutRegistry("dev.acme.bad", new Set()),
      menu: new PluginMenuRegistry("dev.acme.bad"),
      dispose: async () => {},
    });

    const choices = listPluginLayoutChoices(registry);
    expect(choices).toHaveLength(1);
    expect(choices[0].layout.label).toBe("Good layout");
    expect(choices[0].pluginDisplayName).toBe("dev.acme.bad");
  });
});
