import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { createRoot, type Root } from "react-dom/client";
import { act } from "react-dom/test-utils";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { ClientFederationPage } from "./ClientFederationPage";

let params: { id?: string } = { id: "client-a" };
const listIdpConfigsMock = vi.fn();
const createIdpConfigMock = vi.fn();

vi.mock("react-router-dom", async () => {
  const actual =
    await vi.importActual<typeof import("react-router-dom")>(
      "react-router-dom",
    );
  return {
    ...actual,
    useParams: () => params,
  };
});

vi.mock("../../../lib/devApi", () => ({
  listIdpConfigsForClient: (clientId: string) => listIdpConfigsMock(clientId),
  createIdpConfigForClient: (payload: unknown) => createIdpConfigMock(payload),
}));

vi.mock("../../../lib/i18n", () => ({
  t: (key: string, fallback?: string, vars?: Record<string, unknown>) => {
    let text = fallback ?? key;
    for (const [name, value] of Object.entries(vars ?? {})) {
      text = text.replaceAll(`{{${name}}}`, String(value));
    }
    return text;
  },
}));

const roots: Root[] = [];

afterEach(() => {
  for (const root of roots.splice(0)) {
    act(() => {
      root.unmount();
    });
  }
  vi.clearAllMocks();
  document.body.innerHTML = "";
});

beforeEach(() => {
  params = { id: "client-a" };
  listIdpConfigsMock.mockResolvedValue([
    {
      id: "idp-1",
      client_id: "client-a",
      provider_type: "oidc",
      display_name: "Workspace OIDC",
      status: "active",
      issuer_url: "https://accounts.example",
      oidc_client_id: "oidc-client",
      scopes: "openid email profile",
      createdAt: "2026-05-01T00:00:00Z",
      updatedAt: "2026-05-01T00:00:00Z",
    },
  ]);
  createIdpConfigMock.mockResolvedValue({
    id: "idp-2",
    client_id: "client-a",
    provider_type: "oidc",
    display_name: "New Provider",
    status: "active",
    createdAt: "2026-05-02T00:00:00Z",
    updatedAt: "2026-05-02T00:00:00Z",
  });
});

async function setInputValue(input: HTMLInputElement, value: string) {
  const descriptor = Object.getOwnPropertyDescriptor(
    HTMLInputElement.prototype,
    "value",
  );
  descriptor?.set?.call(input, value);
  input.dispatchEvent(new Event("input", { bubbles: true }));
  await new Promise((resolve) => setTimeout(resolve, 0));
}

async function renderPage() {
  const container = document.createElement("div");
  document.body.appendChild(container);
  const root = createRoot(container);
  roots.push(root);
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: { retry: false },
      mutations: { retry: false },
    },
  });

  await act(async () => {
    root.render(
      <QueryClientProvider client={queryClient}>
        <ClientFederationPage />
      </QueryClientProvider>,
    );
  });

  await act(async () => {
    await new Promise((resolve) => setTimeout(resolve, 0));
  });

  return container;
}

describe("ClientFederationPage", () => {
  it("shows a missing client id message when no route param exists", async () => {
    params = {};
    const container = await renderPage();
    expect(container.textContent).toContain("Client ID is missing");
  });

  it("opens the create modal and submits a new IdP config", async () => {
    const container = await renderPage();
    expect(container.textContent).toContain("Workspace OIDC");

    const addButton = Array.from(container.querySelectorAll("button")).find(
      (button) => button.textContent === "Add Provider",
    );
    expect(addButton).toBeTruthy();

    await act(async () => {
      addButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
    });

    expect(container.textContent).toContain("Add Identity Provider");

    const displayName = container.querySelector(
      'input[name="display_name"]',
    ) as HTMLInputElement | null;
    const issuerUrl = container.querySelector(
      'input[name="issuer_url"]',
    ) as HTMLInputElement | null;
    const clientId = container.querySelector(
      'input[name="oidc_client_id"]',
    ) as HTMLInputElement | null;
    const clientSecret = container.querySelector(
      'input[name="oidc_client_secret"]',
    ) as HTMLInputElement | null;

    if (!displayName || !issuerUrl || !clientId || !clientSecret) {
      throw new Error("Expected federation form inputs to be rendered");
    }

    await act(async () => {
      await setInputValue(displayName, "New Provider");
      await setInputValue(issuerUrl, "https://login.example");
      await setInputValue(clientId, "client-oidc");
      await setInputValue(clientSecret, "secret-value");
    });

    const submitButton = Array.from(container.querySelectorAll("button")).find(
      (button) => button.textContent === "Save Configuration",
    );
    expect(submitButton).toBeTruthy();

    await act(async () => {
      submitButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
      await new Promise((resolve) => setTimeout(resolve, 0));
    });

    expect(createIdpConfigMock).toHaveBeenCalledWith(
      expect.objectContaining({
        client_id: "client-a",
        display_name: "New Provider",
        issuer_url: "https://login.example",
        oidc_client_id: "client-oidc",
        oidc_client_secret: "secret-value",
      }),
    );
  });
});
