import {createPortal, useEffect, useId, useRef, useState} from "@wordpress/element";
import {__} from "@wordpress/i18n";
import useClickOutside from "../../../../components/hooks/use-click-outside";
import {DROPDOWN_MENU} from "./_data";

export default function Dropdown({
                                     defaultValue,
                                     defaultIcon,
                                     iconClassName,
                                     value,
                                     setValue,
                                     options,
                                     searchable,
                                     search,
                                     addNew,
                                     addNewLabel,
                                     loadMore,
                                     pagination,
                                     refreshOptions,
                                     refreshOptionsLabel,
                                     filters,
                                     placeholder,
                                     className,
                                     childClassName,
                                     validate,
                                     errorMessage,
                                     children,
                                     hasDeselect,
                                     deselectOptionLabel,
                                     disabled,
                                     dropdownRef,
                                     onInteraction,
                                     onOptionDelete,
                                     onOptionEdit
                                 }) {
    const dropdownLabelRef = useRef(null);
    const dropdownInternalRef = useRef();
    const dropdownMenuRef = useRef(null);
    const [isOpened, setIsOpened] = useState(false);
    const [isSearching, setIsSearching] = useState(false);
    const [searchFocused, setSearchFocused] = useState(false);
    const [inputValue, setInputValue] = useState('');
    const [valueIcon, setValueIcon] = useState(false);
    const [valueIconClassName, setValueIconClassName] = useState(false);
    const [searchTerm, setSearchTerm] = useState('');
    const [filteredOptions, setFilteredOptions] = useState(options);
    const [showLoadMore, setShowLoadMore] = useState(false);
    const [loadingMore, setLoadingMore] = useState(false);
    const [refreshingOptions, setRefreshingOptions] = useState(false);
    const searchDebounce = useRef(null);
    const [menuPosition, setMenuPosition] = useState({
        top: 0,
        left: 0,
        width: 0,
    });

    const fieldId = useId();

    const setDropdownRef = (element) => {
        // Set internal ref
        dropdownInternalRef.current = element;

        // Set forwarded ref
        if (dropdownRef) {
            dropdownRef.current = element;
        }
    }

    useClickOutside([dropdownLabelRef, dropdownInternalRef, dropdownMenuRef], () => {
        setIsOpened(false);
        setSearchTerm('');
    });

    useEffect(() => {
        // Handle resize event
        const handleResize = () => {
            if (isOpened) {
                setupDropdownMenuPosition();
            }
        };

        // Events
        window.addEventListener('resize', handleResize);
        window.addEventListener('scroll', setupDropdownMenuPosition, true);

        return () => {
            window.removeEventListener('resize', handleResize);
            window.removeEventListener('scroll', setupDropdownMenuPosition, true);
        }
    }, [isOpened]);

    useEffect(() => {
        if (isOpened) {
            setupDropdownMenuPosition();
        }
    }, [isOpened]);

    useEffect(() => {
        if (typeof pagination?.set === 'function') {
            pagination.set(prevState => ({...prevState, page: 1}));
        }
    }, [filters, searchTerm]);

    useEffect(() => {
        // Update input value
        const selectedOption = options?.length ? options.find(option => isSelectedOption(option)) : null;
        setInputValue(selectedOption?.label || (defaultValue?.label || defaultValue?.title || defaultValue?.name) || '')
        setValueIcon(selectedOption?.icon || defaultValue?.icon || false);
        setValueIconClassName(selectedOption?.iconClassName || defaultValue?.iconClassName || false);
    }, [value, options, defaultValue]);

    useEffect(() => {
        if (searchTerm?.length && options?.length && typeof search !== 'function') {
            setFilteredOptions(options.filter(option => option.label.toLowerCase().includes(searchTerm.toLowerCase())));
        } else {
            setFilteredOptions(options);
        }
    }, [searchTerm, options]);

    useEffect(() => {
        if (pagination?.get) {
            setShowLoadMore(typeof loadMore === 'function' && (pagination.get.page * pagination.get.perPage < pagination.get.total))
        } else {
            setShowLoadMore(false);
        }
    }, [options, pagination?.get.page, pagination?.get.perPage, pagination?.get.total]);

    /**
     * Setup dropdown menu position
     */
    const setupDropdownMenuPosition = () => {
        const dropdown = dropdownInternalRef.current;
        if (dropdown && dropdownMenuRef.current) {
            const rect = dropdown.getBoundingClientRect();
            const menuRect = dropdownMenuRef.current.getBoundingClientRect();
            setMenuPosition({
                top: rect.bottom - (rect.bottom + menuRect.height > window.innerHeight ? rect.bottom + menuRect.height - window.innerHeight + 4 : 0),
                left: rect.left,
                width: rect.width - (rect.left + rect.width > window.innerWidth ? rect.left + rect.width - window.innerWidth + 4 : 0)
            });
        }
    }

    /**
     * On dropdown click
     */
    const onClick = () => {
        if (!disabled) {
            // Call onInteraction callback if provided
            if (typeof onInteraction === 'function') {
                onInteraction();
            }
            setIsOpened(!isOpened);
        }
    }

    /**
     * Is selected option
     *
     * @param {object} option Option
     * @return {boolean}
     */
    const isSelectedOption = (option) => {
        if ((typeof option.value === 'string' || typeof option.value === 'number') &&
            (typeof value === 'string' || typeof value === 'number') &&
            !isNaN(Number(option.value)) &&
            !isNaN(Number(value))) {
            // Numeric
            return +option.value === +value;
        } else {
            return option.value === value;
        }
    }

    /**
     * Search an option(s)
     *
     * @param {Event} e Event
     */
    const searching = (e) => {
        if (disabled) {
            return;
        }
        setSearchTerm(e.target.value);
        // Keep opened if searching
        if (e.target.value) {
            setIsOpened(true);
        }
        // Search for
        if (typeof search === 'function') {
            // Clear any previously set debounce timer
            clearTimeout(searchDebounce.current);
            // Set a new debounce timer to delay handling the title change
            searchDebounce.current = setTimeout(async () => {
                setIsSearching(true);
                const params = {
                    page: 1,
                    perPage: pagination.get.perPage,
                };
                if (e.target.value.trim()) {
                    params.search = e.target.value;
                }
                await search(params);
                setIsSearching(false);
            }, 400);
        }
    }

    /**
     * Load more options
     */
    const loadMoreOptions = async () => {
        if (disabled) {
            return;
        }
        setLoadingMore(true);
        const params = {
            page: pagination.get.page + 1,
            perPage: pagination.get.perPage,
        };
        // Add search param
        if (searchTerm.trim()) {
            params.search = searchTerm;
        }
        await loadMore(params);
        setLoadingMore(false);
    }

    /**
     * Add new option click callback
     */
    const addNewCallback = () => {
        if (disabled || typeof addNew !== 'function') {
            return;
        }
        setIsOpened(false);
        addNew();
    }

    /**
     * Refresh options
     *
     * @return {Promise<void>}
     */
    const handleRefreshOptions = async () => {
        if (disabled) {
            return;
        }
        setRefreshingOptions(true);
        await refreshOptions();
        setRefreshingOptions(false);
    }

    /**
     * Update value
     *
     * @param {object} option Option data
     */
    const updateValue = (option) => {
        if (disabled) {
            return;
        }
        if (!option.disabled) {
            const newValue = option.extra || option.value;
            setValue(newValue);
            setValueIcon(option.icon || false);
            setValueIconClassName(option.iconClassName || false);
            setIsOpened(false);
            setSearchTerm('');
            // Validate
            if (typeof validate === 'function') {
                validate(newValue);
            }
        }
    }

    /**
     * Handle option delete
     *
     * @param {Event} e Event
     * @param {object} option Option data
     */
    const handleOptionDelete = async (e, option) => {
        e.stopPropagation();
        if (disabled || option.disabled || typeof onOptionDelete !== 'function') {
            return;
        }
        setIsOpened(false);
        await onOptionDelete(option);
    }

    /**
     * Handle option edit
     *
     * @param {Event} e Event
     * @param {object} option Option data
     */
    const handleOptionEdit = (e, option) => {
        e.stopPropagation();
        if (disabled || option.disabled || typeof onOptionEdit !== 'function') {
            return;
        }
        setIsOpened(false);
        onOptionEdit(option);
    }

    /**
     * Render option
     *
     * @param {object} option Option data
     * @param {number} i Option index
     * @return {JSX.Element}
     */
    const renderOption = (option, i) => {
        const hasActions = typeof onOptionDelete === 'function' || typeof onOptionEdit === 'function';
        return (
            <div key={i}
                 className={`lbaic-settings-dropdown-menu-item${isSelectedOption(option) ? ' active' : ''}${option.disabled || disabled ? ' disabled' : ''}${hasActions ? ' lbaic-settings-dropdown-menu-item-has-action' : ''}`}
                 onClick={() => updateValue(option)}>
                {(!!option.icon || defaultIcon) &&
                    <svg className={`lbaic-settings-dropdown-menu-item-i${option.iconClassName ? ' ' + option.iconClassName : ''}${iconClassName ? ' ' + iconClassName : ''}`}
                         xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24'>
                        <use href={`#lbaic-settings-${option.icon || defaultIcon}`}/>
                    </svg>}
                <span className='lbaic-settings-dropdown-menu-item-label'>{option.label}</span>
                {!!option.badge && option.badge}
                {hasActions &&
                    <div className='lbaic-settings-dropdown-menu-item-actions'>
                        {typeof onOptionEdit === 'function' &&
                            <button
                                type='button'
                                className='lbaic-settings-dropdown-menu-item-action'
                                onClick={(e) => handleOptionEdit(e, option)}
                                disabled={disabled || option.disabled}>
                                <svg className='lbaic-settings-dropdown-menu-item-action-i' xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24'>
                                    <use href='#lbaic-settings-edit'/>
                                </svg>
                            </button>}
                        {typeof onOptionDelete === 'function' &&
                            <button
                                type='button'
                                className='lbaic-settings-dropdown-menu-item-action'
                                onClick={(e) => handleOptionDelete(e, option)}
                                disabled={disabled || option.disabled}>
                                <svg className='lbaic-settings-dropdown-menu-item-action-i' xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24'>
                                    <use href='#lbaic-settings-delete'/>
                                </svg>
                            </button>}
                    </div>}
            </div>
        );
    }

    return (
        <div
            className={`lbaic-settings-dropdown-holder${className ? ' ' + className : ''}${children ? ' lbaic-settings-dropdown-has-children' : ''}${isOpened ? ' opened' : ''}${disabled ? ' lbaic-settings-dropdown-disabled' : ''}${errorMessage ? ' lbaic-settings-dropdown-error' : ''}`}
        >
            {Boolean(placeholder) && (
                <div
                    ref={dropdownLabelRef}
                    className='lbaic-settings-dropdown-placeholder'
                >
                    <label htmlFor={fieldId} className='lbaic-settings-dropdown-placeholder-in'>{placeholder}</label>
                </div>
            )}
            <div
                ref={setDropdownRef}
                className={`lbaic-settings-dropdown lbaic-settings-dropdown-h-42${childClassName ? ' ' + childClassName : ''}${searchTerm || inputValue ? ' lbaic-settings-dropdown-has-value' : ''}${searchFocused ? ' lbaic-settings-dropdown-input-focused' : ''}`}
                onClick={onClick}
            >
                {!!children ? <div className='lbaic-settings-dropdown-children'>{children}</div> : null}
                <div className='lbaic-settings-dropdown-in'>
                    {(valueIcon || defaultIcon) &&
                        <svg className={`lbaic-settings-dropdown-icon${valueIconClassName ? ' ' + valueIconClassName : ''}${iconClassName ? ' ' + iconClassName : ''}`}
                             xmlns='http://www.w3.org/2000/svg' fill='none'
                             viewBox='0 0 24 24'>
                            <use href={`#lbaic-settings-${valueIcon || defaultIcon}`}/>
                        </svg>}
                    <input
                        id={fieldId}
                        className='lbaic-settings-dropdown-input' type='text'
                        placeholder={isOpened && searchable ? inputValue : ''}
                        value={isOpened && searchable ? searchTerm : inputValue}
                        onChange={searching} readOnly={!searchable || disabled}
                        onFocus={() => !disabled && setSearchFocused(searchable)}
                        onBlur={() => !disabled && setSearchFocused(false)}
                        autoComplete="off"
                    />
                </div>
                <div className='lbaic-settings-dropdown-arrow'>
                    <svg className='lbaic-settings-dropdown-arrow-in' xmlns='http://www.w3.org/2000/svg' fill='none'
                         viewBox='0 0 24 24'>
                        <use href='#lbaic-settings-arrow'/>
                    </svg>
                </div>
            </div>
            {isOpened && createPortal(<div ref={dropdownMenuRef} className='lbaic-settings-dropdown-menu'
                                           style={{
                                               '--lbaic-settings-dropdown-holder-top': menuPosition.top + (typeof menuPosition.top === 'number' ? 'px' : ''),
                                               '--lbaic-settings-dropdown-holder-left': menuPosition.left + (typeof menuPosition.left === 'number' ? 'px' : ''),
                                               '--lbaic-settings-dropdown-holder-width': menuPosition.width + (typeof menuPosition.width === 'number' ? 'px' : ''),
                                               '--lbaic-settings-dropdown-height': DROPDOWN_MENU.maxHeight + 'px',
                                           }}>
                <div className='lbaic-settings-dropdown-menu-in lbaic-settings-scroll-y lbaic-settings-scroll-color'>
                    {hasDeselect &&
                        <div className='lbaic-settings-dropdown-menu-action'>
                            <button
                                type='button'
                                className={`lbaic-settings-button-reset lbaic-settings-button lbaic-settings-button-primary lbaic-settings-button-center lbaic-settings-dropdown-menu-action-in${disabled ? ' lbaic-settings-button-disabled' : ''}`}
                                onClick={() => {
                                    if (disabled) {
                                        return;
                                    }
                                    setValue('');
                                    setIsOpened(false);
                                }}>
                                <span
                                    className='lbaic-settings-dropdown-menu-action-label'>{deselectOptionLabel || placeholder}</span>
                            </button>
                        </div>}
                    {!!filteredOptions?.length && filteredOptions.map(renderOption)}
                    {!filteredOptions?.length &&
                        <div className='lbaic-settings-dropdown-menu-item disabled'>
                            <span
                                className='lbaic-settings-dropdown-menu-item-label'>{__("No options available.", 'limb-chatbot')}</span>
                        </div>}
                    {showLoadMore &&
                        <div className='lbaic-settings-dropdown-menu-action'>
                            <button
                                type='button'
                                className={`lbaic-settings-button-reset lbaic-settings-button lbaic-settings-button-primary lbaic-settings-button-center lbaic-settings-dropdown-menu-action-in${loadingMore || disabled ? ' lbaic-settings-button-disabled' : ''}`}
                                onClick={loadMoreOptions}>
                                {loadingMore &&
                                    <svg
                                        className='lbaic-settings-button-i lbaic-settings-loading-circle lbaic-settings-button-loading-circle'
                                        xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24'>
                                        <use href='#lbaic-settings-circle'/>
                                    </svg>}
                                <span
                                    className='lbaic-settings-dropdown-menu-action-label'>{__("Load more", 'limb-chatbot')}</span>
                            </button>
                        </div>}
                    {typeof addNew === 'function' &&
                        <div className='lbaic-settings-dropdown-menu-action'>
                            <button
                                type='button'
                                className={`lbaic-settings-button-reset lbaic-settings-dropdown-menu-action-in${disabled ? ' lbaic-settings-button-disabled' : ''}`}
                                onClick={addNewCallback}>
                                <span
                                    className='lbaic-settings-dropdown-menu-action-label'>{addNewLabel || __("Add new", 'limb-chatbot')}</span>
                            </button>
                        </div>}
                    {typeof refreshOptions === 'function' &&
                        <div className='lbaic-settings-dropdown-menu-action'>
                            <button
                                type='button'
                                className={`lbaic-settings-button-reset lbaic-settings-button lbaic-settings-button-primary lbaic-settings-button-center lbaic-settings-dropdown-menu-action-in${refreshingOptions || disabled ? ' lbaic-settings-button-disabled' : ''}`}
                                onClick={handleRefreshOptions}>
                                {refreshingOptions &&
                                    <svg
                                        className='lbaic-settings-button-i lbaic-settings-loading-circle lbaic-settings-button-loading-circle'
                                        xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24'>
                                        <use href='#lbaic-settings-circle'/>
                                    </svg>}
                                <span
                                    className='lbaic-settings-dropdown-menu-action-label'>{refreshOptionsLabel || __("Refresh", 'limb-chatbot')}</span>
                            </button>
                        </div>}
                </div>
            </div>, document.getElementById('lbaic-settings-field-menu'))}
            {errorMessage &&
                <div className='lbaic-settings-dropdown-error-message'>
                    <p className='lbaic-settings-dropdown-error-message-in'>{errorMessage}</p>
                </div>}
        </div>
    )
}