import {TbSearch} from "react-icons/tb";
import {useEffect, useRef, useState} from "react";

// Constants.
import {DEFAULT_RULES, RULES_TYPES} from "@app/config/constants";

// Components.
import ListBox from "@app/components/ListBox";
import SearchRuleGroupItem from "./SearchRuleGroupItem";

function getRulesByType(rules, typeID) {
	return rules.filter((rule) => rule.typeID === typeID);
}

function getFilteredRules({
														rules,
														typeID,
														query
													}) {
	if (typeID && query) {
		return getRulesByType(rules, typeID).filter((rule) => rule.name.toLowerCase().includes(query.toLowerCase()));
	} else if (typeID) {
		return rules.filter((rule) => rule.typeID === typeID);
	} else if (query) {
		return rules.filter((rule) => rule.name.toLowerCase().includes(query.toLowerCase()));
	} else {
		return rules;
	}
}

function transformRulesByType(rules) {
	let groupedRules = [];
	RULES_TYPES.forEach((type) => {
		groupedRules.push(
			{
				id: type.id,
				name: type.name,
				items: getRulesByType(rules, type.id),
			}
		);
	});
	return groupedRules;
}

export default function SearchRules(
	{
		typeID,
		autocomplete,
		handleChange,
		placeholder,
		value,
		variant,
	}
) {
	let classes = VARIANTS["search"]?.replace(/\s+/g, " ");
	const [showResultList, setShowResultsList] = useState(false);
	const [rules, setRules] = useState(DEFAULT_RULES);
	const ref = useRef(null);
	const [query, setQuery] = useState("");
	const filteredRules = getFilteredRules({
		rules,
		typeID,
		query
	});
	const groupedRules = transformRulesByType(filteredRules);

	const ResultsList = () => {
		return (
			<ListBox>
				{groupedRules.map((group, index) => (
					<SearchRuleGroupItem
						group={group}
						query={query}
						key={index}/>
				))}
			</ListBox>
		);
	};

	const handleClick = () => {
		setShowResultsList(true);
	};


	useEffect(() => {
		const handleClickOutside = (event) => {
			if (ref.current && !ref.current.contains(event.target)) {
				setShowResultsList(false);
			}
		};

		const handleKeyup = (event) => {
			switch (event.key) {
				case "ArrowUp":
					event.preventDefault();
					prevOption();

					break;

				case "ArrowDown":
					event.preventDefault()
					nextOption();

					break;

				case "Escape":
					event.preventDefault();
					setShowResultsList(false);
					break;

				// Assume all other cases are printable characters or manipulation keys
				default:
					// Only alphanumeric keys, Backspace, Delete, Tab, and some special characters (_, +, and -)
					if (/^[\b\t.a-zA-Z0-9_+-]*$/.test(String.fromCharCode(event.keyCode))) {
						setQuery(event.target.value);
					}

					break;
			}
		};

		const nextOption = () => {
			const searchDropdownArray = Array.from(
				ref.current.querySelectorAll("a")
			);

			// Get position of where current selected item is
			const currentOption = searchDropdownArray.findIndex(
				(option) => option.getAttribute("aria-selected") === "true"
			);

			if (currentOption < 0) {
				searchDropdownArray[0].setAttribute('aria-selected', 'true')
				searchDropdownArray[0].focus()
			} else {
				searchDropdownArray[currentOption].setAttribute('aria-selected', 'false')
				searchDropdownArray[currentOption + 1].setAttribute('aria-selected', 'true')
				searchDropdownArray[currentOption + 1].focus()
			}
		}

		const prevOption = () => {
			const searchDropdownArray = Array.from(
				ref.current.querySelectorAll("a")
			);

			// Get position of where current selected item is
			let currentOption = searchDropdownArray.findIndex(
				(option) => option.getAttribute("aria-selected") === "true"
			);

			if (currentOption < 0) {
				currentOption = searchDropdownArray?.length;
				searchDropdownArray[currentOption - 1].setAttribute('aria-selected', 'true')
				searchDropdownArray[currentOption - 1].focus()
			} else {
				searchDropdownArray[currentOption].setAttribute('aria-selected', 'false')
				searchDropdownArray[currentOption - 1].setAttribute('aria-selected', 'true')
				searchDropdownArray[currentOption - 1].focus()
			}
		}

		window.addEventListener("DOMContentLoaded", () => {
			ref.current.addEventListener("keyup", handleKeyup);
			document.addEventListener("click", handleClickOutside);
			return () => {
				document.removeEventListener("click", handleClickOutside, true);
			};
		})
	});

	return (
		<div
			className="dxp-relative search-rules"
			ref={ref}>
			<TbSearch
				size="20"
				className="dxp-absolute dxp-left-4 dxp-pointer-events-none dxp-text-purple dxp-top-4 dxp-z-30"
			/>
			<input
				aria-expanded="false"
				className={classes}
				onChange={(event) => setQuery(event.target.value)}
				placeholder={placeholder}
				role="combobox"
				type="search"
				value={value}
				onFocus={handleClick}
			/>
			{showResultList &&
				<div
					className="dxp-relative">
					<ResultsList/>
				</div>
			}
		</div>
	);
}

const VARIANTS = {
	search: `
		!dxp-border-gray-200
		!dxp-leading-5
		!dxp-m-0
		!dxp-pl-12
		!dxp-pr-3.5
		!dxp-py-4
		!dxp-rounded-bl-none
		!dxp-rounded-bt-lg
		!dxp-rounded-s-none
		!dxp-text-sm
		!dxp-w-full
		dxp-relative
		dxp-z-20
		focus-visible:!dxp-shadow-none
		focus-visible:!dxp-border
		focus-visible:!dxp-border-1
		focus-visible:!dxp-border-purple
		focus:!dxp-shadow-none
		focus:!dxp-border
		focus:!dxp-border-1
		focus:!dxp-border-purple
		!placeholder:dxp-text-sm
		!placeholder:dxp-text-tundora
	`,
};
