import * as React from "react";

import axios from "axios";
import { ThemeProvider } from "styled-components";
import { CalledWithMock, mock } from "jest-mock-extended";
import { render, screen, cleanup, act, fireEvent } from "@testing-library/react";

import { parseTheme } from "@ax/helpers";
import globalTheme from "@ax/themes/theme.json";
import LiveFilter, { ILiveFilterProps } from "@ax/components/TableFilters/LiveFilter";
import { IQueryValue } from "@ax/types";

afterEach(cleanup);
jest.mock("axios");
const mockedAxios = axios as jest.MockedFunction<typeof axios>;
const defaultProps = mock<ILiveFilterProps>();

describe("LiveFilter component rendering", () => {
	it("should render live filter", async () => {
		defaultProps.value = [{ value: "all", label: "All" }];
		const filterItems = jest.fn() as CalledWithMock<void, [pointer: string, filter: IQueryValue[]]>;

		defaultProps.filterItems = filterItems;
		const data = {
			data: [
				{ id: 1, title: "Offline", status: "offline" },
				{ id: 2, title: "Publication pending", status: "upload-pending" },
				{ id: 3, title: "Live", status: "active" },
				{ id: 4, title: "Offline pending", status: "offline-pending" },
				{ id: 5, title: "Live & modified", status: "modified" },
				{ id: 6, title: "Publication scheduled", status: "scheduled" },
			],
			status: 200,
			statusText: "Ok",
			headers: {},
			config: {},
		};

		mockedAxios.mockResolvedValue(data);

		await act(async () => {
			render(
				<ThemeProvider theme={parseTheme(globalTheme)}>
					<LiveFilter {...defaultProps} />
				</ThemeProvider>,
			);
		});

		const liveFilter = screen.getByTestId("floating-menu");
		expect(liveFilter).toBeTruthy();
		expect(liveFilter.textContent).toEqual("Live");
	});

	it("should render the component with no options", async () => {
		defaultProps.value = [{ value: "all", label: "All" }];
		const filterItems = jest.fn() as CalledWithMock<void, [pointer: string, filter: IQueryValue[]]>;

		defaultProps.filterItems = filterItems;

		const data = {
			data: [],
			status: 500,
			statusText: "Internal Server Error",
			headers: {},
			config: {},
		};

		mockedAxios.mockResolvedValue(data);

		await act(async () => {
			render(
				<ThemeProvider theme={parseTheme(globalTheme)}>
					<LiveFilter {...defaultProps} />
				</ThemeProvider>,
			);
		});

		const liveFilterButton = screen.getByTestId("floating-menu-button");
		expect(liveFilterButton).toBeTruthy();
		fireEvent.click(liveFilterButton);
		const floatingMenu = screen.getByTestId("floating-menu");
		expect(floatingMenu).toBeTruthy();
		const checkGroupFieldGroup = screen.getByTestId("check-group-field-group");
		expect(checkGroupFieldGroup).toBeTruthy();
		const checkFieldInputs = screen.getAllByTestId<HTMLInputElement>("check-field-input");
		expect(checkFieldInputs).toHaveLength(1);
	});
});

