import { describe, expect, it } from "vitest";
import { fireEvent, render, screen, waitFor, within } from "@testing-library/react";

import {
  MockAgentGateway,
  MockPermissionGateway,
  MockProfileGateway,
} from "@/adapters/mock";
import { DIProvider } from "@/app/di";
import type { Gateways } from "@/ports";
import { PermissionsPanel } from "./PermissionsPanel";

const PROJECT_ID = "proj-permissions-test";

async function renderPanel() {
  const agent = new MockAgentGateway();
  const permission = new MockPermissionGateway();
  await agent.createAgent(PROJECT_ID, { name: "Builder", profileId: "p1" });
  const gateways = {
    agent,
    permission,
    profile: new MockProfileGateway(),
  } as unknown as Gateways;

  render(
    <DIProvider gateways={gateways}>
      <PermissionsPanel projectId={PROJECT_ID} />
    </DIProvider>,
  );

  await waitFor(() => {
    expect(screen.getByText("Builder")).toBeTruthy();
  });

  return { agent, permission };
}

function clickChoice(groupName: string, choice: string) {
  const group = screen.getByRole("group", { name: groupName });
  fireEvent.click(within(group).getByRole("button", { name: choice }));
}

describe("PermissionsPanel", () => {
  it("saves project defaults from the compact policy editor", async () => {
    const { permission } = await renderPanel();

    fireEvent.change(screen.getByLabelText("Project defaults fallback"), {
      target: { value: "allow" },
    });
    clickChoice("Read permission", "Allow");
    clickChoice("Bash permission", "Deny");
    fireEvent.click(screen.getByRole("button", { name: "Save" }));

    await waitFor(async () => {
      const doc = await permission.getProjectPermissions(PROJECT_ID);
      expect(doc.projectDefaults?.fallback).toBe("allow");
      expect(doc.projectDefaults?.rules).toEqual([
        { capability: "read", effect: "allow", paths: ["**"] },
        { capability: "executeBash", effect: "deny", commands: [] },
      ]);
    });
  });

  it("saves and clears an agent override", async () => {
    const { permission } = await renderPanel();

    fireEvent.click(screen.getByText("Builder"));
    fireEvent.change(screen.getByLabelText("Override — Builder fallback"), {
      target: { value: "deny" },
    });
    clickChoice("Write permission", "Allow");
    fireEvent.click(screen.getByRole("button", { name: "Save" }));

    await waitFor(async () => {
      const doc = await permission.getProjectPermissions(PROJECT_ID);
      expect(doc.agents?.[0]?.permissions.fallback).toBe("deny");
      expect(doc.agents?.[0]?.permissions.rules).toEqual([
        { capability: "write", effect: "allow", paths: ["**"] },
      ]);
    });

    fireEvent.click(screen.getByRole("button", { name: "Clear" }));

    await waitFor(async () => {
      const doc = await permission.getProjectPermissions(PROJECT_ID);
      expect(doc.agents).toEqual([]);
    });
  });
});
