(function () {
    'use strict';

    function debounce(fn, wait) {
        var timer;
        return function () {
            var args = arguments;
            clearTimeout(timer);
            timer = setTimeout(function () {
                fn.apply(null, args);
            }, wait);
        };
    }

    function isRtl() {
        return document.documentElement.dir === 'rtl'
            || document.body.classList.contains('rtl');
    }

    function i18n(key, fallback) {
        var bag = window.erpSearchSelectI18n || {};
        return bag[key] || fallback;
    }

    function buildConfig(el, isAsync) {
        var isSmall = el.classList.contains('form-select-sm');
        var config = {
            searchEnabled: true,
            shouldSort: false,
            itemSelectText: '',
            removeItemButton: false,
            searchPlaceholderValue: i18n('searchPlaceholder', 'Search…'),
            noResultsText: i18n('noResults', 'No results found'),
            noChoicesText: i18n('typeToSearch', i18n('noChoices', 'No options')),
            loadingText: i18n('loading', 'Loading…'),
            placeholder: true,
            placeholderValue: el.getAttribute('data-placeholder') || '',
            fuseOptions: {
                threshold: 0.35,
                ignoreLocation: true,
            },
            classNames: {
                containerOuter: 'choices erp-choices' + (isSmall ? ' erp-choices--sm' : ''),
            },
        };

        if (isAsync) {
            config.searchChoices = false;
            config.searchFloor = 1;
        }

        return config;
    }

    function readSelectedValue(el) {
        var selected = el.querySelector('option[selected]');
        if (selected && selected.value) {
            return selected.value;
        }
        return el.value || '';
    }

    function hasPrefetchedOptions(el) {
        return el.querySelectorAll('option[value]:not([value=""])').length > 0;
    }

    function fetchLookup(el, query) {
        var type = el.getAttribute('data-lookup-type');
        var url = el.getAttribute('data-lookup-url');
        if (!type || !url) {
            return Promise.resolve(null);
        }

        var params = new URLSearchParams({
            type: type,
            q: query || '',
            limit: '30',
        });

        var selected = readSelectedValue(el);
        if (selected) {
            params.set('selected', selected);
        }

        var exclude = el.getAttribute('data-lookup-exclude');
        if (exclude) {
            params.set('exclude', exclude);
        }

        var parentName = el.getAttribute('data-lookup-parent');
        if (parentName) {
            var parentEl = document.querySelector('[name="' + parentName + '"]');
            var parentValue = '';
            if (parentEl) {
                if (parentEl._erpChoices && typeof parentEl._erpChoices.getValue === 'function') {
                    parentValue = parentEl._erpChoices.getValue(true) || '';
                }
                if (!parentValue) {
                    parentValue = parentEl.value || '';
                }
            }
            if (parentValue) {
                params.set('parent', parentValue);
            }
        } else if (window.erpSearchSelect && window.erpSearchSelect.appendParentParam) {
            window.erpSearchSelect.appendParentParam(el, params);
        }

        return fetch(url + '?' + params.toString(), {
            headers: {
                Accept: 'application/json',
                'X-Requested-With': 'XMLHttpRequest',
            },
            credentials: 'same-origin',
        })
            .then(function (res) {
                if (!res.ok) {
                    throw new Error('lookup failed');
                }
                return res.json();
            })
            .then(function (data) {
                return data.results || [];
            })
            .catch(function () {
                return null;
            });
    }

    function applyChoices(instance, items, selectedValue, keepExistingOnEmpty) {
        if (items === null) {
            return;
        }

        if (items.length === 0 && keepExistingOnEmpty) {
            return;
        }

        instance.setChoices(items, 'value', 'label', true);

        if (selectedValue) {
            instance.setChoiceByValue(selectedValue);
        }
    }

    function initAsyncSelect(el) {
        var instance = new Choices(el, buildConfig(el, true));
        var selectedValue = readSelectedValue(el);
        var prefetched = hasPrefetchedOptions(el);
        el._erpChoices = instance;

        fetchLookup(el, '').then(function (items) {
            applyChoices(instance, items, selectedValue, prefetched);
        });

        el.addEventListener('search', debounce(function (event) {
            var query = event.detail.value || '';
            fetchLookup(el, query).then(function (items) {
                applyChoices(instance, items, instance.getValue(true), query === '' && prefetched);
            });
        }, 280));
    }

    function initSyncSelect(el) {
        new Choices(el, buildConfig(el, false));
    }

    function initSearchSelect(el) {
        if (el.dataset.erpSearchSelectInit === '1' || typeof Choices === 'undefined') {
            return;
        }

        el.dataset.erpSearchSelectInit = '1';

        if (el.dataset.autoSubmit === '1') {
            el.addEventListener('change', function () {
                var form = el.closest('form');
                if (form) {
                    form.submit();
                }
            });
        }

        if (el.getAttribute('data-lookup-type')) {
            initAsyncSelect(el);
            return;
        }

        initSyncSelect(el);
    }

    function boot() {
        document.querySelectorAll('[data-erp-search-select]').forEach(initSearchSelect);
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', boot);
    } else {
        boot();
    }

    window.erpSearchSelect = {
        init: initSearchSelect,
        isRtl: isRtl,
        resolveParentValue: null,
        appendParentParam: null,
    };
})();
