var SuperSearch = (function ($) {
    'use strict';

    var me = {

        config: {
            inputBuffer: 300 // in milliseconds
        },

        storage: {
            $input: null,
            $overlay: null,
            $details: null,
            $results: null,
            $lastUpdate: null,
            debounceBuffer: null,
            hasResults: false,
            isOpen: false
        },

        init: function () {
            me.storage.$input = $('#supersearch-input');
            if (me.storage.$input.length === 0) {
                return;
            }

            me.registerEvents();
        },

        registerEvents: function () {
            me.storage.$input.on('keyup.SuperSearch', me.onKeyUpSearchInput);

            // Overlay anzeigen bei Focus in das Such-Eingabefeld; nur wenn es schon mal geöffnet war
            me.storage.$input.on('focus.SuperSearch', me.onFocusSearchInput);

            // Overlay mit ESC schließen
            $(document).bind('keydown', function(e) {
                if (me.storage.$overlay === null) {
                    return;
                }
                if (me.storage.isOpen !== true) {
                    return;
                }

                // ESC
                if (e.keyCode === 27) {
                    me.hideOverlay();
                }
            });
        },

        /**
         * @return {jQuery}
         */
        getOverlay: function () {
            if (typeof me.storage.$overlay === 'undefined' || me.storage.$overlay === null) {
                me.storage.$overlay = me.createOverlay();
                me.storage.$details = me.storage.$overlay.find('section.detail');
                me.storage.$results = me.storage.$overlay.find('section.result');
                me.storage.$lastUpdate = me.storage.$overlay.find('section.last-update');
            }

            return me.storage.$overlay;
        },

        showOverlay: function () {
            var $overlay = me.getOverlay();
            $overlay.show();
            me.storage.isOpen = true;
            me.showDetails();
        },

        hideOverlay: function () {
            me.getOverlay().hide();
            me.storage.isOpen = false;
        },

        /**
         * @return {jQuery}
         */
        createOverlay: function () {
            var overlaySelector = '#supersearch-overlay';
            if ($(overlaySelector).length > 0) {
                return $(overlaySelector);
            }

            var overlayTemplate =
                '<span id="supersearch-icon-close" class="icon icon-close"></span>' +
                '<div class="result-wrapper">' +
                '<section class="empty-message">Keine Suchergebnisse gefunden</section>' +
                '<section class="error-message"></section>' +
                '<section class="result"></section>' +
                '<section class="last-update"></section>' +
                '</div>' +
                '<div class="detail-wrapper">' +
                '<section class="detail"></section>' +
                '</div>';

            var overlayIdAttr = overlaySelector.substr(1);
            var $overlay = $('<div>').attr('id', overlayIdAttr).addClass('supersearch-overlay').html(overlayTemplate);

            $overlay.off('click.SuperSearch', '#supersearch-icon-close');
            $overlay.on('click.SuperSearch', '#supersearch-icon-close', function (event) {
                event.preventDefault();
                me.hideOverlay();
            });

            $overlay.hide();
            $overlay.appendTo('#header');
            me.storage.isOpen = false;

            return $overlay;
        },

        /**
         * @param {Event} event
         */
        onKeyUpSearchInput: function (event) {
            event.preventDefault();
            var controlKeyCodes = [
                 9, // Tab
                13, // Enter
                16, // Shift
                17, // Strg
                18, // Alt
                20, // Caps lock
                27, // ESC
                37, // Cursor Left
                38, // Cursor Up
                39, // Cursor Right
                40  // Cursor Down
            ];
            if ($.inArray(event.keyCode, controlKeyCodes) !== -1) {
                return;
            }

            var that = this;
            me.debounce(function () {
                var searchQuery = $(that).val();
                me.fetchSearchResults(searchQuery).then(me.renderSearchResults);
            }, me.config.inputBuffer);
        },

        /**
         * Overlay anzeigen bei Focus in das Such-Eingabefeld; nur wenn es schon mal geöffnet war
         *
         * @param {Event} event
         */
        onFocusSearchInput: function (event) {
            event.preventDefault();
            if (me.storage.$overlay === null) {
                return;
            }
            if (me.storage.hasResults === false) {
                return;
            }

            me.showOverlay();
        },

        /**
         * @param {string} searchQuery
         *
         * @return {jqXHR}
         */
        fetchSearchResults: function (searchQuery) {
            if (typeof searchQuery !== 'string') {
                searchQuery = '';
            }

            return $.ajax({
                url: 'index.php?module=supersearch&action=ajax&cmd=search',
                method: 'post',
                dataType: 'json',
                data: {
                    search_query: searchQuery
                },
                error: function (jqXHR, textStatus, errorThrown) {
                    var errorMessage = 'SuperSearch - Unbekannter Fehler #31: ' + errorThrown;

                    // PHP-Skript hat Fehler geliefert (z.b. 404)
                    if (textStatus === 'error') {
                        errorMessage = 'SuperSearch - Unbekannter Server-Fehler beim Laden der Such-Ergebnisse: ';
                        errorMessage += errorThrown;
                    }

                    // PHP-Skript liefert JSON-Error-Response
                    if (jqXHR.hasOwnProperty('responseJSON') && jqXHR.responseJSON.hasOwnProperty('error')) {
                        errorMessage = 'SuperSearch - Server-Fehler beim Laden der Such-Ergebnisse: ';
                        errorMessage += jqXHR.responseJSON.error;

                        if (jqXHR.responseJSON.hasOwnProperty('data') &&
                            jqXHR.responseJSON.data === 'index-empty') {
                            me.showErrorMessage('Fehler: ' + jqXHR.responseJSON.error);
                            return;
                        }
                    }

                    alert(errorMessage);
                }
            });
        },

        /**
         * @param {Array} rawResult
         */
        renderSearchResults: function (rawResult) {
            var $overlay = me.getOverlay();
            var $resultContainer = $overlay.find('section.result');
            $resultContainer.html('');

            if (rawResult.length === 0 || !rawResult.hasOwnProperty('data')) {
                $resultContainer.html('Fehler: Suche hat fehlerhaftes Ergebnis geliefert.');
                me.storage.hasResults = false;
                me.hideResults();
                return;
            }

            // Overlay ausblenden, wenn Suchbegriff zu kurz
            if (rawResult.data === null) {
                me.storage.hasResults = false;
                me.hideOverlay();
                return;
            }

            // Anzeigen wann der Such-Index das letzte Mal aktualisiert wurde
            if (rawResult.data.hasOwnProperty('last_index_update_formatted')) {
                if (rawResult.data.last_index_update_formatted !== null) {
                    var lastIndexUpdate = rawResult.data.last_index_update_formatted;
                    me.storage.$lastUpdate.text('Such-Index vom ' + lastIndexUpdate).show();
                } else {
                    me.storage.$lastUpdate.text('').hide();
                }
            }

            var resultCount = rawResult.data.count;
            var searchResults = rawResult.data.results;
            if (resultCount === 0) {
                me.storage.hasResults = false;
                me.showEmptyResults();
                return;
            }

            me.storage.$details.html('');
            Object.keys(searchResults).forEach(function (group) {
                var groupResult = searchResults[group];
                var $groupHtml = me.buildGroupResult(groupResult.key, groupResult.title, groupResult.items);
                $resultContainer.append($groupHtml);
            });

            me.storage.hasResults = true;
            me.showResults();
            me.showOverlay();
        },

        /**
         * @param {string} groupKey
         * @param {string} groupTitle
         * @param {array}  items
         *
         * @return {jQuery}
         */
        buildGroupResult: function (groupKey, groupTitle, items) {
            if (items.length === 0) {
                return;
            }
            if (typeof groupTitle === 'undefined') {
                groupTitle = 'Ergebnis';
            }

            var $resultWrapper = $('<div class="result-group">');
            var $resultList = $('<ul class="result-list">');
            var $listHead = $('<li class="result-head">').html(groupTitle);

            $resultList.append($listHead);
            items.forEach(function (item) {
                item.group = groupKey;
                var itemType = item.type !== null ? item.type : 'default';
                var $listItem;

                switch (itemType) {
                    case 'default':
                    default:
                        $listItem = me.buildDefaultItemResult(item);
                        break;
                }

                $resultList.append($listItem);
            });
            $resultWrapper.append($resultList);

            return $resultWrapper;
        },

        /**
         * @param {object} item
         *
         * @return {jQuery}
         */
        buildDefaultItemResult: function (item) {
            var hasSubtitle = item.hasOwnProperty('subtitle') && typeof item.subtitle === 'string';
            var hasAdditionalInfos =
                item.hasOwnProperty('additionalInfos') &&
                typeof item.additionalInfos === 'object' &&
                item.additionalInfos !== null;

            var mainTitle = '<span class="title-main">' + item.title + '</span>';
            var subTitle = hasSubtitle ? '<span class="title-sub">' + item.subtitle + '</span>' : '';
            var titleString = '<span class="title">' + mainTitle + subTitle + '</span>';

            if (hasAdditionalInfos) {
                titleString += '<span class="caption">';
                $.each(item.additionalInfos, function (index, additionalInfo) {
                    titleString += '<span class="additional">' + additionalInfo + '</span>';
                });
                titleString += '</span>';
            }

            var $listItem = $('<li>').addClass('result-item');
            var $itemLink = $('<a>').attr('href', item.link).html(titleString);
            $itemLink.appendTo($listItem);

            $itemLink.on('click', function (e) {
                e.preventDefault();
                me.renderItemDetails(item);
            });

            return $listItem;
        },

        /**
         * Rendert Ergebnisdetails
         *
         * @param {object} item
         */
        renderItemDetails: function (item) {
            // Per AJAX ausführliche Inhalte nachladen
            me.fetchItemDetailsDynamicContent(item).then(
                function (data) {
                    me.renderItemDetailsDynamicContent(data, item);
                },
                function (jqXhr) {
                    var error =
                        typeof jqXhr.responseJSON !== 'undefined' &&
                        typeof jqXhr.responseJSON.error !== 'undefined'
                            ? jqXhr.responseJSON.error
                            : 'Unbekannter Fehler';
                    alert('Fehler beim Laden der Detail-Informationen: ' + error);
                }
            );
        },

        /**
         * @param {object} detailResult
         * @param {object} listItem Originales Item-Objekt aus Suchergebnis-Liste
         *
         * @return {void}
         */
        renderItemDetailsDynamicContent: function (detailResult, listItem) {
            if (!detailResult.hasOwnProperty('data') || detailResult.data === false) {
                // Es wurde kein Detail-Result gefunden
                // Link aus Suchergebnis-Item aufrufen
                me.hideDetails();
                window.location.href = listItem.link;
                return;
            }

            var detail = detailResult.data;
            var $details = me.storage.$details;

            // Überschrift rendern
            var $headline = $('<h1>').html(detail.title);
            $details.html('').append($headline);

            // Attachments (z.B. Buttons) rendern
            if (detail.hasOwnProperty('attachments')) {
                var $attachments = me.generateDetailAttachments(detail.attachments);
                $details.append($attachments);
            }

            me.showDetails();
        },

        /**
         * @param {object} item
         *
         * @return {jqXHR}
         */
        fetchItemDetailsDynamicContent: function (item) {
            return $.ajax({
                url: 'index.php?module=supersearch&action=ajax&cmd=detail',
                method: 'post',
                dataType: 'json',
                data: {
                    detail_group: item.group,
                    detail_identifier: item.identifier
                },
                error: function (jqXHR, textStatus, errorThrown) {
                    var errorMessage = 'SuperSearch - Unbekannter Fehler #32: ' + errorThrown;

                    // PHP-Skript hat Fehler geliefert (z.b. 404)
                    if (textStatus === 'error') {
                        errorMessage = 'SuperSearch - Unbekannter Server-Fehler beim Laden des Detail-Ergebnisses: ';
                        errorMessage += errorThrown;
                    }

                    // PHP-Skript liefer JSON-Error-Response
                    if (jqXHR.hasOwnProperty('responseJSON') && jqXHR.responseJSON.hasOwnProperty('error')) {
                        errorMessage = 'SuperSearch - Server-Fehler beim Laden des Detail-Ergebnisses: ';
                        errorMessage += jqXHR.responseJSON.error;
                    }

                    alert(errorMessage);
                }
            });
        },

        /**
         * @param {Array} attachments
         *
         * @return {jQuery} jQuery-Element
         */
        generateDetailAttachments: function (attachments) {
            var $attachments = $('<div>');

            $.each(attachments, function (index, attachment) {
                if (!attachment.hasOwnProperty('type')) {
                    console.error('Attachment ungültig. "type"-Property fehlt.');
                    return;
                }
                if (!attachment.hasOwnProperty('data')) {
                    console.error('Attachment ungültig. "data"-Property fehlt.');
                    return;
                }

                if (attachment.type === 'button_block') {
                    var $buttonBlock = me.generateDetailAttachmentTypeButtonBlock(attachment.data);
                    $attachments.append($buttonBlock);
                }
                if (attachment.type === 'content_static') {
                    var $contentStatic = me.generateDetailAttachmentTypeStaticContent(attachment.data);
                    $attachments.append($contentStatic);
                }
                if (attachment.type === 'content_dynamic') {
                    var $contentDynamic = me.generateDetailAttachmentTypeDynamicContent(attachment.data);
                    $attachments.append($contentDynamic);
                }
            });

            return $attachments;
        },

        /**
         * @param {Array} items
         *
         * @return {jQuery} jQuery-Element
         */
        generateDetailAttachmentTypeButtonBlock: function (items) {
            var $buttonBlock = $('<div>');

            $.each(items, function (index, item) {
                var $button = $('<a>').text(item.title).addClass('button');
                if (item.hasOwnProperty('attributes')) {

                    // Button-Attribute verarbeiten
                    $.each(item.attributes, function (attrName, attrValue) {
                        if (attrName === 'class') {
                            $button.addClass(attrValue);
                            return;
                        }
                        if (attrName === 'data-icon') {
                            var iconUrl = '';
                            switch (attrValue) {
                                case 'help':
                                    iconUrl = './themes/new/images/help.svg';
                                    break;
                                case 'settings':
                                    iconUrl = './themes/new/images/settings.svg';
                                    break;
                            }
                            if (iconUrl !== '') {
                                $button.addClass('icon');
                                $button.addClass('icon-' + attrValue);
                                var $iconElem = $('<img alt="Handbuch">').attr('src', iconUrl);
                                var $iconWrapper = $('<span class="icon">').append($iconElem);
                                $button.prepend($iconWrapper);

                            }
                        }
                        $button.attr(attrName, attrValue);
                    });
                }
                $button.appendTo($buttonBlock);
            });

            return $buttonBlock;
        },

        /**
         * @param {Object} data
         *
         * @return {jQuery} jQuery-Element
         */
        generateDetailAttachmentTypeStaticContent: function (data) {
            return  $('<p>').html(data.content);
        },

        /**
         * @param {Object} data
         *
         * @return {jQuery} jQuery-Element
         */
        generateDetailAttachmentTypeDynamicContent: function (data) {
            var $dynamicContent = $('<div>').addClass('minidetail');

            if (data.hasOwnProperty('url') && data.url !== null) {
                me.fetchMiniDetailContent(data.url, data.params)
                  .then(
                      function (htmlContent) {
                          $dynamicContent.html(htmlContent);
                          me.storage.$details.append($dynamicContent);
                      },
                      function (jqXhr) {
                          var message = 'Fehler beim Laden der Mini-Details: ';
                          if (jqXhr.hasOwnProperty('responseJSON') && jqXhr.responseJSON.hasOwnProperty('error')) {
                              message += jqXhr.responseJSON.error;
                          } else {
                              message += jqXhr.status + ' ' + jqXhr.statusText;
                          }
                          $('<div class="error"></div>').text(message).appendTo(me.storage.$details);
                      }
                  );
            }

            return $dynamicContent;
        },

        /**
         * @param {string} miniDetailUrl
         * @param {Object} miniDetailParams Zusätzliche POST-Parameter
         *
         * @return {jqXHR}
         */
        fetchMiniDetailContent: function (miniDetailUrl, miniDetailParams) {
            if (miniDetailUrl.substr(0, 10) !== 'index.php?') {
                alert('Mini-Detail-URL ist ungültig: ' + miniDetailUrl);
                throw 'Mini-Detail-URL ist ungültig: ' + miniDetailUrl;
            }
            if (typeof miniDetailParams !== 'object') {
                miniDetailParams = {};
            }

            return $.ajax({
                url: miniDetailUrl,
                data: miniDetailParams,
                method: 'post',
                dataType: 'html'
            });
        },

        /**
         * Suchergebnisse einblenden
         */
        showResults: function () {
            me.getOverlay().addClass('has-result');
            me.getOverlay().find('section.empty-message').hide();
            me.getOverlay().find('section.error-message').hide();
        },

        /**
         * Suchergebnisse ausblenden
         */
        hideResults: function () {
            me.getOverlay().removeClass('has-result');
            me.getOverlay().find('section.empty-message').hide();
            me.getOverlay().find('section.error-message').hide();
            me.getOverlay().find('section.last-update').hide();
        },

        /**
         * Details einblenden
         */
        showDetails: function () {
            me.getOverlay().addClass('has-detail');
            me.getOverlay().find('.detail-wrapper').scrollTop(0);
        },

        /**
         * Details einblenden
         */
        hideDetails: function () {
            me.getOverlay().removeClass('has-detail');
        },

        /**
         * Hinweis anzeigen das keine Ergebnisse gefunden wurden
         */
        showEmptyResults: function () {
            me.showOverlay();
            me.hideDetails();
            me.getOverlay().removeClass('has-result');
            me.getOverlay().find('section.empty-message').show();
            me.getOverlay().find('section.error-message').hide();
        },

        /**
         * @param {string} errorMessage
         */
        showErrorMessage: function (errorMessage) {
            me.showOverlay();
            me.hideDetails();
            me.getOverlay().find('section.empty-message').hide();
            me.getOverlay().find('section.error-message').html(errorMessage).show();
        },

        /**
         * Puffer-Funktion um Events erst nach einer bestimmten Zeit auszuführen
         *
         * @param {function}    callback
         * @param {number}      delay
         * @param {object|null} contextParam
         */
        debounce: function (callback, delay, contextParam) {
            var context = typeof contextParam !== 'undefined' && contextParam !== null ? contextParam : this;
            var args = arguments;

            window.clearTimeout(me.storage.debounceBuffer);
            me.storage.debounceBuffer = window.setTimeout(function () {
                callback.apply(context, args);
            }, delay || 250);
        }
    };

    return {
        init: me.init
    };

})(jQuery);

$(function () {
    SuperSearch.init();
});