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

import { useFetchGetMock } from "__fixtures__/useFetchGet";
import {
  SilenceFormStore,
  NewEmptyMatcher,
  MatcherWithIDT,
} from "Stores/SilenceFormStore";
import { useFetchGet } from "Hooks/useFetchGet";
import { StringToOption } from "Common/Select";
import { MatchCounter } from "./MatchCounter";

let matcher: MatcherWithIDT;
let silenceFormStore: SilenceFormStore;

beforeEach(() => {
  silenceFormStore = new SilenceFormStore();
  matcher = NewEmptyMatcher();
  matcher.name = "foo";
  matcher.values = [StringToOption("bar")];
});

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

const renderMatchCounter = () => {
  return render(
    <MatchCounter silenceFormStore={silenceFormStore} matcher={matcher} />,
  );
};

describe("<MatchCounter />", () => {
  it("matches snapshot", () => {
    useFetchGetMock.fetch.setMockedData({
      response: {
        alerts: Array(25).map((i) => [
          { name: "alertname", value: `alert${i}` },
        ]),
      },
      error: null,
      isLoading: false,
      isRetrying: false,
      retryCount: 0,
      get: jest.fn(),
      cancelGet: jest.fn(),
    });
    const { asFragment } = renderMatchCounter();
    expect(asFragment()).toMatchSnapshot();
  });

  it("renders spinner icon while fetching", () => {
    useFetchGetMock.fetch.setMockedData({
      response: null,
      error: null,
      isLoading: true,
      isRetrying: false,
      retryCount: 0,
      get: jest.fn(),
      cancelGet: jest.fn(),
    });

    const { container } = renderMatchCounter();
    expect(container.querySelectorAll("svg.fa-spinner")).toHaveLength(1);
    expect(
      container.querySelectorAll("svg.fa-spinner.text-danger"),
    ).toHaveLength(0);
  });

  it("renders spinner icon with text-danger while retrying fetching", () => {
    useFetchGetMock.fetch.setMockedData({
      response: null,
      error: null,
      isLoading: true,
      isRetrying: true,
      retryCount: 0,
      get: jest.fn(),
      cancelGet: jest.fn(),
    });

    const { container } = renderMatchCounter();
    expect(
      container.querySelectorAll("svg.fa-spinner.text-danger"),
    ).toHaveLength(1);
  });

  it("renders error icon on failed fetch", () => {
    useFetchGetMock.fetch.setMockedData({
      response: null,
      error: "failed",
      isLoading: false,
      isRetrying: false,
      retryCount: 0,
      get: jest.fn(),
      cancelGet: jest.fn(),
    });

    const { container } = renderMatchCounter();
    expect(
      container.querySelectorAll("svg.fa-circle-exclamation.text-danger"),
    ).toHaveLength(1);
  });

  it("totalAlerts is 0 after mount", () => {
    useFetchGetMock.fetch.setMockedData({
      response: { alerts: [] },
      error: null,
      isLoading: false,
      isRetrying: false,
      retryCount: 0,
      get: jest.fn(),
      cancelGet: jest.fn(),
    });

    renderMatchCounter();
    expect(screen.getByText("0")).toBeInTheDocument();
  });

  it("updates totalAlerts after successful fetch", () => {
    useFetchGetMock.fetch.setMockedData({
      response: {
        alerts: Array(25).map((i) => [
          { name: "alertname", value: `alert${i}` },
        ]),
      },
      error: null,
      isLoading: false,
      isRetrying: false,
      retryCount: 0,
      get: jest.fn(),
      cancelGet: jest.fn(),
    });
    renderMatchCounter();
    expect(screen.getByText("25")).toBeInTheDocument();
  });

  it("sends correct query string for a 'foo=bar' matcher", () => {
    renderMatchCounter();
    expect(
      (useFetchGet as jest.MockedFunction<typeof useFetchGet>).mock.calls[0][0],
    ).toBe("./alertList.json?q=foo%3Dbar");
  });

  it("sends correct query string for a 'foo=~bar' matcher", () => {
    matcher.isRegex = true;
    renderMatchCounter();
    expect(
      (useFetchGet as jest.MockedFunction<typeof useFetchGet>).mock.calls[0][0],
    ).toBe("./alertList.json?q=foo%3D%7E%5Ebar%24");
  });

  it("sends correct query string for a 'foo=(x)' matcher with wasCreated=true & isRegex=false", () => {
    const v = StringToOption("(x)");
    v.wasCreated = true;
    matcher.values = [v];
    matcher.isRegex = false;
    renderMatchCounter();
    expect(
      (useFetchGet as jest.MockedFunction<typeof useFetchGet>).mock.calls[0][0],
    ).toBe("./alertList.json?q=foo%3D%28x%29");
  });

  it("sends correct query string for a 'foo=(x)' matcher with wasCreated=true & isRegex=true", () => {
    const v = StringToOption("(x)");
    v.wasCreated = true;
    matcher.values = [v];
    matcher.isRegex = true;
    renderMatchCounter();
    expect(
      (useFetchGet as jest.MockedFunction<typeof useFetchGet>).mock.calls[0][0],
    ).toBe("./alertList.json?q=foo%3D%7E%5E%28x%29%24");
  });

  it("sends correct query string for a 'foo=(x)' matcher with wasCreated=false & isRegex=false", () => {
    const v = StringToOption("(x)");
    v.wasCreated = false;
    matcher.values = [v];
    matcher.isRegex = false;
    renderMatchCounter();
    expect(
      (useFetchGet as jest.MockedFunction<typeof useFetchGet>).mock.calls[0][0],
    ).toBe("./alertList.json?q=foo%3D%28x%29");
  });

  it("sends correct query string for a 'foo=(x)' matcher with wasCreated=false & isRegex=true", () => {
    const v = StringToOption("(x)");
    v.wasCreated = false;
    matcher.values = [v];
    matcher.isRegex = true;
    renderMatchCounter();
    expect(
      (useFetchGet as jest.MockedFunction<typeof useFetchGet>).mock.calls[0][0],
    ).toBe("./alertList.json?q=foo%3D%7E%5E%5C%28x%5C%29%24");
  });

  it("sends correct query string for a 'foo=~(bar|baz)' matcher", () => {
    matcher.values = [StringToOption("bar"), StringToOption("baz")];
    matcher.isRegex = true;
    silenceFormStore.data.setAlertmanagers([]);
    renderMatchCounter();
    expect(
      (useFetchGet as jest.MockedFunction<typeof useFetchGet>).mock.calls[0][0],
    ).toBe("./alertList.json?q=foo%3D%7E%5E%28bar%7Cbaz%29%24");
  });

  it("selecting one Alertmanager instance appends it to the filters", () => {
    silenceFormStore.data.setAlertmanagers([
      {
        label: "am1",
        value: ["am1"],
      },
    ]);
    renderMatchCounter();
    expect(
      (useFetchGet as jest.MockedFunction<typeof useFetchGet>).mock.calls[0][0],
    ).toBe(
      "./alertList.json?q=foo%3Dbar&q=%40alertmanager%3D%7E%5E%28am1%29%24",
    );
  });

  it("selecting two Alertmanager instances appends it correctly to the filters", () => {
    silenceFormStore.data.setAlertmanagers([
      {
        label: "am1",
        value: ["am1"],
      },
      {
        label: "am2",
        value: ["am2"],
      },
    ]);
    renderMatchCounter();
    expect(
      (useFetchGet as jest.MockedFunction<typeof useFetchGet>).mock.calls[0][0],
    ).toBe(
      "./alertList.json?q=foo%3Dbar&q=%40alertmanager%3D%7E%5E%28am1%7Cam2%29%24",
    );
  });
});
