import { useCallback, useEffect, useState } from "react";

import { areEquals } from "@ax/helpers";
import type { ICheck } from "@ax/types";

const useBulkSelection = (itemIDs: any[]) => {
	const selectedItemsInitialState: IBulkSelectedItems = {
		all: [],
		notPublished: [],
		published: [],
		drafts: [],
	};

	const [items, setItems] = useState<any[]>([]);
	const [selectedItems, setSelectedItems] = useState(selectedItemsInitialState);
	const [isMultiCheck, setIsMultiCheck] = useState(false);
	const [checkState, setCheckState] = useState<Record<string, boolean>>({
		isAllSelected: false,
		indeterminate: false,
		hoverCheck: false,
	});

	// biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
	useEffect(() => {
		const isAllSelected = areAllItemsSelected(items);
		const indeterminate = !isAllSelected && areItemsSelected(items);

		setCheckState({ isAllSelected, indeterminate, hoverCheck: false });
	}, [selectedItems, items]);

	// biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
	useEffect(() => {
		if (items.length !== itemIDs.length) {
			setItems(itemIDs);
		}
	}, [itemIDs]);

	const onWindowKeyDown = useCallback((e: KeyboardEvent) => {
		if (e.defaultPrevented) {
			return;
		}

		if (e.key === "Shift") {
			setIsMultiCheck(true);
		}
	}, []);

	const onWindowKeyUp = useCallback((e: KeyboardEvent) => {
		if (e.defaultPrevented) {
			return;
		}

		if (e.key === "Shift") {
			setIsMultiCheck(false);
		}
	}, []);

	useEffect(() => {
		window.addEventListener("keydown", onWindowKeyDown);
		window.addEventListener("keyup", onWindowKeyUp);
		return () => {
			window.removeEventListener("keydown", onWindowKeyDown);
			window.removeEventListener("keyup", onWindowKeyUp);
		};
	}, [onWindowKeyDown, onWindowKeyUp]);

	const isSelected = (value: any) => selectedItems.all.includes(value);

	const areAllItemsSelected = (itemsAvailable: any) =>
		selectedItems.all.length > 0 && areEquals(itemsAvailable, selectedItems.all);

	const areItemsSelected = (itemsAvailable: any) =>
		!!selectedItems.all.find((element: any) => itemsAvailable.includes(element));

	const resetBulkSelection = () => setSelectedItems(selectedItemsInitialState);

	const multiSelectTo = (value: any) => {
		const indexOfNew = items.indexOf(value);
		const lastSelected = selectedItems.all[selectedItems.all.length - 1];
		const indexOfLast = items.indexOf(lastSelected);

		const isSelectingForwards = indexOfNew > indexOfLast;
		const start = isSelectingForwards ? indexOfLast : indexOfNew;
		const end = isSelectingForwards ? indexOfNew : indexOfLast;

		const inBetween = items.slice(start, end + 1);

		const toAdd = inBetween.filter((id) => !selectedItems.all.includes(id));

		const sorted = isSelectingForwards ? toAdd : [...toAdd].reverse();
		const combined = [...selectedItems.all, ...sorted];
		return combined;
	};

	const addToBulkSelection = (item: ICheck, callback?: any) => {
		const { value, isChecked } = item;

		let bulkSelection: any = [...selectedItems.all];

		if (isChecked && isMultiCheck) {
			bulkSelection = multiSelectTo(value);
		} else if (isChecked) {
			bulkSelection.push(value);
		} else {
			bulkSelection = bulkSelection.filter((id: any) => id !== value);
		}

		if (callback) {
			const filteredItems = callback(bulkSelection);
			setSelectedItems(filteredItems);
		} else {
			setSelectedItems({ ...selectedItems, all: bulkSelection });
		}
	};

	const selectAllItems = (callback?: any) => {
		const bulkSelection: any = [...selectedItems.all];
		itemIDs.forEach((item: any) => {
			!bulkSelection.includes(item) && bulkSelection.push(item);

			if (callback) {
				const filteredItems = callback(bulkSelection);
				setSelectedItems(filteredItems);
			} else {
				setSelectedItems({ ...selectedItems, all: bulkSelection });
			}
		});
	};

	const setHoverCheck = (value: boolean) => setCheckState((state) => ({ ...state, hoverCheck: value }));

	return {
		resetBulkSelection,
		selectedItems,
		isSelected,
		areItemsSelected,
		checkState,
		addToBulkSelection,
		selectAllItems,
		setHoverCheck,
	};
};

export interface IBulkSelectedItems {
	all: any[];
	notPublished: any[];
	published: any[];
	drafts: any[];
}

export { useBulkSelection };