describe("LiveFilter events", () => {
	it("should render check group on click", async () => {
		defaultProps.value = [{ value: "all", label: "All" }];
		const filterItemsMock = jest.fn() as CalledWithMock<void, [pointer: string, filter: IQueryValue[]]>;

		defaultProps.filterItems = filterItemsMock;
		const data = {
			data: [
				{ id: 1, title: "Offline", status: "offline" },
				{ id: 2, title: "Publication pending", status: "upload-pending" },
				{ id: 3, title: "Live", status: "active" },
				{ id: 4, title: "Offline pending", status: "offline-pending" },
				{ id: 5, title: "Live & modified", status: "modified" },
				{ id: 6, title: "Publication scheduled", status: "scheduled" },
			],
			status: 200,
			statusText: "Ok",
			headers: {},
			config: {},
		};
		mockedAxios.mockResolvedValue(data);

		await act(async () => {
			render(
				<ThemeProvider theme={parseTheme(globalTheme)}>
					<LiveFilter {...defaultProps} />
				</ThemeProvider>,
			);
		});

		const liveFilterButton = screen.getByTestId("floating-menu-button");
		expect(liveFilterButton).toBeTruthy();
		fireEvent.click(liveFilterButton);
		const floatingMenu = screen.getByTestId("floating-menu");
		expect(floatingMenu).toBeTruthy();
		const checkGroupFieldGroup = screen.getByTestId("check-group-field-group");
		expect(checkGroupFieldGroup).toBeTruthy();
		const checkFieldLabels = screen.getAllByTestId("check-field-label");
		expect(checkFieldLabels).toHaveLength(7);
	});

	it("should check option on click", async () => {
		defaultProps.value = [{ value: "all", label: "All" }];
		const filterItemsMock = jest.fn() as CalledWithMock<void, [pointer: string, filter: IQueryValue[]]>;

		defaultProps.filterItems = filterItemsMock;
		const data = {
			data: [
				{ id: 1, title: "Offline", status: "offline" },
				{ id: 2, title: "Publication pending", status: "upload-pending" },
				{ id: 3, title: "Live", status: "active" },
				{ id: 4, title: "Offline pending", status: "offline-pending" },
				{ id: 5, title: "Live & modified", status: "modified" },
				{ id: 6, title: "Publication scheduled", status: "scheduled" },
			],
			status: 200,
			statusText: "Ok",
			headers: {},
			config: {},
		};

		mockedAxios.mockResolvedValue(data);

		await act(async () => {
			render(
				<ThemeProvider theme={parseTheme(globalTheme)}>
					<LiveFilter {...defaultProps} />
				</ThemeProvider>,
			);
		});

		const liveFilterButton = screen.getByTestId("floating-menu-button");
		expect(liveFilterButton).toBeTruthy();
		fireEvent.click(liveFilterButton);
		const floatingMenu = screen.getByTestId("floating-menu");
		expect(floatingMenu).toBeTruthy();
		const checkGroupFieldGroup = screen.getByTestId("check-group-field-group");
		expect(checkGroupFieldGroup).toBeTruthy();
		const checkFieldInputs = screen.getAllByTestId<HTMLInputElement>("check-field-input");
		expect(checkFieldInputs).toHaveLength(7);
		fireEvent.click(checkFieldInputs[1]);
		expect(checkFieldInputs[1].checked).toBe(true);
	});

	it("should uncheck selected option on click and select 'All' option", async () => {
		defaultProps.value = [{ value: "all", label: "All" }];
		const filterItemsMock = jest.fn() as CalledWithMock<void, [pointer: string, filter: IQueryValue[]]>;

		defaultProps.filterItems = filterItemsMock;
		const data = {
			data: [
				{ id: 1, title: "Offline", status: "offline" },
				{ id: 2, title: "Publication pending", status: "upload-pending" },
				{ id: 3, title: "Live", status: "active" },
				{ id: 4, title: "Offline pending", status: "offline-pending" },
				{ id: 5, title: "Live & modified", status: "modified" },
				{ id: 6, title: "Publication scheduled", status: "scheduled" },
			],
			status: 200,
			statusText: "Ok",
			headers: {},
			config: {},
		};

		mockedAxios.mockResolvedValue(data);

		await act(async () => {
			render(
				<ThemeProvider theme={parseTheme(globalTheme)}>
					<LiveFilter {...defaultProps} />
				</ThemeProvider>,
			);
		});

		const liveFilterButton = screen.getByTestId("floating-menu-button");
		expect(liveFilterButton).toBeTruthy();
		fireEvent.click(liveFilterButton);
		const floatingMenu = screen.getByTestId("floating-menu");
		expect(floatingMenu).toBeTruthy();
		const checkGroupFieldGroup = screen.getByTestId("check-group-field-group");
		expect(checkGroupFieldGroup).toBeTruthy();
		const checkFieldInputs = screen.getAllByTestId<HTMLInputElement>("check-field-input");
		expect(checkFieldInputs).toHaveLength(7);
		fireEvent.click(checkFieldInputs[1]);
		expect(checkFieldInputs[1].checked).toBe(true);
		fireEvent.click(checkFieldInputs[1]);
		expect(checkFieldInputs[1].checked).toBe(false);
		expect(checkFieldInputs[0].checked).toBe(true);

		expect(filterItemsMock).toHaveBeenCalledTimes(2);
	});

	it("should call filterItems action on change", async () => {
		defaultProps.value = [{ value: "all", label: "All" }];
		const filterItemsMock = jest.fn() as CalledWithMock<void, [pointer: string, filter: IQueryValue[]]>;

		defaultProps.filterItems = filterItemsMock;
		const data = {
			data: [
				{ id: 1, title: "Offline", status: "offline" },
				{ id: 2, title: "Publication pending", status: "upload-pending" },
				{ id: 3, title: "Live", status: "active" },
				{ id: 4, title: "Offline pending", status: "offline-pending" },
				{ id: 5, title: "Live & modified", status: "modified" },
				{ id: 6, title: "Publication scheduled", status: "scheduled" },
			],
			status: 200,
			statusText: "Ok",
			headers: {},
			config: {},
		};

		mockedAxios.mockResolvedValue(data);

		await act(async () => {
			render(
				<ThemeProvider theme={parseTheme(globalTheme)}>
					<LiveFilter {...defaultProps} />
				</ThemeProvider>,
			);
		});

		const liveFilterButton = screen.getByTestId("floating-menu-button");

		expect(liveFilterButton).toBeTruthy();
		fireEvent.click(liveFilterButton);
		const checkFieldInputs = screen.queryAllByTestId("check-field-input");
		fireEvent.click(checkFieldInputs[1]);
		expect(filterItemsMock).toHaveBeenCalledTimes(1);
		expect(filterItemsMock).toBeCalledWith("liveStatus", [{ label: "Offline", value: "offline" }]);
	});

	it("should render live filter with filter options", async () => {
		defaultProps.value = [{ value: "all", label: "All" }];
		const filterItems = jest.fn() as CalledWithMock<void, [pointer: string, filter: IQueryValue[]]>;
		defaultProps.filterOptions = ["offline", "active", "scheduled"];
		defaultProps.filterItems = filterItems;
		const data = {
			data: [
				{ id: 1, title: "Offline", status: "offline" },
				{ id: 2, title: "Publication pending", status: "upload-pending" },
				{ id: 3, title: "Live", status: "active" },
				{ id: 4, title: "Offline pending", status: "offline-pending" },
				{ id: 5, title: "Live & modified", status: "modified" },
				{ id: 6, title: "Publication scheduled", status: "scheduled" },
			],
			status: 200,
			statusText: "Ok",
			headers: {},
			config: {},
		};

		mockedAxios.mockResolvedValue(data);

		await act(async () => {
			render(
				<ThemeProvider theme={parseTheme(globalTheme)}>
					<LiveFilter {...defaultProps} />
				</ThemeProvider>,
			);
		});

		const liveFilterButton = screen.getByTestId("floating-menu-button");
		expect(liveFilterButton).toBeTruthy();
		fireEvent.click(liveFilterButton);
		const floatingMenu = screen.getByTestId("floating-menu");
		expect(floatingMenu).toBeTruthy();
		const checkGroupFieldGroup = screen.getByTestId("check-group-field-group");
		expect(checkGroupFieldGroup).toBeTruthy();
		const checkFieldLabels = screen.getAllByTestId("check-field-label");
		expect(checkFieldLabels).toHaveLength(4);
	});
});
