(function () {
    window.initStagSuggest = function () {

        let suggestionsOuter = null;

        const debounce = (func, wait) => {
            let timeout;
            return function executedFunction(...args) {
                const later = () => {
                    clearTimeout(timeout);
                    func(...args);
                };
                clearTimeout(timeout);
                timeout = setTimeout(later, wait);
            };
        };

        var lastTerm = '';
        var returnedFunction = debounce(function (elem) {
            let term = elem.val();
            let ep = $(elem).attr('stag-suggest-ep'), extra = $(elem).attr('stag-suggest-extra');
            if(!!ep) { // remote lookup
                if (!!term && term.length >= 2) {

                    $.get(ep + '?term=' + $.trim(term) + (!!extra ? '&' + extra : ''), function (_data) {

                        /*
                        expected return format:
                        {
                            success: true,
                            data: [
                                {
                                    x: ...,
                                    y: ...,
                                    text: ...    // "text" key is "mandatory"
                                },
                                {
                                    x: ...,
                                    y: ...,
                                    text: ...    // "text" key is "mandatory"
                                },
                                ...
                            ]
                        }
                         */

                        suggestionsOuter.empty();
                        if(!hasResponseError(_data) && _data.data && _data.data.length) {
                            for (let i = 0; i < _data.data.length; i++) {
                                let item = $('<a native href="#" class="d-block suggest-item stag-suggest text-nowrap"/>');
                                for(let x in _data.data[i]) {
                                    if(_data.data[i].hasOwnProperty(x) && x !== 'text') {
                                        item.attr('data-' + x, _data.data[i][x]);
                                    }
                                }
                                item.data('suggest-data', _data.data[i]);
                                item.html(_data.data[i].text);
                                if(_data.data[i].sub_text) {
                                    item.append($('<span/>')
                                        .addClass('ml-1 text-sm text-secondary')
                                        .append(' (')
                                        .append(_data.data[i].sub_text)
                                        .append(')')
                                    );
                                }
                                if(_data.data[i].text2) {
                                    item.append($('<div/>')
                                        .addClass('text-sm text-secondary')
                                        .html(_data.data[i].text2)
                                    );
                                }
                                if(_data.data[i].tooltip) {
                                    item.attr('title', _data.data[i].tooltip);
                                }
                                if(i === 0) {
                                    item.addClass('active');
                                }
                                suggestionsOuter.append(item);
                            }
                        }
                        else {
                            suggestionsOuter.html('<span class="d-block no-suggest-items">No matches!</span>');
                        }

                        suggestionsOuter.removeClass('d-none');
                    }, 'json');
                    lastTerm = term;
                } else {
                    suggestionsOuter.addClass('d-none');
                }
            }
            else { // local lookup
                let optionList = $(elem).next().next('.data-option-list');
                if(optionList.length) {
                    let matches = [];
                    optionList.find('>div').each(function() {
                        if(!term || $(this).text().toLowerCase().indexOf(term.toLowerCase()) !== -1) {
                            matches.push($(this).text());
                        }
                    });
                    suggestionsOuter.empty();
                    if(matches.length) {
                        for (let i = 0; i < matches.length; i++) {
                            let item = $('<a native href="#" class="d-block suggest-item stag-suggest text-nowrap"/>');
                            item.data('suggest-data', {});
                            item.html(matches[i]);
                            suggestionsOuter.append(item);
                        }
                    }
                    else {
                        suggestionsOuter.html('<span class="d-block no-suggest-items">No matches!</span>');
                    }
                    suggestionsOuter.removeClass('d-none');
                }
            }

        }, 250);

        function handleKeydown(elem, e) {
            let term = $.trim(elem.val());
            let activeItem = suggestionsOuter.find('.suggest-item.active');
            switch (e.which) {
                case 27:
                    if(suggestionsOuter.is(':visible')) {
                        suggestionsOuter.addClass('d-none');
                        markEventAsConsumed(e);
                        return false;
                    }
                    break;
                case 38:
                    if(suggestionsOuter.is(':visible')) {
                        if (activeItem.prev().length) {
                            activeItem.prev()
                                .addClass('active')
                                .siblings().removeClass('active');
                            activeItem = suggestionsOuter.find('.suggest-item.active');
                            if (activeItem.length) {
                                if(activeItem[0].scrollIntoViewIfNeeded) {
                                    activeItem[0].scrollIntoViewIfNeeded(false);
                                }
                                else {
                                    activeItem[0].scrollIntoView({
                                        behavior: "auto",
                                        block: "start",
                                        inline: "start"
                                    });
                                }
                            }
                        }
                        return false;
                    }
                    break;
                case 40:
                    if(suggestionsOuter.is(':visible')) {
                        if (activeItem.next().length) {
                            activeItem.next()
                                .addClass('active')
                                .siblings().removeClass('active');
                            activeItem = suggestionsOuter.find('.suggest-item.active');
                            if (activeItem.length) {
                                if(activeItem[0].scrollIntoViewIfNeeded) {
                                    activeItem[0].scrollIntoViewIfNeeded(false);
                                }
                                else {
                                    activeItem[0].scrollIntoView({
                                        behavior: "auto",
                                        block: "end",
                                        inline: "end"
                                    });
                                }
                            }
                        }
                        return false;
                    }
                    break;
                case 13:
                    if(suggestionsOuter.is(':visible')) {
                        if (activeItem.length) {
                            activeItem.first().trigger('mousedown');
                        }
                        return false;
                    }
                    break;
                default:
                    if (!!term) {
                        suggestionsOuter
                            .html('<span class="d-block no-suggest-items">Searching...</span>')
                            .removeClass('d-none');
                        returnedFunction(elem);
                    } else {
                        suggestionsOuter.addClass('d-none');
                    }
                    break;
            }
        }

        function handleKeypress(elem, e) {
            var term = $.trim(elem.val());
            if (!!term || !$(elem).is('[stag-suggest-ep]')) {
                suggestionsOuter
                    .html('<span class="d-block no-suggest-items">Searching...</span>')
                    .removeClass('d-none');
                returnedFunction(elem);
            } else {
                suggestionsOuter.addClass('d-none');
            }
        }

        $('[stag-suggest]:not([stag-suggest-initialized])').each(function () {
            let elem = $(this);
            elem.next('.stag-suggestions-container').remove();
            $('<div class="stag-suggestions-container position-relative">' +
                '<div class="suggestions-outer stag-suggestions position-absolute d-none"></div>' +
                '</div>').insertAfter(elem);

            elem
                .off('focus.stag-suggest')
                .on('focus.stag-suggest', function (e) {
                    if(!$(this).is('[stag-suggest-ep]')) {
                        suggestionsOuter = $(this).next('.stag-suggestions-container').find('>.suggestions-outer');
                        return handleKeypress($(this), e);
                    }
                })
                .off('keydown.stag-suggest')
                .on('keydown.stag-suggest', function (e) {
                    suggestionsOuter = $(this).next('.stag-suggestions-container').find('>.suggestions-outer');
                    return handleKeydown($(this), e);
                })
                .off('paste.stag-suggest')
                .on('paste.stag-suggest', function (e) {
                    window.setTimeout(() => {
                        suggestionsOuter = $(this).next('.stag-suggestions-container').find('>.suggestions-outer');
                        return handleKeypress($(this), e);
                    }, 100);
                })
                .off('keypress.stag-suggest')
                .on('keypress.stag-suggest', function (e) {
                    suggestionsOuter = $(this).next('.stag-suggestions-container').find('>.suggestions-outer');
                    return handleKeypress($(this), e);
                });

            $(this).attr('stag-suggest-initialized', 1);
        });

        // on auto-suggest selection
        $(document).off('mousedown.stag-suggest', '.suggest-item.stag-suggest');
        $(document).on('mousedown.stag-suggest', '.suggest-item.stag-suggest', function () {

            $('.suggestions-outer.stag-suggestions').addClass('d-none');

            let data = $(this).data('suggest-data'),
                label = $.trim($(this).text());

            // set value
            let input = $(this).closest('.position-relative').prev('[stag-suggest]');

            // if input is [stag-suggest-text-only] - use only the text bit in label
            if(input.is('[stag-suggest-text-only]')) {
                label = data.text;
            }

            input.val(label);
            input.data('suggest-data', data);

            let scope = input.attr('stag-suggest-scope');
            if(!scope) scope = 'form';
            scope = $(scope);

            for(let x in data) {
                if(data.hasOwnProperty(x)) {
                    input.attr('data-' + x, data[x]);

                    // auto-populate if there's a field matching data-name="x" in the scope
                    if(scope.find('[data-name="' + x + '"]').length) {
                        scope.find('[data-name="' + x + '"]').val(data[x]).trigger('change');
                    }

                }
            }
            input.trigger('input');
            input.trigger('change');

            input.trigger('stag-suggest-selected', [input, data]);

            return false;
        });

        // outside click
        $(document)
            .off('mousedown.stag-suggest-outer-click')
            .on('mousedown.stag-suggest-outer-click', function (_e) {
                let elem = $(_e.target);
                // if mousedown is on the input whose stag-suggest is on display, ignore
                if(elem.is('[stag-suggest]') && elem.next('.stag-suggestions-container').find('>.suggestions-outer').is(':visible')) {
                    return false;
                }
                if(!elem.is('.stag-suggestions-container') && !elem.closest('.stag-suggestions-container').length) {
                    if($('.stag-suggestions-container .suggestions-outer:not(.d-none)').length) {
                        $('.stag-suggestions-container .suggestions-outer').addClass('d-none');
                        return false;
                    }
                }
            });

    }
    addMCInitializer('stag-suggest', window.initStagSuggest);
})();
