import { Router } from "react-router-dom";

import { parseTheme } from "@ax/helpers";
import SitesList, { type ISitesListProps } from "@ax/modules/Sites/SitesList";
import { history } from "@ax/routes";
import globalTheme from "@ax/themes/theme.json";

import axios from "axios";
import { mock } from "jest-mock-extended";
import configureStore from "redux-mock-store";
import thunk from "redux-thunk";
import { ThemeProvider } from "styled-components";

import { act, cleanup, fireEvent, render, screen, waitFor } from "./../../../../../config/jest/test-utils";
import { sitesResponse } from "./../../../../__mocks__/axios/SitesList";
import { sitesOptions } from "./../../../../__mocks__/axios/SitesListOptions";
import { sitesDataMock, sitesDataNoThumbnailMock, userDataMock } from "./../../../../__mocks__/store/SitesList";

afterEach(cleanup);

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

const mockStore = configureStore([thunk]);

const initialStore = {
	app: {
		isLoading: false,
		globalLangs: [
			{ id: 4, locale: "en_GB", language: "English", label: "EN", isDefault: true },
			{ id: 2, locale: "es_ES", language: "Spanish", label: "ES", isDefault: false },
		],
		token: "eyJhbGciOiJIUzI",
		hasAnimation: true,
	},
	users: userDataMock,
	sites: sitesDataMock,
	structuredData: {
		structuredData: { global: [], sites: [] },
		categories: { global: [], sites: [] },
	},
};

const renderSitesList = async (storeData: any = initialStore) => {
	const store = mockStore(storeData);
	await act(async () => {
		render(
			<Router history={history}>
				<ThemeProvider theme={parseTheme(globalTheme)}>
					<SitesList {...{ ...storeData, ...defaultProps }} />
				</ThemeProvider>
			</Router>,
			{ store },
		);
	});
};

beforeEach(() => {
	mockedAxios.mockResolvedValue(sitesResponse);
});

describe("Sites module rendering", () => {
	it("should render recent sites list when there are recent sites", async () => {
		await renderSitesList();

		const recentSitesList = screen.getByTestId("recent-sites-list");
		const recentSitesItems = screen.getAllByTestId("recent-sites-item");
		const sectionHeader = screen.getByTestId("recent-sites-header");
		const title = screen.getByTestId("recent-sites-title");
		const recentSitesItemsWrapper = screen.getByTestId("recent-sites-items-wrapper");
		const collapseButton = screen.getByTestId("recent-sites-collapse-button");
		const hideLabel = screen.getByTestId("recent-sites-hide-label");

		expect(recentSitesList).toBeTruthy();
		expect(recentSitesItems.length).toEqual(2);
		expect(sectionHeader).toBeTruthy();
		expect(title).toBeTruthy();
		expect(recentSitesItemsWrapper).toBeTruthy();
		expect(collapseButton).toBeTruthy();
		expect(hideLabel).toBeTruthy();
	});

	it("should render default sites list view", async () => {
		await renderSitesList();

		expect(screen.getByTestId("all-sites-header")).toBeTruthy();
		expect(screen.getByTestId("all-sites-title")).toBeTruthy();
		expect(screen.getByTestId("all-sites-header-icons")).toBeTruthy();
		expect(screen.getByTestId("all-sites-grid-filter")).toBeTruthy();
		expect(screen.getByTestId("all-sites-grid-filter-label")).toBeTruthy();
		expect(screen.getByTestId("sites-grid-list")).toBeTruthy();
	});

	it("should not render recent sites list when recent sites list is empty", async () => {
		await renderSitesList({
			...initialStore,
			sites: { totalItems: 2, sites: sitesDataMock.sites, recentSites: [], config: sitesDataMock.config },
		});

		expect(screen.queryByTestId("recent-sites-list")).not.toBeTruthy();
	});

	it("should render empty state when no sites", async () => {
		await renderSitesList({
			...initialStore,
			sites: { totalItems: 0, sites: [], recentSites: [], config: sitesDataMock.config },
		});

		expect(screen.getByTestId("empty-state")).toBeTruthy();
	});

	it("should render full name when length is not too long", async () => {
		await renderSitesList();

		const itemNames = screen.getAllByTestId("sites-item-name");
		expect(itemNames.length).toEqual(4);
	});

	it("should render tooltip when full name is too long", async () => {
		await renderSitesList({
			...initialStore,
			sites: {
				totalItems: 2,
				sites: [
					{
						id: 1,
						name: "Site 1 long name Site 1 long name Site 1 long name Site 1 long name Site 1 long name Site 1 long name",
						theme: "default-theme",
						author: 2,
						pages: [],
						updated: true,
						home: "//cx.dev.griddo.io/pre-griddo/componentfolder",
						isPublished: false,
						bigAvatar: "http://images.dev.griddo.io/griddo-site-thumbnail-ec2ab0ba-9424-4424-8f60-222ec746902d_10",
						favicon: "https://images.dev.griddo.io/by-themascot-1920_2",
						published: "2021-11-03T10:42:32.000Z",
						rendering: false,
						renderingHours: 0,
						shouldBeUpdated: false,
						slug: "/griddo-components",
						smallAvatar: "http://images.dev.griddo.io/griddo-site-thumbnail-ec2ab0ba-9424-4424-8f60-222ec746902d_9",
						socials: null,
						thumbnail: "http://images.dev.griddo.io/griddo-site-thumbnail-ec2ab0ba-9424-4424-8f60-222ec746902d_7",
						timezone: "Pacific/Pago_Pago",
						deleted: 0,
						hash: null,
						languageSites: [146, 178],
						lastAccess: "2022-09-05T07:34:19.000Z",
						modified: "2022-06-22T07:17:34.000Z",
						navigationModules: null,
					},
					sitesDataMock.sites[1],
				],
				config: sitesDataMock.config,
				recentSites: sitesDataMock.recentSites,
			},
		});

		const tooltip = screen.getAllByTestId("tooltip-component");
		const trimmedName = screen.getAllByTestId("trimmed-item-name");
		expect(tooltip.length).toBeTruthy();
		expect(trimmedName.length).toEqual(1);
	});

	it("should render custom thumbnail if site has one", async () => {
		await renderSitesList();

		const thumbnail = screen.getAllByTestId("sites-item-thumbnail-wrapper");
		expect(thumbnail.length).toEqual(4);
	});

	it("should render placeholder if site does not have thumbnail", async () => {
		await renderSitesList({
			...initialStore,
			sites: sitesDataNoThumbnailMock,
		});

		const sitesItemThumbnail = screen.getAllByTestId("sites-item-thumbnail");
		expect(sitesItemThumbnail.length).toEqual(4);
	});
});

