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

import {
	EmptyState,
	ErrorToast,
	FilterTagsBar,
	MainWrapper,
	Nav,
	SearchTagsBar,
	TableList,
	Toast,
} from "@ax/components";
import { appActions } from "@ax/containers/App";
import { redirectsActions } from "@ax/containers/Redirects";
import { useBulkSelection, useModal, useToast } from "@ax/hooks";
import type { IEmptyStateProps, INavItem, IQueryValue, IRedirect, IRootState, ISite } from "@ax/types";

import { DeleteModal, ImportCheckModal, ImportModal, OverwriteModal } from "./atoms";
import BulkHeader from "./BulkHeader";
import { useFilterQuery, useSortedListStatus } from "./hooks";
import RedirectItem from "./RedirectItem";
import RedirectPanel from "./RedirectPanel";
import { getSortedListStatus } from "./utils";

import * as S from "./style";

const Redirects = (props: IProps): JSX.Element => {
	const {
		navItems,
		currentNavItem,
		setHistoryPush,
		getRedirects,
		redirects,
		totalItems,
		deleteRedirect,
		addRedirect,
		currentSite,
		importRedirects,
		imports,
		totalImports,
		isLoading,
	} = props;

	const itemsPerPage = 50;
	const firstPage = 1;
	const [page, setPage] = useState(1);
	const [isScrolling, setIsScrolling] = useState(false);
	const [isBulkLoading, setIsBulkLoading] = useState(false);
	const [isOpenedSecond, setIsOpenedSecond] = useState(false);
	const { isOpen, toggleModal } = useModal();
	const { isOpen: isOpenDelete, toggleModal: toggleModalDelete } = useModal();
	const tableRef = useRef<HTMLDivElement>(null);
	const { sortedListStatus, setSortedListStatus } = useSortedListStatus();
	const { setFiltersSelection, resetFilterQuery, filterValues, filterQuery } = useFilterQuery(currentSite);
	const { isVisible, toggleToast, setIsVisible } = useToast();
	const { isVisible: isImportVisible, toggleToast: toggleImportToast, setIsVisible: setIsImportVisible } = useToast();
	const { isOpen: isOpenOverwrite, toggleModal: toggleOverwriteModal } = useModal();
	const { isOpen: isOpenImport, toggleModal: toggleImportModal } = useModal();
	const { isOpen: isOpenCheckImport, toggleModal: toggleCheckImportModal } = useModal();
	const [importData, setImportData] = useState<{ from: string; to: string }[]>([]);
	const [isUploading, setIsUploading] = useState(false);
	const [searchQuery, setSearchQuery] = useState<string>("");
	const [searchFilter, setSearchFilter] = useState<string>("");
	const [isEmpty, setIsEmpty] = useState(false);
	const [emptyStateProps, setEmptyStateProps] = useState<IEmptyStateProps>({});

	const initState = {
		from: "",
		to: {
			pageId: undefined,
			url: "",
		},
	};

	const [formValues, setFormValues] = useState<IRedirect>(initState);

	const redIds = redirects?.map((red: any) => red.id);

	const getParams = useCallback(() => {
		const params = {
			page,
			itemsPerPage,
			pagination: true,
			query: searchQuery.trim(),
			filterBy: searchFilter === "filterby" || searchQuery.trim() === "" ? "" : searchFilter,
		};

		return params;
	}, [page, searchQuery, searchFilter]);

	// biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
	useEffect(() => {
		const params = getParams();
		getRedirects(params, filterQuery);
		if (tableRef.current) {
			tableRef.current.scrollTo(0, 0);
		}
	}, [page, filterQuery, searchQuery, searchFilter]);

	// biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
	useEffect(() => {
		if (!isLoading) {
			const emptyState: IEmptyStateProps = {};
			const isSearching = searchQuery.length > 0;
			if (isSearching) {
				emptyState.icon = "search";
				emptyState.title = "Oh! No Results Found";
				emptyState.message = "We couldn’t find what you are looking for. Please, try another search.";
			} else {
				emptyState.message = "To have a redirects on your site, create as many redirects as you want.";
				emptyState.button = "Create New redirect";
				emptyState.action = handleModal;
			}
			setIsEmpty(!redirects.length);
			setEmptyStateProps(emptyState);
		}
	}, [isLoading]);

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

	const bulkDelete = async () => {
		setIsBulkLoading(true);
		const deleted = await deleteRedirect(selectedItems.all);
		if (deleted) {
			toggleToast();
		}
		toggleModalDelete();
		setIsBulkLoading(false);
	};

	const unselectAllItems = () => resetBulkSelection();

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

	const handleSelectAll = () => selectAllItems();

	const sortItems = async (orderPointer: IQueryValue[], isAscending: boolean) => {
		setPage(firstPage);
		const sortedState = getSortedListStatus(orderPointer[0].value.toString(), isAscending);
		setSortedListStatus(sortedState);

		setFiltersSelection("order", orderPointer, isAscending);
	};

	const filterItems = async (filterPointer: string, filtersSelected: IQueryValue[]) => {
		setPage(firstPage);
		setFiltersSelection(filterPointer, filtersSelected);
	};

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

	const TableHeader = (
		<BulkHeader
			showBulk={areItemsSelected(redIds)}
			bulkDelete={toggleModalDelete}
			selectAllItems={handleSelectAll}
			totalItems={totalItems}
			selectItems={selectItems}
			checkState={checkState}
			isScrolling={isScrolling}
			filterItems={filterItems}
			filterValues={filterValues}
			isSiteItem={!!currentSite}
			sortItems={sortItems}
			sortedListStatus={sortedListStatus}
			setHoverCheck={setHoverCheck}
			selectedItems={selectedItems}
			isLoading={isBulkLoading}
		/>
	);

	const onScroll = (e: any) => setIsScrolling(e.target.scrollTop > 0);

	const toggleSecondaryPanel = () => setIsOpenedSecond(!isOpenedSecond);

	const rightButtonProps = {
		label: "New Redirect",
		action: () => handleModal(),
	};

	const rightLineButtonProps = {
		label: "Import Redirects",
		action: () => toggleImportModal(),
	};

	const handleMenuClick = (path: string) => {
		setHistoryPush(path);
	};

	const handleModal = () => {
		setFormValues(initState);
		setIsOpenedSecond(false);
		toggleModal();
	};

	const handleAddRedirect = async (force?: boolean) => {
		const toPage = formValues.to.pageId ? formValues.to.pageId : formValues.to.url;
		await addRedirect({ from: formValues.from, to: toPage }, toggleOverwriteModal, force, filterQuery);
		isOpen && toggleModal();
	};

	const addItemActionForce = () => {
		handleAddRedirect(true);
		toggleOverwriteModal();
	};

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

	const toastProps = {
		setIsVisible,
		message: "Redirect deleted",
	};

	const toastImportProps = {
		setIsVisible: setIsImportVisible,
		message: `${(imports?.ok.length || 0) + (imports?.existing.length || 0)} Redirects imported`,
	};

	const mainDeleteModalAction = {
		title: isBulkLoading ? "Deleting" : "Delete",
		onClick: bulkDelete,
		disabled: isBulkLoading,
	};

	const secondaryDeleteModalAction = { title: "Cancel", onClick: toggleModalDelete };

	const mainOverwriteModalAction = {
		title: "Overwrite redirect",
		onClick: addItemActionForce,
	};

	const secondaryOverwriteModalAction = { title: "Cancel", onClick: toggleOverwriteModal };

	const handleCheckImportData = async (data: { from: string; to: string }[]) => {
		setImportData(data);
		await importRedirects(data, true);
		toggleImportModal();
		setIsUploading(false);
		toggleCheckImportModal();
	};

	const handleImportData = async () => {
		const imported = await importRedirects(importData, false);
		if (imported) {
			toggleImportToast();
		}
		toggleCheckImportModal();
	};

	const mainImportModalAction = {
		title: "Import correct directs",
		onClick: handleImportData,
	};

	const secondaryImportModalAction = { title: "Cancel", onClick: toggleCheckImportModal };

	const searchFilters = [
		{ value: "from", label: "Old URL" },
		{ value: "to", label: "New URL" },
	];

	return (
		<MainWrapper
			backLink={false}
			title="SEO Settings"
			rightButton={rightButtonProps}
			rightLineButton={rightLineButtonProps}
			searchAction={handleSearch}
			filterSearchAction={setSearchFilter}
			searchFilters={searchFilters}
			searchValue={searchQuery}
		>
			<S.Wrapper>
				<Nav current={currentNavItem} items={navItems} onClick={handleMenuClick} />
				<S.ContentWrapper>
					<ErrorToast />
					<S.TitleWrapper>
						<S.Title>URL Redirects Manager</S.Title>
						<S.Description>
							You can create 301 redirects to direct users and search engines from an old URL to a new one.
						</S.Description>
					</S.TitleWrapper>
					<S.TableWrapper>
						<TableList
							tableHeader={TableHeader}
							pagination={pagination}
							onScroll={onScroll}
							hasFixedHeader={true}
							tableRef={tableRef}
						>
							<>
								<S.SearchTags>
									<SearchTagsBar query={searchQuery} setQuery={handleSearch} />
									{!currentSite && (
										<FilterTagsBar
											filters={filterValues}
											setFilters={setFiltersSelection}
											resetFilters={resetFilterQuery}
											labels={{ sites: "Site" }}
										/>
									)}
								</S.SearchTags>
								{isEmpty ? (
									<S.EmptyWrapper>
										<EmptyState {...emptyStateProps} />
									</S.EmptyWrapper>
								) : (
									redirects.map((redirect: any) => {
										const isItemSelected = isSelected(redirect.id);
										return (
											<RedirectItem
												key={redirect.id}
												redirect={redirect}
												isSelected={isItemSelected}
												onChange={addToBulkSelection}
												toggleToast={toggleToast}
												setFormValues={setFormValues}
												formValues={formValues}
												addRedirect={handleAddRedirect}
												currentFilter={filterQuery}
												isSiteItem={!!currentSite}
												hoverCheck={checkState.hoverCheck}
											/>
										);
									})
								)}
							</>
						</TableList>
					</S.TableWrapper>
					{isVisible && <Toast {...toastProps} />}
					{isImportVisible && <Toast {...toastImportProps} />}
				</S.ContentWrapper>
			</S.Wrapper>
			<RedirectPanel
				isOpen={isOpen}
				isOpenedSecond={isOpenedSecond}
				toggleModal={toggleModal}
				toggleSecondaryPanel={toggleSecondaryPanel}
				formValues={formValues}
				setFormValues={setFormValues}
				addRedirect={handleAddRedirect}
				currentFilter={filterQuery}
			/>
			<DeleteModal
				isOpen={isOpenDelete}
				toggleModal={toggleModalDelete}
				secondaryModalAction={secondaryDeleteModalAction}
				mainModalAction={mainDeleteModalAction}
			/>
			<OverwriteModal
				isOpen={isOpenOverwrite}
				toggleModal={toggleOverwriteModal}
				secondaryModalAction={secondaryOverwriteModalAction}
				mainModalAction={mainOverwriteModalAction}
			/>
			<ImportModal
				isOpen={isOpenImport}
				toggleModal={toggleImportModal}
				checkImportData={handleCheckImportData}
				isUploading={isUploading}
				setIsUploading={setIsUploading}
			/>
			<ImportCheckModal
				isOpen={isOpenCheckImport}
				toggleModal={toggleCheckImportModal}
				mainModalAction={mainImportModalAction}
				secondaryModalAction={secondaryImportModalAction}
				imports={imports}
				totalImports={totalImports}
			/>
		</MainWrapper>
	);
};

