import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
import { connect } from "react-redux";

import {
	EmptyState,
	ErrorToast,
	FilterTagsBar,
	Loading,
	MainWrapper,
	Pagination,
	SearchTagsBar,
	TableList,
} from "@ax/components";
import { appActions } from "@ax/containers/App";
import type { IError } from "@ax/containers/App/reducer";
import { sitesActions } from "@ax/containers/Sites";
import { useBulkSelection, useModal, usePermission } from "@ax/hooks";
import type { ICheck, IGetSitesParams, IQueryValue, IRootState, ISite, ISiteListConfig, IUser } from "@ax/types";

import AllSitesHeader from "./AllSitesHeader";
import GridSiteItem from "./GridView/GridSiteItem";
import { useFilterQuery, useIsMount, useSortedListStatus } from "./hooks";
import BulkHeader from "./ListView/BulkHeader";
import ListSiteItem from "./ListView/ListSiteItem";
import RecentSitesList from "./RecentSites";
import SiteModal from "./SiteModal";
import { filterByStatus, getSortedListStatus } from "./utils";
import WelcomeModal from "./WelcomeModal";

import * as S from "./style";

const itemsPerPage = 30;
const firstPage = 1;

const filterLabels = {
	liveStatus: "Live",
};

const SitesList = (props: ISitesListProps) => {
	const {
		token,
		sites,
		recentSites,
		totalItems,
		saveSettings,
		setHistoryPush,
		getSites,
		publishSitesBulk,
		unpublishSitesBulk,
		error,
		config,
		setListConfig,
		hasAnimation,
		setHasAnimation,
		isLoading,
		isSitesLoading,
		currentUser,
	} = props;

	const isMount = useIsMount();

	const [page, setPage] = useState(firstPage);

	const currentConfig = useMemo(() => {
		const raw = localStorage.getItem("sitesConfig");
		return raw ? JSON.parse(raw) : config;
	}, [config]);

	const initialHasAnimation = useRef(hasAnimation);
	const currentConfigRef = useRef(currentConfig);
	useLayoutEffect(() => {
		currentConfigRef.current = currentConfig;
	}, [currentConfig]);

	const [isRecentSitesListDisplayed, setIsRecentSitesListDisplayed] = useState<boolean>(
		currentConfig.displayRecentSites,
	);

	const [displayMode, setDisplayMode] = useState<"list" | "grid">(currentConfig.mode);
	const [searchQuery, setSearchQuery] = useState<string>("");
	const [isBulkLoading, setIsBulkLoading] = useState(false);
	const tableRef = useRef<HTMLDivElement>(null);
	const errorRef = useRef<HTMLDivElement>(null);
	const { isOpen, toggleModal } = useModal();
	const { isOpen: isWelcomeOpen, toggleModal: toggleWelcomeModal } = useModal();

	const scrollToTop = useCallback(() => {
		if (tableRef.current) {
			tableRef.current.scrollTo(0, 0);
		}
	}, []);

	const handlePageChange = useCallback(
		(newPage: number) => {
			scrollToTop();
			setPage(newPage);
		},
		[scrollToTop],
	);

	const sitesIds = sites?.map((site: ISite) => site.id);

	const allowedToCreateSite = usePermission("general.createSite");

	const pagination = {
		setPage: handlePageChange,
		itemsPerPage,
		totalItems,
		currPage: page,
	};

	const rightButtonProps = allowedToCreateSite
		? {
				label: "New",
				action: toggleModal,
			}
		: undefined;

	const { sortedListStatus, setSortedListStatus } = useSortedListStatus(currentConfig.sortedListStatus);
	const { setFiltersSelection, resetFilterQuery, filterValues, filterQuery } = useFilterQuery(
		currentConfig.filterValues,
	);

	const getParams = useCallback((): IGetSitesParams => {
		const query = searchQuery.length ? `&query=${searchQuery}` : "";
		return {
			pagination: true,
			language: undefined,
			recentSitesNumber: 7,
			page,
			itemsPerPage,
			filterQuery: filterQuery,
			searchQuery: query,
		};
	}, [filterQuery, searchQuery, page]);

	const getSiteList = useCallback(async () => {
		if (token) {
			const params = getParams();
			// On first mount, token is required for initial authentication
			if (isMount) {
				await getSites({ ...params, token });
			} else {
				await getSites(params);
			}
		}
	}, [getParams, getSites, isMount, token]);

	useEffect(() => {
		getSiteList();
	}, [getSiteList]);

	useEffect(() => {
		if (!initialHasAnimation.current && !currentUser.profileCreated) {
			toggleWelcomeModal();
		}
	}, [toggleWelcomeModal, currentUser]);

	useEffect(() => {
		setListConfig({ ...currentConfigRef.current, filterValues });
	}, [filterValues, setListConfig]);

	useEffect(() => {
		const errorCode = error?.code;
		if (errorCode === 400 && errorRef.current) {
			errorRef.current.scrollIntoView();
		}
	}, [error]);

	const toggleRecentSites = () => {
		const updatedConfig = { ...currentConfig, displayRecentSites: !isRecentSitesListDisplayed };
		setListConfig(updatedConfig);
		setIsRecentSitesListDisplayed(!isRecentSitesListDisplayed);
	};

	const changeDisplayMode = (mode: "grid" | "list") => {
		const updatedConfig = { ...currentConfig, mode };
		setListConfig(updatedConfig);
		setDisplayMode(mode);
	};

	const sortItems = (orderPointer: IQueryValue[], isAscending: boolean) => {
		scrollToTop();
		setPage(firstPage);
		const orderString = orderPointer[0].value.toString();
		const sortedState = getSortedListStatus(orderString, isAscending);
		setSortedListStatus(sortedState);
		const pointer = orderString === "title" ? [{ value: "name", label: "value" }] : orderPointer;
		setFiltersSelection("order", pointer, isAscending);
		const updatedConfig = { ...currentConfig, filter: filterQuery, sortedListStatus: sortedState };
		setListConfig(updatedConfig);
	};

	const filterItems = async (filterPointer: string, filtersSelected: IQueryValue[]) => {
		scrollToTop();
		setPage(firstPage);
		setFiltersSelection(filterPointer, filtersSelected);
		const updatedConfig = { ...currentConfig, filter: filterQuery };
		setListConfig(updatedConfig);
	};

	const {
		resetBulkSelection,
		selectedItems,
		isSelected,
		areItemsSelected,
		checkState,
		addToBulkSelection,
		selectAllItems,
		setHoverCheck,
	} = useBulkSelection(sitesIds);

	const bulkFilter = useCallback((bulkSelection: number[]) => filterByStatus(bulkSelection, sites), [sites]);

	const handleAddToBulk = useCallback(
		(item: ICheck) => addToBulkSelection(item, bulkFilter),
		[addToBulkSelection, bulkFilter],
	);

	const handleSelectAll = () => selectAllItems(bulkFilter);

	const selectItems = () => (checkState.isAllSelected ? resetBulkSelection() : handleSelectAll());

	const bulkPublishAction = async (isPublish: boolean) => {
		setIsBulkLoading(true);
		const { notPublished, published } = selectedItems;
		const params = getParams();

		if (notPublished.length > 0 && isPublish) {
			await publishSitesBulk(notPublished, params);
		}
		if (published.length > 0 && !isPublish) {
			await unpublishSitesBulk(published, params);
		}
		resetBulkSelection();
		setIsBulkLoading(false);
	};

	const bulkPublish = () => bulkPublishAction(true);
	const bulkUnpublish = () => bulkPublishAction(false);

	const Header = (
		<BulkHeader
			showBulk={areItemsSelected(sitesIds)}
			bulkPublish={bulkPublish}
			bulkUnpublish={bulkUnpublish}
			checkState={checkState}
			selectAllItems={handleSelectAll}
			totalItems={totalItems}
			selectItems={selectItems}
			sortItems={sortItems}
			sortedListStatus={sortedListStatus}
			filterItems={filterItems}
			filterValues={currentConfig.filterValues}
			setHoverCheck={setHoverCheck}
			selectedItems={selectedItems}
			isLoading={isBulkLoading}
		/>
	);

	const mappedSites = useMemo(
		() =>
			sites.length > 0 ? (
				sites.map((site: ISite) => {
					const isItemSelected = isSelected(site.id);
					return displayMode === "grid" ? (
						<GridSiteItem key={site.id} site={site} getParams={getParams} />
					) : (
						<ListSiteItem
							key={site.id}
							site={site}
							isSelected={isItemSelected}
							onCheck={handleAddToBulk}
							getParams={getParams}
							hoverCheck={checkState.hoverCheck}
						/>
					);
				})
			) : (
				<S.EmptyStateWrapper data-testid="empty-state">
					<EmptyState icon="search" message="We couldn't find what you are looking for. Please, try another search." />
				</S.EmptyStateWrapper>
			),
		[sites, displayMode, isSelected, handleAddToBulk, getParams, checkState.hoverCheck],
	);

	const showPagination = totalItems > itemsPerPage;

	const gridList = (
		<>
			<S.SearchTagsGrid>
				<SearchTagsBar query={searchQuery} setQuery={setSearchQuery} />
			</S.SearchTagsGrid>
			<S.GridList data-testid="sites-grid-list" isEmpty={sites.length === 0} fullWidth={sites.length >= 5}>
				{mappedSites}
			</S.GridList>
			{showPagination && (
				<S.PaginationWrapper>
					<Pagination totalItems={totalItems} setPage={handlePageChange} itemsPerPage={itemsPerPage} currPage={page} />
				</S.PaginationWrapper>
			)}
		</>
	);

	const handleSearch = (query: string) => {
		scrollToTop();
		setSearchQuery(query);
		setPage(1);
	};

	const listTable = (
		<TableList tableHeader={Header} pagination={pagination} hasFixedHeader={true} tableRef={tableRef}>
			<>
				<S.SearchTags>
					<SearchTagsBar query={searchQuery} setQuery={handleSearch} />
					<FilterTagsBar
						filters={filterValues}
						setFilters={setFiltersSelection}
						resetFilters={resetFilterQuery}
						labels={filterLabels}
					/>
				</S.SearchTags>
				{mappedSites}
			</>
		</TableList>
	);

	const handleAnimationEnd = () => {
		setHasAnimation(false);
		if (!currentUser.profileCreated) {
			toggleWelcomeModal();
		}
	};

	if (isLoading && !hasAnimation) {
		return <Loading />;
	}

	return (
		<MainWrapper
			title="Sites"
			rightButton={rightButtonProps}
			searchAction={handleSearch}
			hasAnimation={hasAnimation}
			searchValue={searchQuery}
		>
			<ErrorToast ref={errorRef} />
			<S.SitesListWrapper className={hasAnimation ? "animate" : ""}>
				{recentSites?.length > 0 ? (
					<RecentSitesList
						recentSites={recentSites}
						isRecentSitesListDisplayed={isRecentSitesListDisplayed}
						toggleRecentSites={toggleRecentSites}
					/>
				) : null}
				<AllSitesHeader
					displayMode={displayMode}
					sortItems={sortItems}
					sortedListStatus={sortedListStatus}
					changeDisplayMode={changeDisplayMode}
				/>
				{isSitesLoading && !hasAnimation ? (
					<Loading />
				) : (
					<S.AllSitesListWrapper className={hasAnimation ? "animate" : ""} onAnimationEnd={handleAnimationEnd}>
						{displayMode === "grid" ? gridList : listTable}
					</S.AllSitesListWrapper>
				)}
			</S.SitesListWrapper>
			<SiteModal
				isOpen={isOpen}
				toggleModal={toggleModal}
				saveSettings={saveSettings}
				setHistoryPush={setHistoryPush}
			/>
			<WelcomeModal isOpen={isWelcomeOpen} toggleModal={toggleWelcomeModal} />
		</MainWrapper>
	);
};

