// Mock react-cool-dimensions to avoid ResizeObserver console.error
jest.mock("react-cool-dimensions", () => ({
  __esModule: true,
  default: () => ({
    observe: jest.fn(),
    unobserve: jest.fn(),
    width: 1000,
    height: 500,
    entry: undefined,
  }),
}));

import { act } from "react";

import { render, fireEvent } from "@testing-library/react";

import fetchMock from "@fetch-mock/jest";

import { MockThemeContext } from "__fixtures__/Theme";
import { ThemeContext } from "Components/Theme";
import { AlertStore } from "Stores/AlertStore";
import { Settings } from "Stores/Settings";
import { SilenceFormStore } from "Stores/SilenceFormStore";
import SilenceModal from ".";

let alertStore: AlertStore;
let settingsStore: Settings;
let silenceFormStore: SilenceFormStore;

beforeEach(() => {
  alertStore = new AlertStore([]);
  settingsStore = new Settings(null);
  silenceFormStore = new SilenceFormStore();

  jest.useFakeTimers();
  fetchMock.mockReset();
  fetchMock.route("*", {
    body: JSON.stringify([]),
  });
});

afterEach(() => {
  jest.restoreAllMocks();
  fetchMock.mockReset();
  document.body.className = "";
});

const renderSilenceModal = () => {
  return render(
    <ThemeContext value={MockThemeContext}>
      <SilenceModal
        alertStore={alertStore}
        silenceFormStore={silenceFormStore}
        settingsStore={settingsStore}
      />
    </ThemeContext>,
  );
};

describe("<SilenceModal />", () => {
  it("only renders FontAwesomeIcon when modal is not shown", () => {
    const { container } = renderSilenceModal();
    expect(container.querySelectorAll("svg")).toHaveLength(1);
    expect(container.querySelector(".modal-content")).toBeNull();
  });

  it("renders a spinner placeholder while modal content is loading", () => {
    const { container } = renderSilenceModal();
    const toggle = container.querySelector(".nav-link");
    fireEvent.click(toggle!);
    expect(container.querySelectorAll("svg")).not.toHaveLength(0);
    expect(
      document.body.querySelector(".modal-content svg.fa-spinner"),
    ).toBeInTheDocument();
  });

  it("renders a spinner placeholder after modal content load but no upstreams", () => {
    const { container } = renderSilenceModal();
    const toggle = container.querySelector(".nav-link");
    fireEvent.click(toggle!);
    expect(container.querySelectorAll("svg")).not.toHaveLength(0);
    expect(
      document.body.querySelector(".modal-content svg.fa-spinner"),
    ).toBeInTheDocument();
  });

  it("renders modal content if fallback is not used", () => {
    alertStore.data.setUpstreams({
      counters: { total: 1, healthy: 0, failed: 0 },
      instances: [
        {
          name: "dev",
          cluster: "dev",
          clusterMembers: ["dev"],
          uri: "http://localhost:9093",
          publicURI: "http://localhost:9093",
          error: "",
          version: "0.24.0",
          headers: {},
          corsCredentials: "same-origin",
          readonly: false,
        },
      ],
      clusters: { dev: ["dev"] },
    });
    const { container } = renderSilenceModal();
    const toggle = container.querySelector(".nav-link");
    fireEvent.click(toggle!);
    expect(container.querySelectorAll("svg")).not.toHaveLength(0);
    expect(container.querySelector(".modal-content svg.fa-spinner")).toBeNull();
  });

  it("hides the modal when toggle() is called twice", () => {
    const { container } = renderSilenceModal();
    const toggle = container.querySelector(".nav-link");

    fireEvent.click(toggle!);
    act(() => {
      jest.runOnlyPendingTimers();
    });
    expect(document.body.querySelector(".modal-content")).toBeInTheDocument();

    fireEvent.click(toggle!);
    act(() => {
      jest.runOnlyPendingTimers();
    });
    expect(document.body.querySelector(".modal-content")).toBeNull();
  });

  it("hides the modal when hide() is called", () => {
    // Verifies modal closes when hide() is called on the toggle store
    const { container } = renderSilenceModal();
    const toggle = container.querySelector(".nav-link");

    fireEvent.click(toggle!);
    act(() => {
      jest.runOnlyPendingTimers();
    });
    expect(document.body.querySelector(".modal-content")).toBeInTheDocument();

    act(() => {
      silenceFormStore.toggle.hide();
    });
    act(() => {
      jest.runOnlyPendingTimers();
    });
    expect(document.body.querySelector(".modal-content")).toBeNull();
  });

  it("resets progress on hide", () => {
    const { container } = renderSilenceModal();
    const toggle = container.querySelector(".nav-link");
    fireEvent.click(toggle!);

    // mark form as dirty, resetProgress() should change this value to false
    silenceFormStore.data.setWasValidated(true);
    // disable autofill, closing modal should re-enable it
    silenceFormStore.data.setAutofillMatchers(false);

    // click to hide
    fireEvent.click(toggle!);
    // wait for animation to finish
    act(() => {
      jest.runOnlyPendingTimers();
    });
    // form should be reset
    expect(silenceFormStore.data.currentStage).toBe("form");
    expect(silenceFormStore.data.wasValidated).toBe(false);
    expect(silenceFormStore.data.autofillMatchers).toBe(true);
  });

  it("'modal-open' class is appended to body node when modal is visible", () => {
    const { container } = renderSilenceModal();
    const toggle = container.querySelector(".nav-link");
    fireEvent.click(toggle!);
    expect(document.body.className.split(" ")).toContain("modal-open");
  });

  it("'modal-open' class is removed from body node after modal is hidden", () => {
    const { container } = renderSilenceModal();
    const toggle = container.querySelector(".nav-link");

    fireEvent.click(toggle!);
    expect(document.body.className.split(" ")).toContain("modal-open");

    fireEvent.click(toggle!);
    act(() => {
      jest.runOnlyPendingTimers();
    });
    expect(document.body.className.split(" ")).not.toContain("modal-open");
  });

  it("'modal-open' class is removed from body node after modal is unmounted", () => {
    const { container, unmount } = renderSilenceModal();
    const toggle = container.querySelector(".nav-link");
    fireEvent.click(toggle!);
    unmount();
    expect(document.body.className.split(" ")).not.toContain("modal-open");
  });
});
