import { ChevronDown, Info, LucideProps, Search } from "lucide-react";
import React from "react";
import { twMerge } from "tailwind-merge";
import Dropdown from "../elements/Dropdown";
import Stack from "../layout/Stack";
import {
    TWUISelectOptionObject,
    TWUISelectProps,
    TWUISelectValidityObject,
} from "./Select";
import Border from "../elements/Border";
import Input from "./Input";
import Paper from "../elements/Paper";
import Button from "../layout/Button";
import Divider from "../layout/Divider";

/**
 * # Search Select Element
 * @className twui-search-select-wrapper
 * @className twui-search-select
 * @className twui-search-select-dropdown-icon
 */
export default function SearchSelect<
    KeyType extends string,
    T extends { [k: string]: any } = { [k: string]: any }
>({
    label,
    options,
    componentRef,
    labelProps,
    wrapperProps,
    showLabel,
    iconProps,
    changeHandler,
    info,
    validateValueFn,
    wrapperWrapperProps,
    dispatchState,
    ...props
}: TWUISelectProps<KeyType, T>) {
    const [validity, setValidity] = React.useState<TWUISelectValidityObject>({
        isValid: true,
    });

    const selectRef = componentRef || React.useRef<HTMLSelectElement>(null);

    const [currentOptions, setCurrentOptions] =
        React.useState<TWUISelectOptionObject<KeyType, T>[]>(options);

    const defaultOption = options.find((opt) => opt.default) || options[0];

    const [value, setValue] = React.useState<
        TWUISelectOptionObject<KeyType, T>
    >({
        value: defaultOption.value,
        data: defaultOption.data,
    });

    const [inputValue, setInputValue] = React.useState<string>(
        defaultOption.value
    );
    const [selectIndex, setSelectIndex] = React.useState<number | undefined>();

    const [open, setOpen] = React.useState<boolean>(false);
    const isFocusedRef = React.useRef<boolean>(false);
    const inputRef = React.useRef<HTMLInputElement>(null);
    const contentWrapperRef = React.useRef<HTMLDivElement>(null);

    let focusTimeout: any;
    let keyDownInterval: any;
    const FOCUS_TIMEOUT = 200;

    React.useEffect(() => {
        setTimeout(() => {
            requestAnimationFrame(() => {
                const currentSelectValue = selectRef.current?.value;

                if (currentSelectValue && validateValueFn) {
                    validateValueFn(currentSelectValue).then((res) => {
                        setValidity(res);
                    });
                }
            });
        }, 200);
    }, []);

    React.useEffect(() => {
        if (!open) {
            setCurrentOptions(options);
        }
    }, [open]);

    React.useEffect(() => {
        dispatchState?.(value.data);
        setInputValue(value.value);
        clearTimeout(focusTimeout);
        setOpen(false);
        changeHandler?.(value.value);
        setSelectIndex(undefined);
    }, [value]);

    const handleArrowUpScrollAdjust = React.useCallback(() => {
        if (contentWrapperRef.current) {
            const targetOption = contentWrapperRef.current.querySelector(
                ".twui-select-target-option"
            ) as HTMLButtonElement;

            if (targetOption) {
                contentWrapperRef.current.scrollTop =
                    targetOption.offsetTop - 100;
            }
        }
    }, []);

    const handleArrowDownScrollAdjust = React.useCallback(() => {
        if (contentWrapperRef.current) {
            const targetOption = contentWrapperRef.current.querySelector(
                ".twui-select-target-option"
            ) as HTMLButtonElement;

            if (targetOption) {
                contentWrapperRef.current.scrollTop =
                    targetOption.offsetTop -
                    (contentWrapperRef.current.offsetHeight - 100);
            }
        }
    }, []);

    React.useEffect(() => {
        if (!selectIndex) return;
    }, [selectIndex]);

    const handleKey = (e: React.KeyboardEvent) => {
        if (e.key === "Enter") {
            if (selectIndex !== undefined) {
                setValue(currentOptions[selectIndex]);
                if (inputRef.current) {
                    inputRef.current.blur();
                }
            }
        } else if (e.key === "ArrowUp") {
            if (selectIndex == undefined) {
                setSelectIndex(currentOptions.length - 1);
            } else if (selectIndex === 0) {
                setSelectIndex(0);
            } else {
                setSelectIndex(selectIndex - 1);
            }
            handleArrowUpScrollAdjust();
        } else if (e.key === "ArrowDown") {
            if (selectIndex == undefined) {
                setSelectIndex(0);
            } else if (selectIndex === currentOptions.length - 1) {
                setSelectIndex(currentOptions.length - 1);
            } else {
                setSelectIndex(selectIndex + 1);
            }
            handleArrowDownScrollAdjust();
        }
    };

    const handleKeyUp = (e: React.KeyboardEvent) => {
        e.preventDefault();

        clearInterval(keyDownInterval);

        handleKey(e);
    };

    // const handleKeyDown = (e: React.KeyboardEvent) => {
    //     e.preventDefault();

    //     keyDownInterval = setInterval(() => {
    //         handleKey(e);
    //     }, 100);
    // };

    return (
        <Stack
            {...wrapperWrapperProps}
            className={twMerge(
                "gap-1 w-full",
                "twui-search-select-wrapper",
                wrapperWrapperProps?.className
            )}
            onKeyUp={handleKeyUp}
            // onKeyDown={handleKeyDown}
        >
            <Dropdown
                disableClickActions
                target={
                    <Input
                        type="text"
                        placeholder="Search"
                        value={inputValue}
                        prefix={(<Search size={18} />) as any}
                        suffix={(<ChevronDown size={20} />) as any}
                        suffixProps={{
                            onClick: (e) => {
                                e.preventDefault();
                                clearTimeout(focusTimeout);
                                setOpen(!open);
                            },
                            className: "pointer-events-auto opacity-100",
                        }}
                        changeHandler={(value) => {
                            if (!isFocusedRef.current) return;
                            if (!open) setOpen(true);
                        }}
                        onFocus={() => {
                            clearTimeout(focusTimeout);
                            isFocusedRef.current = true;
                            setOpen(true);
                        }}
                        onBlur={() => {
                            focusTimeout = setTimeout(() => {
                                isFocusedRef.current = false;
                                setOpen(false);
                            }, FOCUS_TIMEOUT);
                        }}
                        onChange={(e) => {
                            if (!open) setOpen(true);
                            setInputValue(e.target.value);
                            const updatedOptions = options.filter((option) =>
                                option.value
                                    .toLowerCase()
                                    .match(
                                        new RegExp(
                                            `${e.target.value.toLowerCase()}`
                                        )
                                    )
                            );

                            if (updatedOptions?.[0]) {
                                setCurrentOptions(updatedOptions);
                            } else {
                                setCurrentOptions(options);
                            }

                            setSelectIndex(undefined);
                        }}
                        componentRef={inputRef}
                    />
                }
                targetWrapperProps={{ className: "w-full" }}
                contentWrapperProps={{ className: "w-full" }}
                className="w-full"
                externalOpen={open}
            >
                <Paper
                    className={twMerge(
                        "gap-0 p-0 w-full max-h-[40vh] overflow-y-auto"
                    )}
                    componentRef={contentWrapperRef}
                >
                    <Stack className="w-full items-start gap-0">
                        {currentOptions.map((_o, index) => {
                            const isTargetOption = index === selectIndex;
                            const targetOptionClasses = twMerge(
                                "bg-background-dark dark:bg-background-light text-foreground-dark dark:text-foreground-light",
                                "twui-select-target-option"
                            );

                            return (
                                <React.Fragment key={index}>
                                    <Button
                                        title={_o.title || "Option"}
                                        variant="ghost"
                                        onClick={(e) => {
                                            e.preventDefault();
                                            setValue(_o);
                                            setOpen(false);
                                        }}
                                        className={twMerge(
                                            "w-full text-foreground-light dark:text-foreground-dark",
                                            "hover:bg-gray/20 dark:hover:bg-gray-dark/20",
                                            isTargetOption
                                                ? targetOptionClasses
                                                : ""
                                        )}
                                    >
                                        {_o.value}
                                    </Button>
                                    <Divider />
                                </React.Fragment>
                            );
                        })}
                    </Stack>
                </Paper>
            </Dropdown>
        </Stack>
    );
}
