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

import {
	EmptyState,
	ErrorToast,
	FilterTagsBar,
	MainWrapper,
	Notification,
	SearchTagsBar,
	TableList,
	Toast,
} from "@ax/components";
import { appActions } from "@ax/containers/App";
import { formsActions } from "@ax/containers/Forms";
import { ITEMS_PER_PAGE } from "@ax/containers/Forms/constants";
import { getFormTemplates, getSchemaFormCategories } from "@ax/helpers";
import { useBulkSelection, useCategoryColors, useModals, usePermissions, useToast } from "@ax/hooks";
import type {
	FormContent,
	FormState,
	GetFormsParams,
	ILanguage,
	INotification,
	IQueryValue,
	IRootState,
	ISchema,
	ISite,
} from "@ax/types";

import { DeleteModal, UnPublishModal } from "../atoms";
import FormsMenu from "../FormsMenu";
import { getAllLangFormsIds, getSortedListStatus } from "../utils";
import BulkHeader from "./BulkHeader";
import FormItem from "./FormItem";
import { useFilterQuery, useSortedListStatus } from "./hooks";
import Summary from "./Summary";
import { TemplateModal } from "./TemplateModal";

import * as S from "./style";

const FormList = (props: IUserListProps): JSX.Element => {
	const {
		currentSiteInfo,
		forms,
		summary,
		lang,
		globalLangs,
		siteLanguages,
		setHistoryPush,
		addTemplate,
		getForms,
		setCurrentFormID,
		setLanguage,
		updateFormState,
		deleteForm,
	} = props;

	const firstPage = 1;
	const isSiteView = !!currentSiteInfo;
	const BASE_URL = isSiteView ? "/sites/forms" : "/forms";
	const isEmpty = !forms || forms.length === 0;
	const formTemplates = getFormTemplates();
	const noFormTemplates = !formTemplates || !Object.keys(formTemplates).length;
	const formCategories = getSchemaFormCategories();
	const featuredCategory = formCategories.find((cat) => cat.featured);
	const availableLanguages = isSiteView ? siteLanguages : globalLangs;
	const errorTemplates: INotification | null = noFormTemplates
		? { text: "There are not form templates exported", type: "error" }
		: null;

	const [page, setPage] = useState(firstPage);
	const [isScrolling, setIsScrolling] = useState(false);
	const [isBulkLoading, setIsBulkLoading] = useState(false);
	const [selectedTemplate, setSelectedTemplate] = useState<ISchema | null>(null);
	const [deleteAllVersions, setDeleteAllVersions] = useState(false);
	const [arePagesTranslated, setArePagesTranslated] = useState(false);
	const [searchQuery, setSearchQuery] = useState<string>("");
	const [notification, setNotification] = useState<INotification | null>(errorTemplates);
	const { isVisible, toggleToast, setIsVisible, state: toastState } = useToast();
	const { categoryColors, addCategoryColors } = useCategoryColors();
	const { isOpen, toggleModal } = useModals(["new", "delete", "unpublish"]);
	const { sortedListStatus, setSortedListStatus } = useSortedListStatus();
	const { setFiltersSelection, resetFilterQuery, filterValues, query: currentFilterQuery } = useFilterQuery();

	const wrapperRef = useRef<HTMLDivElement>(null);
	const tableRef = useRef<HTMLDivElement>(null);
	const formIds = forms.map((form) => form.id);
	const isSearching = searchQuery.length > 0;
	const isFiltering = currentFilterQuery.length > 0;

	const isAllowedTo = usePermissions({
		createForms: isSiteView ? "forms.createForms" : "global.forms.createForms",
		deleteForms: isSiteView ? "forms.deleteForms" : "global.forms.deleteForms",
		publishUnpublishForms: isSiteView ? "forms.publishUnpublishForms" : "global.forms.publishUnpublishForms",
	});

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

	const pagination = {
		setPage,
		itemsPerPage: ITEMS_PER_PAGE,
		totalItems: summary.total,
		currPage: page,
	};

	const getParams = useCallback(() => {
		const params: GetFormsParams = {
			page,
			query: searchQuery,
			filterQuery: currentFilterQuery,
		};

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

	// biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
	useEffect(() => {
		const handleGetForms = async () => {
			const params = getParams();
			await getForms(currentSiteInfo?.id || null, params);
		};
		handleGetForms();
	}, [getParams, lang]);

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

	const handleSelectAll = () => selectAllItems();

	const selectItems = () => (checkState.isAllSelected ? resetBulkSelection() : 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 goToEditor = () => setHistoryPush(`${BASE_URL}/editor`, true);

	const createNewForm = () => {
		const templateSchema = selectedTemplate ? selectedTemplate : Object.values(formTemplates)[0];
		addTemplate(templateSchema.component);
		setSelectedTemplate(null);
		setHistoryPush(`${BASE_URL}/editor/new`, true);
	};

	const handleClick = (formID: number) => {
		setCurrentFormID(formID);
		goToEditor();
	};

	const addNewAction = () => (Object.keys(formTemplates).length > 1 ? toggleModal("new") : createNewForm());

	const handleLanguage = (language: ILanguage) => {
		const { locale, id } = language;
		setLanguage({ locale, id });
		setPage(firstPage);
		resetBulkSelection();
	};

	const publishForm = async () => {
		setIsBulkLoading(true);
		const isUpdated = await updateFormState(selectedItems.all, "active");
		if (isUpdated) {
			resetBulkSelection();
			toggleToast(`${selectedItems.all.length} form${selectedItems.all.length > 1 ? "s" : ""} published`);
		}
		setIsBulkLoading(false);
	};

	const unpublishForm = async () => {
		setIsBulkLoading(true);
		const isUsed = forms.some((form) => selectedItems.all.includes(form.id) && !!form.formInUse);
		if (isUsed) {
			setNotification({
				text: "Some forms are currently used on pages. Please remove it from those pages before unpublishing.",
				type: "error",
			});
		} else {
			const isUpdated = await updateFormState(selectedItems.all, "inactive");
			if (isUpdated) {
				resetBulkSelection();
				toggleToast(`${selectedItems.all.length} form${selectedItems.all.length > 1 ? "s" : ""} unpublished`);
			}
		}
		setIsBulkLoading(false);
	};

	const handleDeleteForm = async () => {
		setIsBulkLoading(true);
		const idsToBeDeleted = getAllLangFormsIds(forms, selectedItems.all, deleteAllVersions);
		const isUsed = forms.some((form) => idsToBeDeleted.includes(form.id) && !!form.formInUse);
		if (isUsed) {
			setNotification({
				text: "Some forms are currently used on pages. Please remove it from those pages before deleting.",
				type: "error",
			});
		} else {
			const isDeleted = await deleteForm(idsToBeDeleted);
			if (isDeleted) {
				toggleToast(`${selectedItems.all.length} form${selectedItems.all.length > 1 ? "s" : ""} deleted`);
				toggleModal("delete");
			}
		}
		setIsBulkLoading(false);
	};

	const handleToggleBulkDelete = () => {
		const hasTranslations = forms
			.filter((form) => selectedItems.all.includes(form.id))
			.some((form) => form.dataLanguages.length > 1);
		setArePagesTranslated(hasTranslations);
		toggleModal("delete");
	};

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

	const bulkActions = isAllowedTo.publishUnpublishForms
		? [
				{
					icon: "upload-pending",
					text: "publish",
					action: publishForm,
				},
				{
					icon: "offline",
					text: "unpublish",
					action: () => toggleModal("unpublish"),
				},
			]
		: [];

	if (isAllowedTo.deleteForms) {
		bulkActions.push({
			icon: "delete",
			text: "delete",
			action: handleToggleBulkDelete,
		});
	}

	const TableHeader = (
		<BulkHeader
			selectedItems={selectedItems}
			filterValues={filterValues}
			showBulk={areItemsSelected(formIds)}
			selectAllItems={handleSelectAll}
			totalItems={summary.total}
			selectItems={selectItems}
			checkState={checkState}
			isScrolling={isScrolling}
			sortItems={sortItems}
			filterItems={filterItems}
			sortedListStatus={sortedListStatus}
			setHoverCheck={setHoverCheck}
			featuredCategory={featuredCategory}
			bulkActions={bulkActions}
			siteID={currentSiteInfo?.id || null}
			isLoading={isBulkLoading}
		/>
	);

	const handleCloseModal = () => {
		isOpen("new") && toggleModal("new");
		setSelectedTemplate(null);
	};

	const mainNewModalAction = {
		title: "Create new form",
		onClick: createNewForm,
		disabled: !selectedTemplate,
	};

	const secondaryNewModalAction = { title: "Cancel", onClick: handleCloseModal };

	const rightButtonProps = isAllowedTo.createForms
		? {
				label: "New",
				action: addNewAction,
				disabled: noFormTemplates,
			}
		: undefined;

	const emptyProps = {
		message: isAllowedTo.createForms ? "To start using forms in your site, create as many forms as you need" : "",
		button: isAllowedTo.createForms ? "New form" : undefined,
		action: addNewAction,
	};

	const emptySearchStateProps = {
		icon: "search",
		title: "Oh! No Results Found",
		message: "We couldn’t find what you are looking for. Please, try another search.",
	};

	const filterLabels = {
		translated: "Language",
		type: featuredCategory?.label || "",
		state: "State",
	};

	return (
		<MainWrapper
			title="Forms"
			rightButton={rightButtonProps}
			language={lang}
			availableLanguages={availableLanguages}
			languageAction={handleLanguage}
			searchAction={handleSearch}
			searchValue={searchQuery}
		>
			<S.FormListWrapper ref={wrapperRef} data-testid="forms-wrapper">
				<FormsMenu />
				<S.TableListWrapper>
					{notification && (
						<S.NotificationWrapper>
							<Notification
								type={notification.type}
								text={notification.text}
								btnText={notification.btnText}
								onClick={notification.onClick}
								resetError={() => setNotification(null)}
							/>
						</S.NotificationWrapper>
					)}
					<ErrorToast />
					{(isSearching || isFiltering || !isEmpty) && <Summary summary={summary} filterItems={filterItems} />}
					{
						<TableList
							tableHeader={TableHeader}
							pagination={pagination}
							onScroll={onScroll}
							hasFixedHeader={true}
							tableRef={tableRef}
						>
							<>
								<S.SearchTags>
									<SearchTagsBar query={searchQuery} setQuery={handleSearch} />
									<FilterTagsBar
										filters={filterValues}
										setFilters={setFiltersSelection}
										resetFilters={resetFilterQuery}
										labels={filterLabels}
									/>
								</S.SearchTags>
								{isEmpty ? (
									<S.EmptyWrapper data-testid="empty-wrapper">
										{isSearching || isFiltering ? (
											<EmptyState {...emptySearchStateProps} />
										) : (
											<EmptyState {...emptyProps} />
										)}
									</S.EmptyWrapper>
								) : (
									forms?.map((form) => {
										const isItemSelected = isSelected(form.id);
										return (
											<FormItem
												form={form}
												key={form.id}
												onClick={handleClick}
												onChange={addToBulkSelection}
												isSelected={isItemSelected}
												toggleToast={toggleToast}
												languages={availableLanguages}
												lang={lang}
												featuredCategory={featuredCategory}
												categoryColors={categoryColors}
												addCategoryColors={addCategoryColors}
												goToEditor={goToEditor}
												hoverCheck={checkState.hoverCheck}
												setNotification={setNotification}
											/>
										);
									})
								)}
							</>
						</TableList>
					}
				</S.TableListWrapper>
			</S.FormListWrapper>
			{isOpen("new") && (
				<TemplateModal
					isOpen={isOpen("new")}
					toggleModal={handleCloseModal}
					templates={formTemplates}
					mainModalAction={mainNewModalAction}
					secondaryModalAction={secondaryNewModalAction}
					setSelectedTemplate={setSelectedTemplate}
					selectedTemplate={selectedTemplate}
				/>
			)}
			{isOpen("unpublish") && (
				<UnPublishModal
					isOpen={isOpen("unpublish")}
					toggleModal={() => toggleModal("unpublish")}
					onUnpublish={unpublishForm}
					title={selectedItems.all.length > 1 ? "these forms" : "this form"}
				/>
			)}
			{isOpen("delete") && (
				<DeleteModal
					isOpen={isOpen("delete")}
					toggleModal={() => toggleModal("delete")}
					onDelete={handleDeleteForm}
					title={selectedItems.all.length > 1 ? "these forms" : "this form"}
					{...{ isTranslated: arePagesTranslated, deleteAllVersions, setDeleteAllVersions }}
				/>
			)}
			{isVisible && <Toast setIsVisible={setIsVisible} message={toastState} />}
		</MainWrapper>
	);
};

const mapStateToProps = (state: IRootState) => ({
	currentSiteInfo: state.sites.currentSiteInfo,
	forms: state.forms.forms,
	summary: state.forms.summary,
	siteLanguages: state.sites.currentSiteLanguages,
	globalLangs: state.app.globalLangs,
	lang: state.app.lang,
});

const mapDispatchToProps = {
	setHistoryPush: appActions.setHistoryPush,
	addTemplate: formsActions.addTemplate,
	getForms: formsActions.getForms,
	setCurrentFormID: formsActions.setCurrentFormID,
	setLanguage: appActions.setLanguage,
	deleteForm: formsActions.deleteForm,
	updateFormState: formsActions.updateFormState,
};

interface IDispatchProps {
	setHistoryPush(path: string, isEditor?: boolean): Promise<void>;
	addTemplate(template: string): Promise<void>;
	getForms(siteID: number | null, params?: GetFormsParams): Promise<void>;
	setCurrentFormID(formID: number | null): void;
	setLanguage(lang: { locale: string; id: number }): void;
	deleteForm: (formID: number | number[]) => Promise<boolean>;
	updateFormState(formID: number | number[], state: FormState): Promise<boolean>;
}

interface IProps {
	currentSiteInfo: ISite | null;
	forms: FormContent[];
	summary: { total: number; active: number; inactive: number };
	lang: { locale: string; id: number };
	siteLanguages: ILanguage[];
	globalLangs: ILanguage[];
}

export type IUserListProps = IProps & IDispatchProps;

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