describe("Sites module events", () => {
	it("should hide recent sites on click collapse button", async () => {
		await renderSitesList();

		const collapseButton = screen.getByTestId("recent-sites-collapse-button");
		const hideButtonLabel = screen.getByTestId("recent-sites-hide-label");

		expect(collapseButton).toBeTruthy();
		expect(hideButtonLabel).toBeTruthy();

		fireEvent.click(collapseButton);
		const showButtonLabel = screen.getByTestId("recent-sites-show-label");
		expect(showButtonLabel).toBeTruthy();
	});

	it("should go to site on click recent site item", async () => {
		window.HTMLElement.prototype.scrollTo = jest.fn();

		await renderSitesList();

		const recentSiteItems = screen.getAllByTestId("recent-sites-item");
		expect(recentSiteItems.length).toEqual(2);

		fireEvent.click(recentSiteItems[0]);
		await waitFor(() => expect(history.location.pathname).toEqual("/sites/pages"));
	});

	it("should display list view on click list option button", async () => {
		await renderSitesList();

		const gridIconAction = screen.getByTestId("icon-action-Grid2");
		const listIconAction = screen.getByTestId("icon-action-BulletList");
		expect(gridIconAction).toBeTruthy();
		expect(listIconAction).toBeTruthy();
		await act(async () => {
			fireEvent.click(listIconAction);
		});
		const listSitesItems = screen.getAllByTestId("list-site-item");
		expect(listSitesItems.length).toEqual(2);
	});

	it("should go to site on click list site item", async () => {
		window.HTMLElement.prototype.scrollTo = jest.fn();

		await renderSitesList();

		const gridIconAction = screen.getByTestId("icon-action-Grid2");
		const listIconAction = screen.getByTestId("icon-action-BulletList");
		expect(gridIconAction).toBeTruthy();
		expect(listIconAction).toBeTruthy();
		await act(async () => {
			fireEvent.click(listIconAction);
		});
		const listSitesItems = screen.getAllByTestId("list-site-item");
		expect(listSitesItems.length).toEqual(2);

		fireEvent.click(listSitesItems[0]);
		await waitFor(() => expect(history.location.pathname).toEqual("/sites/pages"));
	});

	it("should display grid view on click grid option button", async () => {
		await renderSitesList();

		const gridIconAction = screen.getByTestId("icon-action-Grid2");
		const listIconAction = screen.getByTestId("icon-action-BulletList");
		expect(gridIconAction).toBeTruthy();
		expect(listIconAction).toBeTruthy();

		fireEvent.click(gridIconAction);
		const allSitesGridFilter = screen.getByTestId("all-sites-grid-filter");
		const gridSitesItems = screen.queryAllByTestId("grid-site-item");
		expect(allSitesGridFilter).toBeTruthy();
		expect(gridSitesItems.length).toEqual(2);
	});

	it("should go to site on click grid site item", async () => {
		await renderSitesList();

		const gridSiteItems = screen.getAllByTestId("grid-site-item");
		expect(gridSiteItems.length).toEqual(2);

		await act(async () => {
			fireEvent.click(gridSiteItems[0]);
		});

		expect(history.location.pathname).toEqual("/sites/pages");
	});

	it("should render site modal when clicking new button", async () => {
		mockedAxios.mockResolvedValue(sitesOptions);

		await renderSitesList();

		const newButton = screen.getByTestId("button-default");
		expect(newButton.textContent).toEqual("New");
		await act(async () => {
			fireEvent.click(newButton);
		});
		const siteModalForm = screen.getByTestId("site-modal-form");
		expect(siteModalForm).toBeTruthy();
	});

	it("should display filters when clicking sort items button on grid view", async () => {
		await renderSitesList();

		const floatingMenuButton = screen.getByTestId("grid-floating-menu-button");
		expect(floatingMenuButton).toBeTruthy();
		fireEvent.click(floatingMenuButton);
		const listItem = screen.getAllByTestId("list-item");
		expect(listItem.length).toEqual(6);
		const sortByNameAsc = listItem[0];
		const sortByNameDesc = listItem[1];
		const sortByLastAccessDesc = listItem[2];
		const sortByLastAccessAsc = listItem[3];
		expect(sortByNameAsc.textContent).toEqual("Ascendent");
		expect(sortByNameDesc.textContent).toEqual("Descendent");
		expect(sortByLastAccessDesc.textContent).toEqual("Newest");
		expect(sortByLastAccessAsc.textContent).toEqual("Oldest");
	});

	it("should render filter on grid view", async () => {
		await renderSitesList();

		const floatingMenuButton = screen.getByTestId("grid-floating-menu-button");
		expect(floatingMenuButton).toBeTruthy();
		fireEvent.click(floatingMenuButton);
		const listItem = screen.getAllByTestId("list-item");
		expect(listItem.length).toEqual(6);

		const sortAscendingName = listItem[0];
		const sortDescendingName = listItem[1];
		const sortDescendingLastAccess = listItem[2];
		const sortAscendingLastAccess = listItem[3];

		expect(sortAscendingName.textContent).toEqual("Ascendent");
		expect(sortDescendingName.textContent).toEqual("Descendent");
		expect(sortDescendingLastAccess.textContent).toEqual("Newest");
		expect(sortAscendingLastAccess.textContent).toEqual("Oldest");
	});

	it("should change filter's label to name when filtered by name", async () => {
		await renderSitesList();

		const floatingMenuButton = screen.getByTestId("grid-floating-menu-button");
		expect(floatingMenuButton).toBeTruthy();
		fireEvent.click(floatingMenuButton);
		const listItem = screen.getAllByTestId("list-item");
		expect(listItem.length).toEqual(6);

		const sortAscendingName = listItem[0];
		const sortDescendingName = listItem[1];
		const sortDescendingLastAccess = listItem[2];
		const sortAscendingLastAccess = listItem[3];

		expect(sortAscendingName.textContent).toEqual("Ascendent");
		expect(sortDescendingName.textContent).toEqual("Descendent");
		expect(sortDescendingLastAccess.textContent).toEqual("Newest");
		expect(sortAscendingLastAccess.textContent).toEqual("Oldest");
		const label0 = screen.getByTestId("filter-label");
		expect(label0.textContent).toEqual("Last access");

		fireEvent.click(sortAscendingName);
		const label = screen.getByTestId("filter-label");
		expect(label.textContent).toEqual("Name");
	});

	it("should change filter's label to Last access when filtered by lastAccess", async () => {
		await renderSitesList();

		const floatingMenuButton = screen.getByTestId("grid-floating-menu-button");
		expect(floatingMenuButton).toBeTruthy();
		fireEvent.click(floatingMenuButton);
		const listItem = screen.getAllByTestId("list-item");
		expect(listItem.length).toEqual(6);
		const sortDescendingLastAccess = listItem[2];
		fireEvent.click(sortDescendingLastAccess);
		const labelAccess = screen.getByTestId("filter-label");
		expect(labelAccess.textContent).toEqual("Last access");
	});

	it("should change filter's label to Creation Date when filtered by dateCreated", async () => {
		await renderSitesList();

		const floatingMenuButton = screen.getByTestId("grid-floating-menu-button");
		expect(floatingMenuButton).toBeTruthy();
		fireEvent.click(floatingMenuButton);
		const listItem = screen.getAllByTestId("list-item");
		expect(listItem.length).toEqual(6);
		const sortDescendinDateCreated = listItem[4];
		fireEvent.click(sortDescendinDateCreated);
		const labelAccess = screen.getByTestId("filter-label");
		expect(labelAccess.textContent).toEqual("Creation date");
	});

	it("should open delete site modal from grid item", async () => {
		await renderSitesList();

		const gridItems = screen.getAllByTestId("grid-site-item");
		const moreInfoButton = screen.getAllByTestId("more-info-button");

		expect(gridItems[0]).toBeTruthy();
		fireEvent.click(moreInfoButton[0]);

		const actionMenuItem = screen.getAllByTestId("action-menu-item");
		expect(actionMenuItem.length).toEqual(2);
		fireEvent.click(actionMenuItem[0]);
		const deleteModal = screen.getByTestId("delete-modal");
		expect(deleteModal).toBeTruthy();
	});

	it("should open delete site modal from list item", async () => {
		await renderSitesList();

		const listIconAction = screen.getByTestId("icon-action-BulletList");
		expect(listIconAction).toBeTruthy();
		await act(async () => {
			fireEvent.click(listIconAction);
		});
		const listItems = screen.getAllByTestId("list-site-item");
		const moreInfoButton = screen.getAllByTestId("more-info-button");

		expect(listItems[0]).toBeTruthy();
		fireEvent.click(moreInfoButton[0]);

		const actionMenuItem = screen.getAllByTestId("action-menu-item");
		expect(actionMenuItem.length).toEqual(2);
		fireEvent.click(actionMenuItem[0]);
		const deleteModal = screen.getByTestId("delete-modal");
		expect(deleteModal).toBeTruthy();
	});

	it("should open publish site modal from grid item", async () => {
		await renderSitesList();

		const gridIconAction = screen.getByTestId("icon-action-Grid2");
		fireEvent.click(gridIconAction);

		const gridItems = screen.getAllByTestId("grid-site-item");
		const moreInfoButton = screen.getAllByTestId("more-info-button");

		expect(gridItems[0]).toBeTruthy();
		fireEvent.click(moreInfoButton[0]);

		const actionMenuItem = screen.getAllByTestId("action-menu-item");
		expect(actionMenuItem.length).toEqual(2);
		fireEvent.click(actionMenuItem[1]);
		const publishModal = screen.getByTestId("publish-modal");
		expect(publishModal).toBeTruthy();
	});

	it("should open publish site modal from list item", async () => {
		await renderSitesList();

		const listIconAction = screen.getByTestId("icon-action-BulletList");
		expect(listIconAction).toBeTruthy();
		await act(async () => {
			fireEvent.click(listIconAction);
		});
		const listItems = screen.getAllByTestId("list-site-item");
		const moreInfoButton = screen.getAllByTestId("more-info-button");

		expect(listItems[0]).toBeTruthy();
		fireEvent.click(moreInfoButton[0]);

		const actionMenuItem = screen.getAllByTestId("action-menu-item");
		expect(actionMenuItem.length).toEqual(2);
		fireEvent.click(actionMenuItem[1]);
		const publishModal = screen.getByTestId("publish-modal");
		expect(publishModal).toBeTruthy();
	});
});
