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

const navigateMock = vi.fn();
const fetchMeMock = vi.fn();
const fetchDevAuditLogsMock = vi.fn();
let gateState = {
  hasDeveloperAccess: true,
  isDeveloperRequestPending: false,
  canRequestDeveloperAccess: false,
  isLoadingDeveloperAccessGate: false,
};

vi.mock("react-oidc-context", () => ({
  useAuth: () => ({
    isAuthenticated: true,
    isLoading: false,
    user: {
      access_token: "access-token",
      profile: {
        role: "super_admin",
        tenant_id: "tenant-1",
      },
    },
  }),
}));

vi.mock("react-router-dom", () => ({
  useNavigate: () => navigateMock,
}));

vi.mock("../developer-access/developerAccessGate", () => ({
  useDeveloperAccessGate: () => gateState,
}));

vi.mock("../../lib/devApi", () => ({
  fetchDevAuditLogs: (...args: unknown[]) => fetchDevAuditLogsMock(...args),
}));

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

vi.mock("../../../../common/core/components/audit", () => ({
  AuditLogTable: ({
    logs,
    onLoadMore,
  }: {
    logs: Array<{ event_id: string }>;
    onLoadMore: () => void;
  }) => (
    <div>
      <div>table:{logs.length}</div>
      <button type="button" onClick={onLoadMore}>
        Load more
      </button>
    </div>
  ),
}));

vi.mock("../../components/common/ForbiddenMessage", () => ({
  ForbiddenMessage: ({ resourceToken }: { resourceToken: string }) => (
    <div>Forbidden:{resourceToken}</div>
  ),
}));

const roots: Root[] = [];

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

beforeEach(() => {
  gateState = {
    hasDeveloperAccess: true,
    isDeveloperRequestPending: false,
    canRequestDeveloperAccess: false,
    isLoadingDeveloperAccessGate: false,
  };
  fetchMeMock.mockResolvedValue({
    id: "user-1",
    role: "super_admin",
  });
  fetchDevAuditLogsMock.mockResolvedValue({
    items: [
      {
        event_id: "evt-1",
        timestamp: "2026-05-28T06:07:18.000Z",
        user_id: "user-1",
        event_type: "CLIENT_UPDATE",
        status: "success",
        ip_address: "127.0.0.1",
        user_agent: "Vitest",
        details: JSON.stringify({
          action: "업데이트",
          target_id: "client-a",
        }),
      },
    ],
    limit: 50,
  });
});

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 },
    },
  });

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

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

  return container;
}

describe("AuditLogsPage", () => {
  it("shows the loading gate state", async () => {
    gateState = {
      hasDeveloperAccess: false,
      isDeveloperRequestPending: false,
      canRequestDeveloperAccess: false,
      isLoadingDeveloperAccessGate: true,
    };

    const container = await renderPage();
    expect(container.textContent).toContain("로딩 중...");
  });

  it("renders the access request card when access is denied", async () => {
    gateState = {
      hasDeveloperAccess: false,
      isDeveloperRequestPending: false,
      canRequestDeveloperAccess: true,
      isLoadingDeveloperAccessGate: false,
    };

    const container = await renderPage();
    expect(container.textContent).toContain(
      "감사 로그는 개발자 권한이 있어야 볼 수 있습니다.",
    );

    const button = Array.from(container.querySelectorAll("button")).find(
      (item) => item.textContent?.includes("개발자 권한 신청"),
    );
    expect(button).toBeTruthy();

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

    expect(navigateMock).toHaveBeenCalledWith("/developer-requests");
  });

  it("exports the fetched logs as CSV", async () => {
    const createObjectURL = vi
      .spyOn(URL, "createObjectURL")
      .mockReturnValue("blob:csv");
    const revokeObjectURL = vi.spyOn(URL, "revokeObjectURL").mockReturnValue();
    const clickSpy = vi
      .spyOn(HTMLAnchorElement.prototype, "click")
      .mockImplementation(() => {});

    const container = await renderPage();
    expect(container.textContent).toContain("table:1");

    const button = Array.from(container.querySelectorAll("button")).find(
      (item) => item.textContent === "CSV 내보내기",
    );
    expect(button).toBeTruthy();

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

    expect(createObjectURL).toHaveBeenCalled();
    expect(clickSpy).toHaveBeenCalled();
    expect(revokeObjectURL).toHaveBeenCalledWith("blob:csv");
  });

  it("renders the forbidden state on 403 errors", async () => {
    fetchDevAuditLogsMock.mockRejectedValueOnce({
      response: { status: 403 },
      message: "Forbidden",
    });

    const container = await renderPage();
    expect(container.textContent).toContain("Forbidden:audit");
  });
});