interface ISitesProps {
	sites: ISite[];
	recentSites: ISite[];
	totalItems: number;
	token: string;
	error: IError;
	config: ISiteListConfig;
	hasAnimation: boolean;
	isLoading: boolean;
	isSitesLoading: boolean;
	currentUser: IUser;
}

const mapStateToProps = (state: IRootState) => ({
	totalItems: state.sites.sitesTotalItems,
	token: state.app.token,
	sites: state.sites.sites,
	recentSites: state.sites.recentSites,
	error: state.app.error,
	config: state.sites.config,
	hasAnimation: state.app.hasAnimation,
	isLoading: state.app.isLoading,
	isSitesLoading: state.sites.isSitesLoading,
	currentUser: state.users.currentUser!,
});

interface IDispatchProps {
	setHistoryPush(path: string, isEditor?: boolean): void;
	saveSettings(form: Partial<ISite>): Promise<boolean>;
	getSites(params: IGetSitesParams): Promise<void>;
	publishSitesBulk(ids: number[], params?: IGetSitesParams): Promise<void>;
	unpublishSitesBulk(ids: number[], params?: IGetSitesParams): Promise<void>;
	setListConfig(config: ISiteListConfig): void;
	setHasAnimation(hasAnimation: boolean): void;
}

const mapDispatchToProps = {
	setHistoryPush: appActions.setHistoryPush,
	saveSettings: sitesActions.saveSettings,
	getSites: sitesActions.getSites,
	publishSitesBulk: sitesActions.publishSitesBulk,
	unpublishSitesBulk: sitesActions.unpublishSitesBulk,
	setListConfig: sitesActions.setListConfig,
	setHasAnimation: appActions.setHasAnimation,
};

export type ISitesListProps = ISitesProps & IDispatchProps;

export default connect(mapStateToProps, mapDispatchToProps)(SitesList);
