(function ($, document, window, viewport) {
    'use strict';

    // PLUGIN CLASS
    // ============
    const StickyPluginClass = {

        element: null,

        initSticky: function() {
            $('.et-sticky-placeholder').remove();
            $('.et-sticky').each(function () {
                // add placeholder div beneath each potentially sticky element
                let $this = $(this);

                // check if the element is sticky on this viewport
                let breakpoints = $this.data('sticky-breakpoints');
                if(breakpoints) {
                    let currentViewport = viewport.current();
                    if(currentViewport && !breakpoints[currentViewport]) {
                        // Reset styles set from other breakpoints
                        $this.css('top', '');
                        $this.css('width', '');
                        return true;
                    }
                }

                // remove sticky active class - will be re-added, if necessary, by updateSticky
                $this.removeClass('et-sticky-active');
                $this.removeClass('et-sticky-active-last');

                // also reset the top value, if necessary/possible
                let initialCssTop = $this.data('sticky-initial-css-top');
                if(initialCssTop) {
                    $this.css('top', initialCssTop);
                }

                let id = window.JtlHelpers.guid();
                $this.data('sticky-height', $this.outerHeight());
                $this.data('sticky-initial-y', $this.offset().top);
                // initially, the y offset for affixes is the same, but as sticky elements may shrink, this value may need to be updated on scroll
                $this.data('sticky-affix-initial-y', $this.offset().top);
                $this.data('sticky-initial-css-top', $this.css('top'));

                // affixed elements now get their width set to a fixed value and limits for the distance they can scroll downwards
                if ($this.hasClass('et-sticky-affix') && $this.is(':visible')) {
                    $this.data('sticky-initial-css-width', $this.css('width'));
                    $this.css('width', $this.outerWidth(false));
                } else {
                    // normal sticky elements get a placeholder element below them
                    $this.before('<div class="et-sticky-placeholder" id="' + id + '"></div>');
                    $this.data('sticky-placeholder-id', id);
                }
                return true;
            });

            this.Events.publish('easytemplate/Plugin/etStickyPlugin/onInitSticky', [this]);
        },

        updateSticky: function() {
            let offsetTop = 0;
            let stickyElementsCount = 0;
            let baseZIndex = 0;
            let scrollTop = $(window).scrollTop();
            // update sticky navigation
            $('.et-sticky:visible:not(.et-skip-sticky)').each(function () {
                let $this = $(this);

                // check if the element is sticky on this viewport
                let breakpoints = $this.data('sticky-breakpoints');
                if(breakpoints) {
                    let currentViewport = viewport.current();
                    if(currentViewport && !breakpoints[currentViewport]) {
                        return true;
                    }
                }

                $this.removeClass('et-sticky-active-last');
                $this.data('sticky-height', $this.outerHeight()); // recalculate height (i.e. header might grow because of menu!)

                if ($this.hasClass('et-sticky-affix')) {
                    if(!$this.hasClass('et-sticky-active')) {
                        // we are not sticky yet, update our initial y offset, just in case of a sticky above us being smaller now
                        $this.data('sticky-affix-initial-y', $this.offset().top);
                    }
                    if ((scrollTop + offsetTop) >= $this.data('sticky-affix-initial-y')) {
                        let verticalLimit = 0;
                        let dimensionDelta = 10;
                        let containerSelector = $this.data('affix-to');
                        let $container;
                        if (containerSelector.length) {
                            let inParent = (containerSelector === 'parent');
                            $container = inParent ? $this.parent() : $(containerSelector);
                            // Special check for affixes that are affixed to parents
                            if(inParent) {
                                let $container = $this.parent();
                                let $parentContainer = $container.closest('.container, .container-fluid');
                                if($parentContainer.length) {
                                    let previousOverflow = $this.css('overflow');
                                    $this.css('overflow', 'hidden'); // to account for margins within this element
                                    if ($this.outerHeight(true) >= $parentContainer.height()) {
                                        // We should never be sticky - we seem to define the .container height
                                        $this.css('overflow', previousOverflow);
                                        return true;
                                    }
                                    $this.css('overflow', previousOverflow);
                                }
                            }
                            if ($container.length) {
                                verticalLimit =  $container.outerHeight(false) - $this.data('sticky-height');
                                if(verticalLimit <= 0
                                    || ($container && $container.length && $this.outerHeight(true) >= ($container.outerHeight(false) - dimensionDelta))
                                    || ($container && $container.length && $this.outerWidth(true) >= $container.innerWidth() - dimensionDelta)) {
                                    // prevent the affix from becoming sticky if it is bigger than its container or the same width as its container
                                    $this.removeClass('et-sticky-active');
                                } else {
                                    $this.addClass('et-sticky-active');
                                    let verticalOffset = (scrollTop + offsetTop) - $this.data('sticky-affix-initial-y');
                                    if (verticalOffset >= verticalLimit) {
                                        // absolute style display within parent
                                        $this.css('top', verticalLimit + "px");
                                        $this.addClass('et-sticky-affix-at-limit')
                                    } else {
                                        // fixed style display - is smoother
                                        $this.css('top', offsetTop + 'px');
                                        $this.removeClass('et-sticky-affix-at-limit');
                                    }
                                }
                            }
                        }
                    } else {
                        $this.removeClass('et-sticky-active');
                        $this.css('top', $this.data('sticky-initial-css-top'));
                    }
                } else {
                    let id = $this.data('sticky-placeholder-id');
                    let $placeholder = $('.et-sticky-placeholder#' + id);

                    if ((scrollTop + offsetTop) > $this.data('sticky-initial-y')) {
                        // make sticky!
                        $placeholder.css('padding-top', $this.data('sticky-height') + 'px');
                        $this.addClass('et-sticky-active');
                        $this.css('top', offsetTop + 'px');
                        // auto adjust z-index to allow dropdowns to overlay "later" sticky elements
                        if (stickyElementsCount === 0) {
                            baseZIndex = $this.css('z-index');
                        } else {
                            $this.css('z-index', baseZIndex - stickyElementsCount);
                        }
                        offsetTop = offsetTop + $this.data('sticky-height');
                        stickyElementsCount++;
                    } else {
                        // make unsticky
                        $this.removeClass('et-sticky-active');
                        $placeholder.css('padding-top', '0');
                        $this.css('top', $this.data('sticky-initial-css-top'));
                        offsetTop = offsetTop - $this.data('sticky-height');
                    }
                }
                return true;
            });
            $('.et-sticky-active:not(.et-sticky-affix)').last().addClass('et-sticky-active-last');

            /*
             Also: Update the position of the JTL Search Result box if it exists. (It does not work with
             sticky search inputs on its own).

             TODO: Adjust for JTL Search for Shop 5
             */
            let $jtlsearchresults = $('.jtl_search_results');
            if($jtlsearchresults.length) {
                let $input = $('.ac_input'); // this is the selector JTL search looks for in its inject.php
                if($input.length) {
                    $input = $input.first();
                    let newTop = $input.offset().top + $input.outerHeight();
                    $jtlsearchresults.css('top', newTop + 'px');
                }
            }

            /*
                We use the "et-current-link" class to tag the first link that matches the top most visible anchor
                TODO: Check if this still works as intended or is needed
             */
            let found = false;
            $('.et-vertical-nav-anchor').each(function() {
                if(found) {
                    return;
                }
                // notice we walk through the anchors from top to bottom. so the first one that is beyond our sticky threshold, is the one we want
                let $this = $(this);
                if($this.offset().top > (scrollTop + offsetTop) - 10) { // 10 px for imprecise heights
                    found = true;
                    $('.et-vertical-nav a').removeClass('et-current-link');
                    $('.et-vertical-nav a[href="#'+$this.attr('id')+'"]').addClass('et-current-link');
                }
            });

            // Update Scroll Padding top to prevent sticky elements from overlapping the scroll target
            if(offsetTop > 0) {
                $('html').css('scroll-padding-top', offsetTop + 'px');
            } else {
                $('html').css('scroll-padding-top', '');
            }
        },

        /**
         * @this {PluginBase & StickyPluginClass}
         * @param {HTMLElement} element
         * @param {Object} options
         */
        init: function (element, options) {
            $(window).on('load',() => {
                this.initSticky();
                $(window).resize(window.JtlHelpers.debounce(() => {
                    this.initSticky(); // re-initialization may be needed due to breakpoint changes.
                    this.updateSticky();
                }, 150));
                // handle sticky elements within variation loaded content
                $('body').on('article-variation-loaded.et', () => {
                    this.initSticky(); // new elements might have been added
                    this.updateSticky();
                });
                // handle sticky elements within checkout, TODO: Check if this still holds true, probably not.
                $(document).on('evo:loaded.evo.content', () => {
                    this.initSticky();
                    this.updateSticky();
                });
                $(window).scroll(this.updateSticky.bind(this));
            });
        },
    };

    // REGISTER AND ADD THE PLUGIN
    // ===========================
    if (!window.easytemplate && !window.easytemplate.PluginManager) {
        throw Error('Cannot initialize Easytemplate Plugin. plugin-manager.js has to be loaded first!');
    }

    /** @type {PluginManager} manager */
    const manager = window.easytemplate.PluginManager;
    manager.addPlugin('body', StickyPluginClass, 'etStickyPlugin');
})(jQuery, document, window, ResponsiveBootstrapToolkit);