import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { act } from "react";
import { createRoot, type Root } from "react-dom/client";
import { MemoryRouter } from "react-router-dom";
import { afterEach, describe, expect, it, vi } from "vitest";
import { OrgPickerEmbedPage } from "./OrgPickerPage";

const adminApiMocks = vi.hoisted(() => ({
  fetchAllTenants: vi.fn(),
  fetchOrgChartSnapshot: vi.fn(),
  fetchPublicOrgChart: vi.fn(),
  fetchUsers: vi.fn(),
}));

vi.mock("../../../lib/adminApi", () => adminApiMocks);

globalThis.IS_REACT_ACT_ENVIRONMENT = true;

const now = "2026-06-10T00:00:00.000Z";

function tenant({
  id,
  name,
  slug,
  type,
  parentId,
}: {
  id: string;
  name: string;
  slug: string;
  type: string;
  parentId?: string;
}) {
  return {
    id,
    type,
    name,
    slug,
    description: "",
    status: "active",
    parentId,
    memberCount: 0,
    createdAt: now,
    updatedAt: now,
  };
}

function user({
  id,
  name,
  tenantSlug,
}: {
  id: string;
  name: string;
  tenantSlug: string;
}) {
  return {
    id,
    email: `${id}@example.com`,
    name,
    role: "user",
    status: "active",
    tenantSlug,
    createdAt: now,
    updatedAt: now,
  };
}

const snapshot = {
  tenants: [
    tenant({
      id: "group-1",
      name: "Hanmac Family",
      slug: "hanmac-family",
      type: "COMPANY_GROUP",
    }),
    tenant({
      id: "company-1",
      name: "Snapshot Company",
      slug: "snapshot-company",
      type: "COMPANY",
      parentId: "group-1",
    }),
  ],
  users: [
    user({
      id: "user-1",
      name: "Snapshot User",
      tenantSlug: "snapshot-company",
    }),
  ],
};

const renderedPickers: ReturnType<typeof renderPicker>[] = [];

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

  act(() => {
    root.render(
      <QueryClientProvider client={queryClient}>
        <MemoryRouter initialEntries={[initialEntry]}>
          <OrgPickerEmbedPage />
        </MemoryRouter>
      </QueryClientProvider>,
    );
  });

  const rendered = { container, queryClient, root };
  renderedPickers.push(rendered);
  return rendered;
}

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

async function waitForExpect(assertion: () => void) {
  let lastError: unknown;

  for (let attempt = 0; attempt < 20; attempt += 1) {
    await flushQueries();
    try {
      assertion();
      return;
    } catch (error) {
      lastError = error;
    }
  }

  throw lastError;
}

function cleanupRendered({
  container,
  queryClient,
  root,
}: {
  container: HTMLDivElement;
  queryClient: QueryClient;
  root: Root;
}) {
  act(() => {
    root.unmount();
  });
  queryClient.clear();
  container.remove();
}

describe("OrgPickerEmbedPage orgchart data source", () => {
  afterEach(() => {
    while (renderedPickers.length > 0) {
      const rendered = renderedPickers.pop();
      if (rendered) cleanupRendered(rendered);
    }
    vi.clearAllMocks();
  });

  it("uses the authenticated orgchart snapshot instead of legacy tenant and user list APIs", async () => {
    adminApiMocks.fetchOrgChartSnapshot.mockResolvedValue(snapshot);
    adminApiMocks.fetchPublicOrgChart.mockResolvedValue(snapshot);
    adminApiMocks.fetchAllTenants.mockResolvedValue({
      items: [],
      limit: 100,
      offset: 0,
      total: 0,
    });
    adminApiMocks.fetchUsers.mockResolvedValue({
      items: [],
      limit: 5000,
      offset: 0,
      total: 0,
    });

    const rendered = renderPicker("/embed/picker?select=both");

    await waitForExpect(() => {
      expect(adminApiMocks.fetchOrgChartSnapshot).toHaveBeenCalledTimes(1);
      expect(adminApiMocks.fetchPublicOrgChart).not.toHaveBeenCalled();
      expect(adminApiMocks.fetchAllTenants).not.toHaveBeenCalled();
      expect(adminApiMocks.fetchUsers).not.toHaveBeenCalledWith(5000, 0);
      expect(rendered.container.textContent).toContain("Snapshot Company");
      expect(rendered.container.textContent).toContain("Snapshot User");
    });
  });

  it("uses the public orgchart snapshot when a share token is present", async () => {
    adminApiMocks.fetchOrgChartSnapshot.mockResolvedValue(snapshot);
    adminApiMocks.fetchPublicOrgChart.mockResolvedValue(snapshot);

    const rendered = renderPicker(
      "/embed/picker?token=public-token&select=user",
    );

    await waitForExpect(() => {
      expect(adminApiMocks.fetchPublicOrgChart).toHaveBeenCalledTimes(1);
      expect(adminApiMocks.fetchPublicOrgChart).toHaveBeenCalledWith(
        "public-token",
      );
      expect(adminApiMocks.fetchOrgChartSnapshot).not.toHaveBeenCalled();
      expect(adminApiMocks.fetchAllTenants).not.toHaveBeenCalled();
      expect(adminApiMocks.fetchUsers).not.toHaveBeenCalledWith(5000, 0);
      expect(rendered.container.textContent).toContain("Snapshot User");
    });
  });

  it("allows tenant checks in user-only multi mode but confirms descendant users only", async () => {
    adminApiMocks.fetchOrgChartSnapshot.mockResolvedValue(snapshot);
    const postMessageSpy = vi.spyOn(window.parent, "postMessage");

    const rendered = renderPicker(
      "/embed/picker?mode=multiple&select=user&includeDescendants=true",
    );

    await waitForExpect(() => {
      expect(rendered.container.textContent).toContain("Snapshot Company");
      expect(rendered.container.textContent).toContain("Snapshot User");
    });

    const tenantCheckbox = rendered.container.querySelector<HTMLInputElement>(
      'input[aria-label="Snapshot Company 선택"]',
    );
    expect(tenantCheckbox).not.toBeNull();

    await act(async () => {
      tenantCheckbox?.click();
    });

    const confirmButton = Array.from(
      rendered.container.querySelectorAll("button"),
    ).find((button) => button.textContent?.includes("선택 완료"));
    expect(confirmButton).toBeDefined();

    await act(async () => {
      confirmButton?.click();
    });

    expect(postMessageSpy).toHaveBeenCalledWith(
      {
        type: "orgfront:picker:confirm",
        payload: {
          mode: "multiple",
          selections: [
            {
              type: "user",
              id: "user-1",
              name: "Snapshot User",
              email: "user-1@example.com",
            },
          ],
        },
      },
      "*",
    );
  });
});
