/**
 * #23 — detaching a View into its own OS window from `ProjectsView`. The
 * "Window" menu (and the docked/floating header ⤢ control) call the
 * {@link WindowGateway}; the slot becomes "detached" (nothing renders in the
 * main window), and the backend's close event re-toggles it back out of
 * "detached". The one-slot invariant holds throughout.
 */
import { describe, it, expect } from "vitest";
import { render, screen, within, waitFor, fireEvent } from "@testing-library/react";

import {
  MockAgentGateway,
  MockGitGateway,
  MockProfileGateway,
  MockFocusedProjectGateway,
  MockProjectGateway,
  MockSystemGateway,
  MockTemplateGateway,
  MockWindowGateway,
  MockWorkStateGateway,
} from "@/adapters/mock";
import type { Gateways } from "@/ports";
import { DIProvider } from "@/app/di";
import { ProjectsView } from "./ProjectsView";

async function renderWithProject() {
  const project = new MockProjectGateway();
  await project.createProject("alpha", "/p/a");
  const agentGateway = new MockAgentGateway();
  const windowGateway = new MockWindowGateway();
  const gateways = {
    system: new MockSystemGateway(),
    project,
    agent: agentGateway,
    profile: new MockProfileGateway(),
    template: new MockTemplateGateway(agentGateway),
    git: new MockGitGateway(),
    workState: new MockWorkStateGateway(),
    window: windowGateway,
    focusedProject: new MockFocusedProjectGateway(),
  } as unknown as Gateways;
  render(
    <DIProvider gateways={gateways}>
      <ProjectsView />
    </DIProvider>,
  );
  fireEvent.click(await screen.findByRole("button", { name: "Open" }));
  await waitFor(() =>
    expect(within(screen.getByRole("tablist")).getAllByRole("tab")).toHaveLength(1),
  );
  return { windowGateway };
}

/**
 * Picks a placement for a panel from the single « Panneaux » menu (#26): menu →
 * panel entry (opens its placement submenu) → the placement leaf ("Flottant",
 * "Ancré à gauche", "Fenêtre détachée", …).
 */
function pickPlacement(panelTitle: string, placementLabel: string) {
  fireEvent.click(screen.getByRole("button", { name: "Panneaux" }));
  fireEvent.click(screen.getByRole("button", { name: panelTitle }));
  fireEvent.click(screen.getByRole("button", { name: placementLabel }));
}

describe("ProjectsView detach to OS window (#23)", () => {
  it("« Fenêtre détachée » detaches a view: opens the OS window and marks the slot detached", async () => {
    const { windowGateway } = await renderWithProject();

    // A docked view is visible in the main window first…
    pickPlacement("Git", "Ancré à gauche");
    await screen.findByRole("complementary", { name: "left dock" });

    // …then « Panneaux → Git → Fenêtre détachée » pops it out.
    pickPlacement("Git", "Fenêtre détachée");

    await waitFor(() =>
      expect(windowGateway.open.has("git")).toBe(true),
    );
    // The slot is detached ⇒ nothing renders for Git in the main window.
    await waitFor(() =>
      expect(screen.queryByRole("complementary", { name: "left dock" })).toBeNull(),
    );
    expect(screen.queryByRole("dialog", { name: "Git" })).toBeNull();
  });

  it("re-toggles the placement out of detached when the OS window closes", async () => {
    const { windowGateway } = await renderWithProject();

    pickPlacement("Git", "Fenêtre détachée");
    await waitFor(() =>
      expect(windowGateway.open.has("git")).toBe(true),
    );

    // The « Fenêtre détachée » submenu leaf shows the active marker (●) while
    // detached. Re-navigate the submenu each time to read it.
    const openGitSubmenu = () => {
      fireEvent.click(screen.getByRole("button", { name: "Panneaux" }));
      fireEvent.click(screen.getByRole("button", { name: "Git" }));
    };
    const detachedLeaf = () =>
      screen.getByRole("button", { name: "Fenêtre détachée" });

    openGitSubmenu();
    await waitFor(() => expect(detachedLeaf().textContent).toContain("●"));
    fireEvent.click(screen.getByRole("button", { name: "Panneaux" })); // close

    // The backend reports the OS window closed ⇒ the slot leaves "detached".
    windowGateway.simulateOsClose("git");
    openGitSubmenu();
    await waitFor(() =>
      expect(detachedLeaf().textContent).not.toContain("●"),
    );
  });
});
