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

const fetchClientMock = vi.fn();
const fetchConsentsMock = vi.fn();
const fetchRPUserMetadataMock = vi.fn();
const updateRPUserMetadataMock = vi.fn();
const revokeConsentMock = vi.fn();

vi.mock("../../lib/devApi", () => ({
  fetchClient: (...args: unknown[]) => fetchClientMock(...args),
  fetchConsents: (...args: unknown[]) => fetchConsentsMock(...args),
  fetchRPUserMetadata: (...args: unknown[]) => fetchRPUserMetadataMock(...args),
  updateRPUserMetadata: (...args: unknown[]) =>
    updateRPUserMetadataMock(...args),
  revokeConsent: (...args: unknown[]) => revokeConsentMock(...args),
}));

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[] = [];

const clientDetail = {
  client: {
    id: "client-a",
    name: "Claims App",
    type: "private" as const,
    status: "active" as const,
    redirectUris: ["https://rp.example.com/callback"],
    scopes: ["openid", "profile"],
    tokenEndpointAuthMethod: "client_secret_basic",
    metadata: {
      id_token_claims: [
        {
          namespace: "rp_claims",
          key: "license",
          value: "12345678",
          valueType: "text",
          readPermission: "admin_only",
          writePermission: "admin_only",
        },
      ],
    },
  },
  endpoints: {
    discovery: "https://issuer/.well-known/openid-configuration",
    issuer: "https://issuer",
    authorization: "https://issuer/oauth2/auth",
    token: "https://issuer/oauth2/token",
    userinfo: "https://issuer/userinfo",
  },
};

function buildMetadata() {
  return {
    license: "abcd",
    license_permissions: {
      readPermission: "user_and_admin",
      writePermission: "admin_only",
    },
  };
}

async function flush() {
  await act(async () => {
    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}>
        <MemoryRouter initialEntries={["/clients/client-a/consents"]}>
          <Routes>
            <Route
              path="/clients/:id/consents"
              element={<ClientConsentsPage />}
            />
          </Routes>
        </MemoryRouter>
      </QueryClientProvider>,
    );
  });
  await flush();

  return { container };
}

describe("ClientConsentsPage RP custom claims", () => {
  beforeEach(() => {
    fetchClientMock.mockResolvedValue(clientDetail);
    fetchConsentsMock.mockResolvedValue({
      items: [
        {
          subject: "user-1",
          userName: "Consent User",
          clientId: "client-a",
          clientName: "Claims App",
          grantedScopes: ["openid", "profile"],
          authenticatedAt: "2026-06-11T09:00:00Z",
          createdAt: "2026-06-10T09:00:00Z",
          status: "active",
          tenantId: "tenant-1",
          tenantName: "Hanmac",
          rpMetadata: buildMetadata(),
        },
      ],
    });
    fetchRPUserMetadataMock.mockResolvedValue({
      clientId: "client-a",
      userId: "user-1",
      metadata: buildMetadata(),
    });
    updateRPUserMetadataMock.mockResolvedValue({
      clientId: "client-a",
      userId: "user-1",
      metadata: buildMetadata(),
    });
    revokeConsentMock.mockResolvedValue(undefined);
  });

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

  it("removes the RP custom claim permission selectors while keeping claim data editable", async () => {
    const { container } = await renderPage();

    const editButton = Array.from(container.querySelectorAll("button")).find(
      (button) =>
        button.textContent?.includes("사용자 Claim 설정") ||
        button.textContent?.includes("User Claim Settings"),
    );
    expect(editButton).toBeDefined();

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

    expect(
      container.querySelectorAll('select[aria-label="읽기 권한"]'),
    ).toHaveLength(0);
    expect(
      container.querySelectorAll('select[aria-label="쓰기 권한"]'),
    ).toHaveLength(0);
    expect(container.textContent).toContain("license");
    expect(container.textContent).toContain("abcd");

    const saveButton = Array.from(container.querySelectorAll("button")).find(
      (button) =>
        button.textContent?.includes("Claim 저장") ||
        button.textContent?.includes("Save"),
    );
    expect(saveButton).toBeDefined();

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

    expect(updateRPUserMetadataMock).toHaveBeenCalledWith(
      "client-a",
      "user-1",
      expect.objectContaining({
        license: "abcd",
        license_permissions: {
          readPermission: "user_and_admin",
          writePermission: "admin_only",
        },
      }),
    );
  });

  it("keeps date claim inputs and timezone selectors on the same row", async () => {
    fetchClientMock.mockResolvedValue({
      ...clientDetail,
      client: {
        ...clientDetail.client,
        metadata: {
          id_token_claims: [
            {
              namespace: "rp_claims",
              key: "contract_date",
              value: "",
              valueType: "date",
              readPermission: "admin_only",
              writePermission: "admin_only",
            },
          ],
        },
      },
    });
    fetchConsentsMock.mockResolvedValue({
      items: [
        {
          subject: "user-1",
          userName: "Consent User",
          clientId: "client-a",
          clientName: "Claims App",
          grantedScopes: ["openid", "profile"],
          authenticatedAt: "2026-06-11T09:00:00Z",
          createdAt: "2026-06-10T09:00:00Z",
          status: "active",
          tenantId: "tenant-1",
          tenantName: "Hanmac",
          rpMetadata: {
            contract_date: 1781017200,
            contract_date_permissions: {
              readPermission: "admin_only",
              writePermission: "admin_only",
            },
          },
        },
      ],
    });
    fetchRPUserMetadataMock.mockResolvedValue({
      clientId: "client-a",
      userId: "user-1",
      metadata: {
        contract_date: 1781017200,
        contract_date_permissions: {
          readPermission: "admin_only",
          writePermission: "admin_only",
        },
      },
    });

    const { container } = await renderPage();

    const editButton = Array.from(container.querySelectorAll("button")).find(
      (button) =>
        button.textContent?.includes("사용자 Claim 설정") ||
        button.textContent?.includes("User Claim Settings"),
    );

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

    const dateInput = container.querySelector(
      'input[aria-label="contract_date date"]',
    );
    const timeZoneSelect = container.querySelector(
      'select[aria-label="contract_date timezone"]',
    );

    expect(dateInput).not.toBeNull();
    expect(timeZoneSelect).not.toBeNull();
    expect(dateInput?.parentElement).toBe(timeZoneSelect?.parentElement);
    expect(dateInput?.parentElement?.className).toContain("items-center");
    expect(dateInput?.parentElement?.className).not.toContain("flex-col");
  });
});
