/**
 * #43, F3 — `ProjectsView` wires the `gitRepository` `when` signal to a real
 * check (`GitGateway.branches`), not a hardcoded `false` (Architect-mandated,
 * blocking for #43 closure). This exercises a plugin menu item declared
 * `when: "projectOpen && gitRepository"` end-to-end: disabled for a project
 * whose git check fails (not a repo), enabled for one whose check succeeds.
 */
import { describe, it, expect } from "vitest";
import { render, screen, waitFor, fireEvent } from "@testing-library/react";

import {
  MockAgentGateway,
  MockDesktopServerGateway,
  MockProfileGateway,
  MockProjectGateway,
  MockSystemGateway,
  MockTemplateGateway,
  MockWindowGateway,
  MockWorkStateGateway,
} from "@/adapters/mock";
import type { GatewayError, GitBranches, GitCommit, GitFileStatus, GraphCommit } from "@/domain";
import type { Gateways, GitGateway } from "@/ports";
import { DIProvider } from "@/app/di";
import {
  PluginCommandRegistry,
  PluginLayoutRegistry,
  PluginMenuRegistry,
  PluginRuntimeRegistry,
  type LoadedPlugin,
} from "@/plugins/runtime";
import { PluginRuntimeProvider } from "@/features/plugins";
import { ProjectsView } from "./ProjectsView";

/** Minimal `GitGateway` double whose `branches()` always rejects — "not a repo". */
class NonRepoGitGateway implements GitGateway {
  status(): Promise<GitFileStatus[]> {
    return Promise.resolve([]);
  }
  stage(): Promise<void> {
    return Promise.resolve();
  }
  unstage(): Promise<void> {
    return Promise.resolve();
  }
  commit(): Promise<GitCommit> {
    return Promise.reject({ code: "NOT_IMPLEMENTED", message: "n/a" } satisfies GatewayError);
  }
  branches(): Promise<GitBranches> {
    const err: GatewayError = { code: "NOT_A_REPOSITORY", message: "not a git repository" };
    return Promise.reject(err);
  }
  checkout(): Promise<void> {
    return Promise.resolve();
  }
  log(): Promise<GitCommit[]> {
    return Promise.resolve([]);
  }
  init(): Promise<void> {
    return Promise.resolve();
  }
  graph(): Promise<GraphCommit[]> {
    return Promise.resolve([]);
  }
}

/** Minimal `GitGateway` double whose `branches()` always resolves — a real repo. */
class RealRepoGitGateway extends NonRepoGitGateway {
  override branches(): Promise<GitBranches> {
    return Promise.resolve({ branches: ["main"], current: "main" });
  }
}

function gitAwarePlugin(): LoadedPlugin {
  const contributes = {
    menus: [],
    menuItems: [
      {
        id: "dev.acme.gitgraph.open",
        targetMenuId: "panels" as const,
        label: "Ouvrir le graphe Git",
        command: "dev.acme.gitgraph.open",
        when: "projectOpen && gitRepository",
      },
    ],
    layouts: [],
    mcpServers: [],
  };
  return {
    pluginId: "dev.acme.gitgraph",
    displayName: "Git Graph",
    contributes,
    commands: new PluginCommandRegistry(
      "dev.acme.gitgraph",
      new Set(["dev.acme.gitgraph.open"]),
    ),
    layouts: new PluginLayoutRegistry("dev.acme.gitgraph", new Set()),
    menu: new PluginMenuRegistry("dev.acme.gitgraph"),
    dispose: async () => {},
  };
}

function renderWithPlugin(git: GitGateway) {
  const agentGateway = new MockAgentGateway();
  const gateways = {
    system: new MockSystemGateway(),
    project: new MockProjectGateway(),
    agent: agentGateway,
    profile: new MockProfileGateway(),
    template: new MockTemplateGateway(agentGateway),
    git,
    workState: new MockWorkStateGateway(),
    window: new MockWindowGateway(),
    desktopServer: new MockDesktopServerGateway(),
  } as unknown as Gateways;

  const registry = new PluginRuntimeRegistry();
  registry.add(gitAwarePlugin());

  return render(
    <DIProvider gateways={gateways}>
      <PluginRuntimeProvider value={{ registry, failures: [], pending: [], loading: false }}>
        <ProjectsView />
      </PluginRuntimeProvider>
    </DIProvider>,
  );
}

async function waitForIdle() {
  await waitFor(() =>
    expect(
      (screen.getByRole("button", { name: "Refresh" }) as HTMLButtonElement).disabled,
    ).toBe(false),
  );
}

async function createProject(name: string, root: string) {
  await waitForIdle();
  fireEvent.change(screen.getByLabelText("project name"), { target: { value: name } });
  fireEvent.change(screen.getByLabelText("project root"), { target: { value: root } });
  fireEvent.click(screen.getByRole("button", { name: "Create project" }));
}

function panelsMenuItem(): HTMLButtonElement {
  return screen.getByRole("button", { name: /Ouvrir le graphe Git/ }) as HTMLButtonElement;
}

describe("ProjectsView — plugin `gitRepository` when-context wiring", () => {
  it("disables the item when the project's git check fails (not a repo)", async () => {
    renderWithPlugin(new NonRepoGitGateway());
    await createProject("alpha", "/home/me/non-repo");
    await screen.findByRole("tab");

    fireEvent.click(screen.getByRole("button", { name: "Panneaux" }));
    await waitFor(() => {
      expect(panelsMenuItem().disabled).toBe(true);
    });
  });

  it("enables the item once the project's git check succeeds (real repo)", async () => {
    renderWithPlugin(new RealRepoGitGateway());
    await createProject("beta", "/home/me/real-repo");
    await screen.findByRole("tab");

    fireEvent.click(screen.getByRole("button", { name: "Panneaux" }));
    await waitFor(() => {
      expect(panelsMenuItem().disabled).toBe(false);
    });
  });
});
