import { act } from "react";

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

import { MockSilence } from "__fixtures__/Alerts";
import { MockThemeContext } from "__fixtures__/Theme";
import { inReactiveContext } from "__fixtures__/MobX";
import type { APISilenceT } from "Models/APITypes";
import { AlertStore } from "Stores/AlertStore";
import { SilenceFormStore } from "Stores/SilenceFormStore";
import { ThemeContext } from "Components/Theme";
import { ManagedSilence, GetAlertmanager } from ".";

let alertStore: AlertStore;
let silenceFormStore: SilenceFormStore;
let cluster: string;
let silence: APISilenceT;

beforeEach(() => {
  jest.useFakeTimers();
  jest.setSystemTime(Date.UTC(2000, 0, 1, 0, 30, 0));

  alertStore = new AlertStore([]);
  silenceFormStore = new SilenceFormStore();
  cluster = "am";
  silence = MockSilence();

  alertStore.data.setUpstreams({
    counters: { total: 1, healthy: 1, failed: 0 },
    instances: [
      {
        name: "am1",
        cluster: "am",
        clusterMembers: ["am1"],
        uri: "http://localhost:9093",
        publicURI: "http://example.com",
        readonly: false,
        error: "",
        version: "0.24.0",
        headers: {},
        corsCredentials: "include",
      },
    ],
    clusters: { am: ["am1"] },
  });

  jest.restoreAllMocks();
});

afterEach(() => {
  jest.restoreAllMocks();
  jest.useRealTimers();
});

const renderManagedSilence = (onDidUpdate?: () => void) => {
  return render(
    <ThemeContext value={MockThemeContext}>
      <ManagedSilence
        cluster={cluster}
        alertCount={123}
        alertCountAlwaysVisible={true}
        silence={silence}
        alertStore={alertStore}
        silenceFormStore={silenceFormStore}
        onDidUpdate={onDidUpdate}
      />
    </ThemeContext>,
  );
};

describe("<ManagedSilence />", () => {
  it("matches snapshot when collapsed", () => {
    const { asFragment } = renderManagedSilence();
    expect(asFragment()).toMatchSnapshot();
  });

  it("clicking on expand toggle shows silence details", () => {
    const { container } = renderManagedSilence();
    const toggle = container.querySelector("svg.text-muted.cursor-pointer");
    fireEvent.click(toggle!);
    expect(screen.getByText("Edit")).toBeInTheDocument();
  });

  it("matches snapshot with expanded details", () => {
    const { container, asFragment } = renderManagedSilence();
    const toggle = container.querySelector("svg.text-muted.cursor-pointer");
    fireEvent.click(toggle!);
    expect(asFragment()).toMatchSnapshot();
  });

  it("GetAlertmanager() returns alertmanager object from alertStore.data.upstreams.instances", () => {
    inReactiveContext(() => {
      const am = GetAlertmanager(alertStore, cluster);
      expect(am).toEqual({
        name: "am1",
        cluster: "am",
        clusterMembers: ["am1"],
        uri: "http://localhost:9093",
        publicURI: "http://example.com",
        readonly: false,
        error: "",
        version: "0.24.0",
        headers: {},
        corsCredentials: "include",
      });
    });
  });

  it("GetAlertmanager() returns only writable instances", () => {
    alertStore.data.setUpstreams({
      counters: { total: 2, healthy: 2, failed: 0 },
      instances: [
        {
          name: "am1",
          cluster: "am",
          clusterMembers: ["am1", "am2"],
          uri: "http://localhost:9093",
          publicURI: "http://example.com",
          readonly: false,
          error: "",
          version: "0.24.0",
          headers: {},
          corsCredentials: "include",
        },
        {
          name: "am2",
          cluster: "am",
          clusterMembers: ["am1", "am2"],
          uri: "http://localhost:9094",
          publicURI: "http://example.com",
          readonly: true,
          error: "",
          version: "0.24.0",
          headers: {},
          corsCredentials: "include",
        },
      ],
      clusters: { am: ["am1", "am2"] },
    });

    inReactiveContext(() => {
      const am = GetAlertmanager(alertStore, cluster);
      expect(am).toEqual({
        name: "am1",
        cluster: "am",
        clusterMembers: ["am1"],
        uri: "http://localhost:9093",
        publicURI: "http://example.com",
        readonly: false,
        error: "",
        version: "0.24.0",
        headers: {},
        corsCredentials: "include",
      });
    });
  });

  it("shows Edit button on unexpired silence", () => {
    const { container } = renderManagedSilence();
    const toggle = container.querySelector("svg.text-muted.cursor-pointer");
    fireEvent.click(toggle!);

    expect(screen.getByText("Edit")).toBeInTheDocument();
  });

  it("shows Delete button on unexpired silence", () => {
    const { container } = renderManagedSilence();
    const toggle = container.querySelector("svg.text-muted.cursor-pointer");
    fireEvent.click(toggle!);

    expect(screen.getByText("Delete")).toBeInTheDocument();
  });

  it("shows Recreate button on expired silence", () => {
    jest.setSystemTime(Date.UTC(2000, 0, 1, 23, 30, 0));
    const { container } = renderManagedSilence();
    const toggle = container.querySelector("svg.text-muted.cursor-pointer");
    fireEvent.click(toggle!);

    expect(screen.getByText("Recreate")).toBeInTheDocument();
  });

  it("clicking on Edit calls", () => {
    const { container } = renderManagedSilence();
    const toggle = container.querySelector("svg.text-muted.cursor-pointer");
    fireEvent.click(toggle!);

    expect(silenceFormStore.data.silenceID).toBeNull();

    const button = screen.getByText("Edit");
    fireEvent.click(button);
    expect(silenceFormStore.data.silenceID).toBe(silence.id);
  });

  it("call onDidUpdate if passed", () => {
    const fakeUpdate = jest.fn();
    const { container } = renderManagedSilence(fakeUpdate);
    const toggle = container.querySelector("svg.text-muted.cursor-pointer");
    fireEvent.click(toggle!);
    expect(fakeUpdate).toHaveBeenCalled();
  });
});

