import React, {MutableRefObject, useCallback, useMemo, useRef, useState} from "react";
import {classes} from "../../Utils";
import {Check} from "@phosphor-icons/react";

/**
 * Suggestions preselected options navigation configuration.
 */
export interface SuggestionsNavigation
{
	/**
	 * Return true if the preselected options navigation is initialized.
	 */
	initialized(): boolean;

	/**
	 * Preselect the next option in the suggestions.
	 */
	next(): void;

	/**
	 * Preselect the previous option in the suggestions.
	 */
	previous(): void;

	/**
	 * Select the currently preselected option.
	 */
	select(): void;
}

/**
 * Hook to get the preselected options navigation reference.
 */
export function useSuggestionsNavigation(): MutableRefObject<SuggestionsNavigation>
{
	return useRef({
		initialized(): boolean
		{
			return false;
		},

		next(): void
		{
		},

		previous(): void
		{
		},

		select(): void
		{
		},
	} as SuggestionsNavigation);
}

export function OptionsSuggestions<OptionKey extends keyof any, Option>({options, onSelected, selectedOptions, renderOption, navigator}: {
	/**
	 * Options to suggest.
	 */
	options: Record<OptionKey, Option>;

	/**
	 * Called when an option is selected.
	 * @param key
	 * @param option
	 */
	onSelected: (key: OptionKey, option: Option) => void;

	/**
	 * Already selected options that will be shown differently.
	 */
	selectedOptions?: Record<OptionKey, Option>;

	/**
	 * Render an option.
	 * @param option The option to render.
	 */
	renderOption?: (option: Option) => React.ReactNode;

	/**
	 * A reference to a preselected suggestions options navigation object.
	 */
	navigator?: MutableRefObject<SuggestionsNavigation>;
}): React.ReactNode
{
	// Initialize default option render function.
	const defaultRenderOption = useCallback((option: Option) => (String(option)), []);

	const optionsArray = useMemo(() => (Object.entries(options) as [OptionKey, Option][]), [options]);

	const [preselectedOptionIndex, setPreselectedOptionIndex] = useState<number>(0);

	const navigation = useMemo(() => ({
		initialized(): boolean
		{
			return true;
		},

		next(): void
		{
			// Preselect the next option in the options array, or the first one if it's the last element.
			setPreselectedOptionIndex((optionsArray.length == (preselectedOptionIndex + 1)) ? 0 : (preselectedOptionIndex + 1));
		},

		previous(): void
		{
			// Preselect the previous option in the options array, or the last one if it's the first element.
			setPreselectedOptionIndex(((preselectedOptionIndex - 1) < 0) ? (optionsArray.length - 1) : (preselectedOptionIndex - 1));
		},

		select(): void
		{
			// Get the currently preselected option.
			const [optionKey, option] = optionsArray[preselectedOptionIndex];
			// Select the currently preselected option.
			onSelected(optionKey, option);
		},
	}), [optionsArray, preselectedOptionIndex, setPreselectedOptionIndex]);

	if (navigator)
		// If navigator reference is set, assigning it.
		navigator.current = navigation;

	return optionsArray.map(([key, option], index) => (
		<a key={String(key)} className={classes("suggestion", preselectedOptionIndex == index ? "preselected" : null, selectedOptions?.[key] ? "selected" : null)}
		   onClick={() => { onSelected(key, option); }}>
			{(renderOption ?? defaultRenderOption)(option)}

			<span className={"selected"}><Check weight={"bold"} /></span>
		</a>
	));
}
