import { useEffect, useRef, useState } from "react";
import { createPortal } from "react-dom";

import { IconAction, MenuItem, SearchField } from "@ax/components";
import SideModalOption from "@ax/components/SideModal/SideModalOption";
import { filterByCategory, getDisplayName } from "@ax/helpers";
import { useHandleClickOutside } from "@ax/hooks";
import type { IComponent, IContainedComponent, ModuleCategoryInfo } from "@ax/types";

import * as S from "./style";

const SideModal = (props: ISideModalProps): JSX.Element | null => {
	const {
		whiteList,
		optionsType,
		isOpen,
		toggleModal,
		componentOptions,
		categories,
		onClick,
		current,
		showSearch,
		theme,
	} = props;

	const componentList: IComponent[] = [];
	const hasCategories = !!(categories && categories.length > 1);
	const optionList = componentOptions ? componentList : whiteList || [];

	const filteredValues = {
		options: optionList,
		category: "all",
	};

	const [options, setOptions] = useState(filteredValues);
	const [searchQuery, setSearchQuery] = useState("");
	const node = useRef<HTMLDivElement | null>(null);

	useEffect(() => {
		const handleKeyPress = (event: KeyboardEvent) => {
			if (event.key === "Escape") {
				toggleModal();
			}
		};

		if (isOpen) {
			document.addEventListener("keydown", handleKeyPress);
		}

		return () => {
			document.removeEventListener("keydown", handleKeyPress);
		};
	}, [isOpen, toggleModal]);

	// biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
	useEffect(() => {
		if (componentOptions) {
			for (const key in componentOptions) {
				const currentComponent = current?.component;
				const availableComponent = componentOptions[key].component;
				if (availableComponent !== currentComponent) {
					componentList.push(componentOptions[key]);
				}
			}
			const filteredOptions = {
				options: componentList,
				category: "all",
			};
			setOptions(filteredOptions);
		} else {
			setOptions(filteredValues);
		}
	}, [current, whiteList]);

	const handleClick = (moduleType: string | IComponent) => {
		if (onClick) {
			onClick(moduleType);
		}
		handleCloseModal();
	};

	const handleCloseModal = () => {
		setSearchQuery("");
		isOpen && toggleModal();
	};

	const handleClickOutside = (e: any) => {
		if (node.current?.contains(e.target)) {
			return;
		}

		handleCloseModal();
	};

	useHandleClickOutside(isOpen, handleClickOutside);

	const filterOptionsByCategory = (category: string) => {
		const options = category !== "all" ? filterByCategory(optionList, category) : optionList;

		const filteredValues = {
			options,
			category,
		};

		setOptions(filteredValues);
	};

	const getCategoryItem = (category: ModuleCategoryInfo, i: number) => {
		const { value, label } = category;
		const filterOptions = () => filterOptionsByCategory(value);
		const isSelected = value === options.category;
		const selectedClass = isSelected ? "selected" : "";

		return (
			<MenuItem key={`${value}${i}`} className={selectedClass} onClick={filterOptions}>
				<S.NavLink data-testid="side-modal-nav-link">
					<S.Link data-testid="side-modal-link" active={isSelected}>
						{label}
					</S.Link>
				</S.NavLink>
			</MenuItem>
		);
	};

	const featuredFilters =
		hasCategories &&
		categories
			.filter((cat) => cat.featured)
			.map((category: ModuleCategoryInfo, i: number) => {
				return getCategoryItem(category, i);
			});

	const filters =
		hasCategories &&
		categories
			.filter((cat) => !cat.featured)
			.map((category: ModuleCategoryInfo, i: number) => {
				return getCategoryItem(category, i);
			});

	const filteredOptions = options.options?.map((option, i) => {
		const optionComponent = typeof option === "string" ? option : option.component;
		const optionKey = typeof option === "string" ? option : (option.id ?? option.editorID ?? optionComponent);
		const displayName = getDisplayName(optionComponent);
		if (searchQuery.length > 0) {
			const name = displayName.toLowerCase();
			const search = searchQuery.toLocaleLowerCase();
			if (!name.includes(search)) return null;
		}
		return (
			<SideModalOption option={option} handleClick={handleClick} key={optionKey} theme={theme}>
				{displayName}
			</SideModalOption>
		);
	});

	return createPortal(
		<S.Wrapper ref={node} optionsType={optionsType} isOpen={isOpen} data-testid="side-modal">
			{isOpen && (
				<>
					<S.Header>
						<S.Title data-testid="side-modal-title">{optionsType}</S.Title>
						<S.ButtonWrapper data-testid="side-modal-close-button">
							<IconAction icon="close" onClick={handleCloseModal} />
						</S.ButtonWrapper>
					</S.Header>
					<S.ColumnsWrapper>
						{(filters || featuredFilters) && (
							<S.FilterContent>
								{featuredFilters && <S.FeaturedWrapper>{featuredFilters}</S.FeaturedWrapper>}
								{filters && <div>{filters}</div>}
							</S.FilterContent>
						)}
						<S.Content>
							{showSearch && optionsType !== "components" && (
								<S.SearchWrapper>
									<SearchField
										onChange={setSearchQuery}
										value={searchQuery}
										searchOnEnter={false}
										placeholder="Search"
									/>
								</S.SearchWrapper>
							)}
							{filteredOptions}
						</S.Content>
					</S.ColumnsWrapper>
				</>
			)}
		</S.Wrapper>,
		document.body,
	);
};

export interface ISideModalProps {
	isOpen: boolean;
	whiteList?: string[];
	categories?: ModuleCategoryInfo[];
	optionsType: string;
	toggleModal: () => void;
	onClick?: (moduleType: string | IComponent) => void;
	componentOptions?: Record<string, IComponent>;
	current?: IContainedComponent;
	showSearch?: boolean;
	theme: string;
}

export default SideModal;