const mapStateToProps = (state: IRootState) => ({
	redirects: state.redirects.redirects,
	totalItems: state.redirects.totalItems,
	currentSite: state.sites.currentSiteInfo,
	totalImports: state.redirects.totalImports,
	imports: state.redirects.imports,
	isLoading: state.app.isLoading,
});

const mapDispatchToProps = {
	setHistoryPush: appActions.setHistoryPush,
	getRedirects: redirectsActions.getRedirects,
	deleteRedirect: redirectsActions.deleteRedirect,
	addRedirect: redirectsActions.addRedirect,
	importRedirects: redirectsActions.importRedirects,
};
interface IRedirectsProps {
	navItems: INavItem[];
	currentNavItem: INavItem;
	redirects: IRedirect[];
	totalItems: number;
	currentSite: ISite | null;
	totalImports: number;
	isLoading: boolean;
	imports: null | {
		error: IRedirect[];
		existing: IRedirect[];
		ok: IRedirect[];
	};
}

interface IDispatchProps {
	setHistoryPush(path: string, isEditor?: boolean): void;
	getRedirects(params: any, filters: string): void;
	deleteRedirect(redirectID: number[]): Promise<boolean>;
	addRedirect(
		redirect: { from: string; to: string | number },
		errorAction: () => void,
		force?: boolean,
		filter?: string,
	): Promise<void>;
	importRedirects(redirects: { from: string; to: string | number }[], check: boolean): Promise<boolean>;
}

type IProps = IRedirectsProps & IDispatchProps;

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