/**
 * #47 — `ProjectsView` is the single writer of the focused-project channel: it
 * publishes the active project (or `null` when none is active) so detached
 * panel-only windows can follow it. This drives the open → publish → close →
 * publish-null flow and asserts what reaches the {@link FocusedProjectGateway}.
 */
import { describe, it, expect } from "vitest";
import { render, screen, within, waitFor, fireEvent } from "@testing-library/react";

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

async function renderView() {
  const project = new MockProjectGateway();
  const created = await project.createProject("alpha", "/p/a");
  const agentGateway = new MockAgentGateway();
  const focusedProject = new MockFocusedProjectGateway();
  // Record every published focus so we can assert the sequence.
  const published: (FocusedProject | null)[] = [];
  await focusedProject.onFocusedProjectChanged((p) => published.push(p));
  const gateways = {
    system: new MockSystemGateway(),
    project,
    agent: agentGateway,
    profile: new MockProfileGateway(),
    template: new MockTemplateGateway(agentGateway),
    git: new MockGitGateway(),
    workState: new MockWorkStateGateway(),
    window: new MockWindowGateway(),
    focusedProject,
  } as unknown as Gateways;
  render(
    <DIProvider gateways={gateways}>
      <ProjectsView />
    </DIProvider>,
  );
  return { focusedProject, published, projectId: created.id };
}

describe("ProjectsView focus publishing (#47)", () => {
  it("publishes the active project on open and null when it is closed", async () => {
    const { focusedProject, published, projectId } = await renderView();

    // Mount with no active project publishes a `null` focus.
    await waitFor(() => expect(published).toContainEqual(null));

    // Open the project → publishes the { id, name, root } snapshot.
    fireEvent.click(await screen.findByRole("button", { name: "Open" }));
    await waitFor(() =>
      expect(within(screen.getByRole("tablist")).getAllByRole("tab")).toHaveLength(1),
    );
    await waitFor(() =>
      expect(focusedProject.getFocusedProject()).resolves.toEqual({
        id: projectId,
        name: "alpha",
        root: "/p/a",
      }),
    );

    // Close the tab → back to no active project → publishes `null`.
    fireEvent.click(screen.getByRole("button", { name: "close alpha" }));
    await waitFor(() =>
      expect(focusedProject.getFocusedProject()).resolves.toBeNull(),
    );
  });
});
