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 DeveloperRequestPage from "./DeveloperRequestPage";

const fetchDeveloperRequestsMock = vi.fn();
const fetchMyTenantsMock = vi.fn();
const fetchMeMock = vi.fn();
const requestDeveloperAccessMock = vi.fn();

let authState = {
  user: {
    access_token: "access-token",
    profile: {
      role: "user",
      tenant_id: "tenant-1",
      companyCode: "HANMAC",
      name: "Requester",
      email: "requester@example.com",
      phone: "010-1234-5678",
    },
  },
};

vi.mock("react-oidc-context", () => ({
  useAuth: () => authState,
}));

vi.mock("../auth/authApi", () => ({
  fetchMe: () => fetchMeMock(),
}));

vi.mock("../../lib/devApi", () => ({
  fetchDeveloperRequests: () => fetchDeveloperRequestsMock(),
  fetchMyTenants: () => fetchMyTenantsMock(),
  requestDeveloperAccess: (...args: unknown[]) =>
    requestDeveloperAccessMock(...args),
  approveDeveloperRequest: vi.fn(),
  rejectDeveloperRequest: vi.fn(),
  cancelDeveloperRequestApproval: vi.fn(),
}));

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(() => {
  authState = {
    user: {
      access_token: "access-token",
      profile: {
        role: "user",
        tenant_id: "tenant-1",
        companyCode: "HANMAC",
        name: "Requester",
        email: "requester@example.com",
        phone: "010-1234-5678",
      },
    },
  };

  fetchDeveloperRequestsMock.mockResolvedValue([]);
  fetchMyTenantsMock.mockResolvedValue([
    {
      id: "tenant-1",
      name: "Hanmac",
      slug: "hanmac",
      type: "COMPANY",
      parentId: null,
      description: "",
      status: "active",
      memberCount: 10,
      createdAt: "2026-05-01T00:00:00Z",
      updatedAt: "2026-05-01T00:00:00Z",
    },
  ]);
  fetchMeMock.mockResolvedValue({
    id: "user-1",
    name: "Requester",
    email: "requester@example.com",
    phone: "010-1234-5678",
    role: "user",
  });
  requestDeveloperAccessMock.mockResolvedValue({ status: "pending" });
});

async function setTextAreaValue(input: HTMLTextAreaElement, value: string) {
  const descriptor = Object.getOwnPropertyDescriptor(
    HTMLTextAreaElement.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}>
        <DeveloperRequestPage />
      </QueryClientProvider>,
    );
  });

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

  return container;
}

describe("DeveloperRequestPage", () => {
  it("opens the request modal and submits a request", async () => {
    const container = await renderPage();
    expect(container.textContent).toContain("신규 신청하기");

    const actionButton = Array.from(container.querySelectorAll("button")).find(
      (button) => button.textContent?.includes("신규 신청하기"),
    );
    expect(actionButton).toBeTruthy();

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

    expect(container.textContent).toContain("개발자 등록 신청");

    const reasonField = container.querySelector(
      "textarea",
    ) as HTMLTextAreaElement | null;
    if (!reasonField) {
      throw new Error("Expected reason textarea to be rendered");
    }

    await act(async () => {
      await setTextAreaValue(reasonField, "Need RP access");
    });

    const submitButton = Array.from(container.querySelectorAll("button")).find(
      (button) => button.textContent === "신청하기",
    );
    expect(submitButton).toBeTruthy();

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

    expect(requestDeveloperAccessMock).toHaveBeenCalled();
    expect(requestDeveloperAccessMock.mock.calls[0]?.[0]).toEqual({
      name: "Requester",
      organization: "Hanmac",
      reason: "Need RP access",
      tenantId: "tenant-1",
    });
  });

  it("shows a tenant-required notice and hides the request button when tenant is missing", async () => {
    authState = {
      user: {
        access_token: "access-token",
        profile: {
          role: "user",
          companyCode: "HANMAC",
          name: "Requester",
          email: "requester@example.com",
          phone: "010-1234-5678",
        },
      },
    };
    fetchMeMock.mockResolvedValue({
      id: "user-1",
      name: "Requester",
      email: "requester@example.com",
      phone: "010-1234-5678",
      role: "user",
    });

    const container = await renderPage();
    expect(container.textContent).toContain(
      "개발자 권한을 신청하려면 먼저 테넌트에 소속되어 있어야 합니다.",
    );
    expect(container.textContent).not.toContain("신규 신청하기");
    expect(requestDeveloperAccessMock).not.toHaveBeenCalled();
  });
});
