import React from "react";
import "@testing-library/jest-dom";
import { fireEvent, render, screen, waitFor, within } from "@testing-library/react";
import { CssVarsProvider } from "@mui/material/styles";

import { api } from "./api";
import { ConfirmProvider } from "./confirm";
import { I18nProvider } from "./i18n/I18nProvider";
import { PromptProvider } from "./prompt";
import { getTheme } from "./theme";
import { ToastProvider } from "./toast";
import AdminUsersPage from "./views/AdminUsersPage";

jest.mock("./api", () => ({
  api: { get: jest.fn(), put: jest.fn(), post: jest.fn(), delete: jest.fn() },
  getApiErrorMessage: (_error: unknown, fallback: string) => fallback,
}));

const mockedApi = api as jest.Mocked<typeof api>;

function renderPage(users: unknown[]) {
  mockedApi.get.mockResolvedValue({ data: users } as any);
  mockedApi.put.mockResolvedValue({ data: null } as any);
  mockedApi.delete.mockResolvedValue({ data: null } as any);
  render(
    <CssVarsProvider theme={getTheme("light") as any} defaultMode="light">
      <I18nProvider>
        <ToastProvider>
          <ConfirmProvider>
            <PromptProvider>
              <AdminUsersPage />
            </PromptProvider>
          </ConfirmProvider>
        </ToastProvider>
      </I18nProvider>
    </CssVarsProvider>,
  );
}

beforeEach(() => {
  jest.clearAllMocks();
  Object.defineProperty(window, "matchMedia", {
    configurable: true,
    value: jest.fn().mockImplementation(() => ({
      matches: true,
      addEventListener: jest.fn(),
      removeEventListener: jest.fn(),
      addListener: jest.fn(),
      removeListener: jest.fn(),
    })),
  });
});

test("requires explicit confirmation before removing your own admin role", async () => {
  renderPage([{ id: "me", email: "me@example.com", userName: "me", roles: ["Admin"], emailConfirmed: true, isCurrentUser: true, canRemoveAdmin: true }]);

  fireEvent.click(await screen.findByRole("button", { name: "Remove admin" }));
  expect(await screen.findByText(/you will immediately lose access/i)).toBeInTheDocument();

  fireEvent.click(screen.getByRole("button", { name: "Cancel" }));
  expect(mockedApi.put).not.toHaveBeenCalled();
  await waitFor(() => expect(screen.queryByRole("dialog")).not.toBeInTheDocument());

  fireEvent.click(screen.getByRole("button", { name: "Remove admin" }));
  fireEvent.click(within(await screen.findByRole("dialog")).getByRole("button", { name: "Remove admin" }));

  await waitFor(() => expect(mockedApi.put).toHaveBeenCalledWith("/users/me/roles", { roles: [] }));
});

test("warns before demoting another administrator", async () => {
  renderPage([{ id: "other", email: "other@example.com", userName: "other", roles: ["Admin"], emailConfirmed: true, isCurrentUser: false, canRemoveAdmin: true }]);

  fireEvent.click(await screen.findByRole("button", { name: "Remove admin" }));

  expect(await screen.findByText(/they will immediately lose access/i)).toBeInTheDocument();
  fireEvent.click(screen.getByRole("button", { name: "Cancel" }));
  expect(mockedApi.put).not.toHaveBeenCalled();
});

test("disables demotion and deletion for the final administrator", async () => {
  renderPage([{ id: "me", email: "me@example.com", userName: "me", roles: ["Admin"], emailConfirmed: true, isCurrentUser: true, canRemoveAdmin: false }]);

  expect(await screen.findByRole("button", { name: "Remove admin" })).toBeDisabled();
  expect(screen.getByRole("button", { name: "Delete" })).toBeDisabled();
});

test("sends the exact account email after confirming deletion", async () => {
  renderPage([{ id: "other", email: "other@example.com", userName: "other", roles: [], emailConfirmed: true, isCurrentUser: false, canRemoveAdmin: true }]);

  fireEvent.click(await screen.findByRole("button", { name: "Delete" }));
  fireEvent.click(within(await screen.findByRole("dialog")).getByRole("button", { name: "Delete" }));

  await waitFor(() => expect(mockedApi.delete).toHaveBeenCalledWith("/users/other", {
    headers: { "X-Confirm-Account-Deletion": "other@example.com" },
  }));
});
