/*
Template Name: Corpboot - CbSelect
Description: Corporate HTML5 Template based on Bootstrap 5.
Version: 3.1
Author: Rafael Memmel - Wailo
Author URI: https://themeforest.net/user/wailothemes
*/

// Updates the selected value(s) in the cbselect based on the button clicked by the user.
function cbselectUpdate(button, classElement, classToggler) {
    const value = button.dataset.cbselectValue;
    const target = button.closest(`.${classElement}`).previousElementSibling;
    const toggler = target.nextElementSibling.querySelector(`.${classToggler}`);
    const input = target.nextElementSibling.querySelector('input');

    if (target.multiple) {
        const optionToUpdate = Array.from(target.options).find(option => option.value === value);
        if (optionToUpdate) optionToUpdate.selected = true;
    } else {
        target.value = value;
    }

    target.dispatchEvent(new Event('change'));
    toggler.focus();

    if (input) {
        input.value = '';
    }

    if (target.multiple) {
        toggler.click();
    }
}

// Removes the tag (selected value) from the cbselect when the user clicks the remove icon on the tag.
function cbselectRemoveTag(button, classElement, classToggler) {
    const value = button.parentNode.dataset.cbselectValue;
    const target = button.closest(`.${classElement}`).previousElementSibling;
    const optionToUpdate = Array.from(target.options).find(option => option.value === value);
    if (optionToUpdate) optionToUpdate.selected = false;

    const toggler = target.nextElementSibling.querySelector(`.${classToggler}`);
    const input = target.nextElementSibling.querySelector('input');

    target.dispatchEvent(new Event('change'));

    if (input) {
        input.value = '';
    }

    toggler.click();
}

// Handles the search functionality in the cbselect and also manages the 'creatable' option which allows users to add new values.
function cbselectSearch(event, input, classElement, classToggler, creatable) {
    const filterValue = input.value.toLowerCase().trim();
    const itemsContainer = input.nextElementSibling;
    const headers = itemsContainer.querySelectorAll('.dropdown-header');
    const items = itemsContainer.querySelectorAll('.dropdown-item');
    const noResults = itemsContainer.nextElementSibling;

    headers.forEach(header => header.classList.add('d-none'));

    items.forEach(item => {
        if (item.textContent.toLowerCase().includes(filterValue)) {
            item.classList.remove('d-none');
            let header = item;
            while ((header = header.previousElementSibling)) {
                if (header.classList.contains('dropdown-header')) {
                    header.classList.remove('d-none');
                    break;
                }
            }
        } else {
            item.classList.add('d-none');
        }
    });

    const foundItems = Array.from(items).filter(item => !item.classList.contains('d-none') && !item.hasAttribute('hidden'));
    if (!foundItems.length) {
        noResults.classList.remove('d-none');
        itemsContainer.classList.add('d-none');

        if (creatable) {
            noResults.innerHTML = `Press Enter to add "<strong>${input.value}</strong>"`;

            if (event.key === 'Enter') {
                const target = input.closest(`.${classElement}`).previousElementSibling;
                const toggler = target.nextElementSibling.querySelector(`.${classToggler}`);

                target.insertAdjacentHTML('afterbegin', `<option value="${input.value}" selected>${input.value}</option>`);
                target.dispatchEvent(new Event('change'));

                input.value = '';
                input.dispatchEvent(new Event('keyup'));

                toggler.click();
                toggler.focus();
            }
        }
    } else {
        noResults.classList.add('d-none');
        itemsContainer.classList.remove('d-none');
    }
}

// Clears all selected values from the cbselect.
function cbselectClear(button, classElement) {
    const target = button.closest(`.${classElement}`).previousElementSibling;
    Array.from(target.options).forEach(option => option.selected = false);
    target.dispatchEvent(new Event('change'));
}

