import React, { Dispatch, SetStateAction, useCallback, useEffect, useState } from "react";
import { connect } from "react-redux";

import { FormContent, GetFormsParams, IModal, IQueryValue, IRootState } from "@ax/types";
import {
	Button,
	CategoryFilter,
	EmptyState,
	ErrorToast,
	FilterTagsBar,
	Loading,
	Modal,
	SearchField,
	SearchTagsBar,
	Tabs,
} from "@ax/components";
import { getSchemaFormCategories } from "@ax/helpers";
import { formsActions } from "@ax/containers/Forms";

import * as S from "./style";

const FormModal = (props: IFormModal): JSX.Element => {
	const { isOpen, toggleModal, site, forms, categories, getForms, selectedForm, setSelectedForm, selectForm } = props;

	const tabs: string[] = [];
	if (site) {
		tabs.unshift(...["Site", "Global"]);
	}

	const formCategories = getSchemaFormCategories();
	const isGlobal = !site;

	const filterLabels = formCategories.reduce((a, v) => ({ ...a, [v.value]: v.label }), {});

	const [selectedTab, setSelectedTab] = useState(site ? "Site" : "Global");
	const [searchQuery, setSearchQuery] = useState<string>("");
	const [isLoading, setIsLoading] = useState<boolean>(false);

	const initialState = formCategories.reduce((a, v) => ({ ...a, [v.value]: [{ value: "all", label: "All" }] }), {});
	const [filterValues, setFilterValues] = useState<Record<string, IQueryValue[]>>(initialState);

	const siteID = site && selectedTab !== "Global" ? site : null;
	const isFiltering = searchQuery.length > 0 || Object.values(filterValues).length;

	const getParams = useCallback(() => {
		const userCategories = Object.values(filterValues).reduce((acc: (number | string)[], current: IQueryValue[]) => {
			const values = current.filter((val) => val.value !== "all").map((val) => val.value);
			return [...acc, ...values];
		}, []);

		const hasSchemaCategories = !!categories && categories.length > 0;
		const filterCategories = hasSchemaCategories ? [...categories, ...userCategories] : userCategories;

		const typeFilter = filterCategories.length ? `&type=${filterCategories.join(",")}` : "";

		const params: GetFormsParams = {
			page: 1,
			pagination: false,
			query: searchQuery,
			filterQuery: `&state=active${typeFilter}`,
		};

		return params;
	}, [searchQuery, filterValues, categories]);

	// biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
	useEffect(() => {
		const handleGetForms = async () => {
			setIsLoading(true);
			const params = getParams();
			await getForms(siteID, params, false);
			setIsLoading(false);
		};

		let isMounted = true;
		if (isMounted) {
			handleGetForms();
		}

		return function cleanup() {
			isMounted = false;
		};
	}, [getParams, selectedTab]);

	const handleSelectedTab = (tab: string) => {
		if (tab !== selectedTab) {
			setSelectedTab(tab);
		}
	};

	const filterItems = (filterPointer: string, filtersSelected: IQueryValue[]) =>
		setFilterValues((state) => ({ ...state, [filterPointer]: filtersSelected }));

	const resetFilterQuery = () => setFilterValues(initialState);

	const filters = formCategories ? (
		<S.Filters>
			{formCategories.map((formCategory, i: number) => (
				<S.FilterWrapper key={formCategory.value}>
					<CategoryFilter
						filterItems={filterItems}
						value={filterValues[formCategory.value]}
						formCategory={formCategory}
						siteID={siteID}
						pointer={formCategory.value}
						autoWidth={true}
						menuPosition={i === formCategories.length - 1 && !isGlobal ? "right" : "left"}
						hideValues={categories}
						hideOnLoading={true}
					/>
				</S.FilterWrapper>
			))}
		</S.Filters>
	) : (
		<></>
	);

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

	return (
		<Modal isOpen={isOpen} hide={toggleModal} title="Select Form" size="XL">
			<S.Wrapper>
				<S.Header>
					{!isGlobal && (
						<S.ModalTabs>
							{!!tabs.length && (
								<S.TabsWrapper>
									<Tabs tabs={tabs} active={selectedTab} setSelectedTab={handleSelectedTab} noMargins />
								</S.TabsWrapper>
							)}
							{filters}
						</S.ModalTabs>
					)}
					<S.SearchWrapper>
						{isGlobal && filters}
						<S.Search full={!isGlobal}>
							<SearchField
								onChange={setSearchQuery}
								value={searchQuery}
								placeholder="Type a name or #tag"
								focus={false}
							/>
						</S.Search>
					</S.SearchWrapper>
				</S.Header>
				<S.ModalContent>
					<ErrorToast size="l" />
					<S.SearchTags>
						<SearchTagsBar query={searchQuery} setQuery={setSearchQuery} />
						<FilterTagsBar
							filters={filterValues}
							setFilters={filterItems}
							labels={filterLabels}
							resetFilters={resetFilterQuery}
						/>
					</S.SearchTags>
					{isLoading ? (
						<Loading />
					) : isFiltering && !forms.length ? (
						<S.EmptyWrapper data-testid="empty-wrapper">
							<EmptyState {...emptySearchStateProps} />
						</S.EmptyWrapper>
					) : (
						<S.FormsGrid>
							{forms.map((form) => {
								const handleClick = () => setSelectedForm(form);
								return (
									<S.FormWrapper key={form.id}>
										<S.FormItem onClick={handleClick} isSelected={form.id === selectedForm?.id}>
											<img src={form.thumbnail} alt={form.title} title={form.title} />
										</S.FormItem>
										<S.FormTitle>{form.title}</S.FormTitle>
									</S.FormWrapper>
								);
							})}
						</S.FormsGrid>
					)}
				</S.ModalContent>
				<S.ModalFooter>
					<Button type="button" buttonStyle="text" onClick={toggleModal}>
						Close
					</Button>
					<Button type="button" onClick={selectForm} disabled={!selectedForm}>
						Select Form
					</Button>
				</S.ModalFooter>
			</S.Wrapper>
		</Modal>
	);
};

interface IFormModal extends IModal {
	site: number | null;
	forms: FormContent[];
	selectedForm: FormContent | null;
	getForms(siteID: number | null, params?: GetFormsParams, loading?: boolean): Promise<void>;
	setSelectedForm: Dispatch<SetStateAction<FormContent | null>>;
	selectForm(): void;
	categories?: string[];
}

const mapStateToProps = (state: IRootState) => ({
	forms: state.forms.forms,
});

const mapDispatchToProps = {
	getForms: formsActions.getForms,
};

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