import React, {
    DetailedHTMLProps,
    HTMLAttributes,
    PropsWithChildren,
} from "react";
import { twMerge } from "tailwind-merge";

export const TWUIDropdownContentPositions = [
    "left",
    "right",
    "center",
] as const;

export type TWUI_DROPDOWN_PROPS = PropsWithChildren &
    DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement> & {
        target: React.ReactNode;
        contentWrapperProps?: DetailedHTMLProps<
            HTMLAttributes<HTMLDivElement>,
            HTMLDivElement
        >;
        targetWrapperProps?: DetailedHTMLProps<
            HTMLAttributes<HTMLDivElement>,
            HTMLDivElement
        >;
        debounce?: number;
        hoverOpen?: boolean;
        above?: boolean;
        position?: (typeof TWUIDropdownContentPositions)[number];
        topOffset?: number;
        externalSetOpen?: React.Dispatch<React.SetStateAction<boolean>>;
    };

let timeout: any;

/**
 * # Toggle Component
 * @className_wrapper twui-dropdown-wrapper
 * @className_wrapper twui-dropdown-target
 * @className_wrapper twui-dropdown-content
 *
 * @note use the class `cancel-link` to prevent popup open on click
 */
export default function Dropdown({
    contentWrapperProps,
    targetWrapperProps,
    hoverOpen,
    above,
    debounce = 500,
    target,
    position = "center",
    topOffset,
    externalSetOpen,
    ...props
}: TWUI_DROPDOWN_PROPS) {
    const [open, setOpen] = React.useState(false);

    const dropdownRef = React.useRef<HTMLDivElement>(null);

    const handleClickOutside = React.useCallback((e: MouseEvent) => {
        const targetEl = e.target as HTMLElement;
        const closestWrapper = targetEl.closest(".twui-dropdown-wrapper");

        if (!closestWrapper) {
            externalSetOpen?.(false);
            return setOpen(false);
        }
        if (closestWrapper && closestWrapper !== dropdownRef.current) {
            externalSetOpen?.(false);
            return setOpen(false);
        }
    }, []);

    React.useEffect(() => {
        document.addEventListener("click", handleClickOutside);
        return () => {
            document.removeEventListener("click", handleClickOutside);
        };
    }, []);

    return (
        <div
            {...props}
            className={twMerge(
                "flex flex-col items-center relative",
                "twui-dropdown-wrapper",
                props.className
            )}
            onMouseEnter={() => {
                if (!hoverOpen) return;
                window.clearTimeout(timeout);
                externalSetOpen?.(true);
                setOpen(true);
            }}
            onMouseLeave={() => {
                if (!hoverOpen) return;
                timeout = setTimeout(() => {
                    externalSetOpen?.(false);
                    setOpen(false);
                }, debounce);
            }}
            onBlur={() => {
                window.clearTimeout(timeout);
            }}
            ref={dropdownRef}
        >
            <div
                onClick={(e) => {
                    const targetEl = e.target as HTMLElement | null;
                    if (targetEl?.closest?.(".cancel-link")) return;
                    externalSetOpen?.(!open);
                    setOpen(!open);
                }}
                className={twMerge(
                    "cursor-pointer",
                    "twui-dropdown-target",
                    targetWrapperProps?.className
                )}
            >
                {target}
            </div>

            <div
                {...contentWrapperProps}
                className={twMerge(
                    "absolute z-10",
                    position == "left"
                        ? "left-0"
                        : position == "right"
                        ? "right-0"
                        : "",
                    above ? "-translate-y-[120%]" : "",
                    open ? "flex" : "hidden",
                    "twui-dropdown-content",
                    contentWrapperProps?.className
                )}
                onMouseEnter={() => {
                    if (!hoverOpen) return;
                    window.clearTimeout(timeout);
                }}
                onBlur={() => {
                    if (!hoverOpen) return;
                    window.clearTimeout(timeout);
                }}
                style={{
                    top: `calc(100% + ${topOffset || 0}px)`,
                    ...contentWrapperProps?.style,
                }}
            >
                {props.children}
            </div>
        </div>
    );
}