describe("<ManagedSilence progress bar />", () => {
  it("renders with class 'danger' and no progressbar when expired", () => {
    jest.setSystemTime(Date.UTC(2001, 0, 1, 23, 0, 0));
    const { container } = renderManagedSilence();
    expect(container.innerHTML).toMatch(/bg-danger/);
    expect(screen.getByText(/Expired 1 year ago/)).toBeInTheDocument();
  });

  it("renders with class 'warning' if <= 5m is left", () => {
    jest.setSystemTime(Date.UTC(2000, 0, 1, 0, 56, 0));
    const { container } = renderManagedSilence();
    expect(container.innerHTML).toMatch(/bg-warning/);
    expect(screen.getByText(/Expires in 4 minutes/)).toBeInTheDocument();
  });

  it("progressbar uses class 'danger' when > 90%", () => {
    jest.setSystemTime(Date.UTC(2000, 0, 1, 0, 55, 0));
    const { container } = renderManagedSilence();
    expect(container.innerHTML).toMatch(/progress-bar bg-danger/);
  });

  it("progressbar uses class 'danger' when > 75%", () => {
    jest.setSystemTime(Date.UTC(2000, 0, 1, 0, 50, 0));
    const { container } = renderManagedSilence();
    expect(container.innerHTML).toMatch(/progress-bar bg-warning/);
  });

  it("progressbar uses class 'success' when <= 75%", () => {
    jest.setSystemTime(Date.UTC(2000, 0, 1, 0, 30, 0));
    const { container } = renderManagedSilence();
    expect(container.innerHTML).toMatch(/progress-bar bg-success/);
  });

  it("progressbar is updated every 30 seconds", () => {
    jest.setSystemTime(Date.UTC(2000, 0, 1, 0, 30, 0));
    const { container } = renderManagedSilence();
    expect(container.innerHTML).toMatch(/progress-bar bg-success/);

    jest.setSystemTime(Date.UTC(2000, 0, 1, 0, 50, 0));
    act(() => {
      jest.runOnlyPendingTimers();
    });
    expect(container.innerHTML).toMatch(/progress-bar bg-warning/);

    jest.setSystemTime(Date.UTC(2000, 0, 1, 0, 55, 0));
    act(() => {
      jest.runOnlyPendingTimers();
    });
    expect(container.innerHTML).toMatch(/progress-bar bg-danger/);
  });

  it("unmounts cleanly", () => {
    const { unmount } = renderManagedSilence();
    unmount();
  });
});