// Initializes and constructs the cbselect dropdown, applying all configurations and options provided.
function cbselect(el, option = {}) {
    el.style.display = 'none'
    const classElement = 'cbselect-wrapper'
    const classNoResults = 'cbselect-no-results'
    const classTag = 'cbselect-tag'
    const classTagRemove = 'cbselect-tag-remove'
    const classPlaceholder = 'cbselect-placeholder'
    const classClearBtn = 'cbselect-clear'
    const classTogglerClearable = 'cbselect-clearable'
    const defaultSearch = false
    const defaultCreatable = false
    const defaultClearable = false
    const defaultMaxHeight = '360px'
    const defaultSize = ''
    const search = attrBool('search') || option.search || defaultSearch
    const creatable = attrBool('creatable') || option.creatable || defaultCreatable
    const clearable = attrBool('clearable') || option.clearable || defaultClearable
    const maxHeight = el.dataset.cbselectMaxHeight || option.maxHeight || defaultMaxHeight
    let size = el.dataset.cbselectSize || option.size || defaultSize
    size = size !== '' ? ` form-select-${size}` : ''
    const classToggler = `form-select${size}`

    const searchInput = search ? `<input onkeydown="return event.key !== 'Enter'" onkeyup="cbselectSearch(event, this, '${classElement}', '${classToggler}', ${creatable})" type="text" class="form-control" placeholder="Search" autofocus>` : ''

    function attrBool(attr) {
        const attribute = `data-cbselect-${attr}`
        if (!el.hasAttribute(attribute)) return null

        const value = el.getAttribute(attribute)
        return value.toLowerCase() === 'true'
    }

    function removePrev() {
        if (el.nextElementSibling && el.nextElementSibling.classList && el.nextElementSibling.classList.contains(classElement)) {
            el.nextElementSibling.remove()
        }
    }

    function isPlaceholder(option) {
        return option.getAttribute('value') === ''
    }

    function selectedTag(options, multiple) {
        if (multiple) {
            const selectedOptions = Array.from(options).filter(option => option.selected && !isPlaceholder(option))
            const placeholderOption = Array.from(options).filter(option => isPlaceholder(option))
            let tag = []
            if (selectedOptions.length === 0) {
                const text = placeholderOption.length ? placeholderOption[0].textContent : '&nbsp;'
                tag.push(`<span class="${classPlaceholder}">${text}</span>`)
            } else {
                for (const option of selectedOptions) {
                    tag.push(`
                        <div class="${classTag}" data-cbselect-value="${option.value}">
                            ${option.text}
                            <svg onclick="cbselectRemoveTag(this, '${classElement}', '${classToggler}')" class="${classTagRemove}" width="14" height="14" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z"/></svg>
                        </div>
                    `)
                }
            }
            return tag.join('')
        } else {
            const selectedOption = options[options.selectedIndex]
            return isPlaceholder(selectedOption)
            ? `<span class="${classPlaceholder}">${selectedOption.innerHTML}</span>`
            : selectedOption.innerHTML
        }
    }

    function selectedText(options) {
        const selectedOption = options[options.selectedIndex]
        return isPlaceholder(selectedOption) ? '' : selectedOption.textContent
    }

    function itemTags(options) {
        let items = []
        for (const option of options) {
            if (option.tagName === 'OPTGROUP') {
                items.push(`<h6 class="dropdown-header">${option.getAttribute('label')}</h6>`)
            } else {
                const hidden = isPlaceholder(option) ? ' hidden' : ''
                const active = option.selected ? ' active' : ''
                const disabled = el.multiple && option.selected ? ' disabled' : ''
                const value = option.value
                const text = option.textContent
                items.push(`<button${hidden} class="dropdown-item${active}" data-cbselect-value="${value}" type="button" onclick="cbselectUpdate(this, '${classElement}', '${classToggler}')"${disabled}>${text}</button>`)
            }
        }
        items = items.join('')
        return items
    }

    function createDom() {
        const autoclose = el.multiple ? ' data-bs-auto-close="outside"' : ''
        const additionalClass = Array.from(el.classList).filter(className => {
            return className !== 'form-select'
            && className !== 'form-select-sm'
            && className !== 'form-select-lg'
        }).join(' ')
        const clearBtn = clearable && !el.multiple ? `
        <button type="button" class="btn ${classClearBtn}" title="Clear selection" onclick="cbselectClear(this, '${classElement}')">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 14" fill="none">
                <path d="M13 1L0.999999 13" stroke-width="2" stroke="currentColor"></path>
                <path d="M1 1L13 13" stroke-width="2" stroke="currentColor"></path>
            </svg>
        </button>
        ` : ''
        const template = `
        <div class="dropdown ${classElement} ${additionalClass}">
            <button class="${classToggler} text-start ${!el.multiple && clearable ? classTogglerClearable : ''} selectcorp" data-cbselect-text="${!el.multiple && selectedText(el.options)}" type="button" data-bs-toggle="dropdown" aria-expanded="false"${autoclose}>
                ${selectedTag(el.options, el.multiple)}
            </button>
            <div class="dropdown-menu w-100">
                <div class="d-flex flex-column">
                    ${searchInput}
                    <div class="cbselect-items" style="max-height:${maxHeight};overflow:auto">
                        ${itemTags(el.querySelectorAll('*'))}
                    </div>
                    <div class="${classNoResults} d-none">No results found</div>
                </div>
            </div>
            ${clearBtn}
        </div>
        `
        removePrev()
        el.insertAdjacentHTML('afterend', template) // insert template after element
    }
    createDom()

    function updateDom() {
        const dropdown = el.nextElementSibling
        const toggler = dropdown.getElementsByClassName(classToggler)[0]
        const dSelectItems = dropdown.getElementsByClassName('cbselect-items')[0]
        toggler.innerHTML = selectedTag(el.options, el.multiple)
        dSelectItems.innerHTML = itemTags(el.querySelectorAll('*'))
        if (!el.multiple) {
            toggler.dataset.cbselectText = selectedText(el.options)
        }
    }

    el.addEventListener('change', updateDom)
}
