export default function sticky(config = {}) {
    return {
        mode: "static",
        width: 0,
        left: 0,
        height: 0,
        anchor: config.anchor || "top",
        releaseAtBoundary: config.releaseAtBoundary ?? true,
        topOffset: config.topOffset || 0,
        bottomOffset: config.bottomOffset || 0,
        breakpoint: 1024,
        scrollHandler: null,
        resizeHandler: null,

        init() {
            this.scrollHandler = () => this.update();
            this.resizeHandler = () => this.update();

            window.addEventListener("scroll", this.scrollHandler, {passive: true});
            window.addEventListener("resize", this.resizeHandler);

            this.$nextTick(() => this.update());
        },

        destroy() {
            if (this.scrollHandler) {
                window.removeEventListener("scroll", this.scrollHandler);
            }

            if (this.resizeHandler) {
                window.removeEventListener("resize", this.resizeHandler);
            }
        },

        update() {
            if (window.innerWidth < this.breakpoint) {
                this.reset();
                return;
            }

            const root = this.$root;
            const panel = this.$refs.panel;
            const bounds = root ? root.parentElement : null;

            if (!root || !panel || !bounds) {
                return;
            }

            const rootRect = root.getBoundingClientRect();
            const boundsRect = bounds.getBoundingClientRect();
            const scrollY = window.scrollY || window.pageYOffset;
            const rootTop = scrollY + rootRect.top;
            const rootBottom = rootTop + root.offsetHeight;
            const boundsTop = scrollY + boundsRect.top;
            const boundsHeight = bounds.offsetHeight;
            const boundsBottom = boundsTop + boundsHeight;
            const panelHeight = panel.offsetHeight;

            this.width = rootRect.width;
            this.left = rootRect.left + window.scrollX;
            this.height = panelHeight;

            if (panelHeight >= boundsHeight) {
                this.mode = "static";
                return;
            }

            if (this.anchor === "bottom") {
                const viewportBottom = scrollY + window.innerHeight - this.bottomOffset;
                const start = rootBottom;
                const end = boundsBottom - this.bottomOffset;

                if (viewportBottom <= start) {
                    this.mode = "static";
                    return;
                }

                if (viewportBottom >= end) {
                    this.mode = this.releaseAtBoundary ? "bottom" : "fixed";
                    return;
                }

                this.mode = "fixed";
                return;
            }

            const start = rootTop - this.topOffset;
            const end = boundsTop + boundsHeight - panelHeight - this.topOffset;

            if (scrollY <= start) {
                this.mode = "static";
                return;
            }

            if (scrollY >= end) {
                this.mode = this.releaseAtBoundary ? "bottom" : "fixed";
                return;
            }

            this.mode = "fixed";
        },

        panelStyle() {
            if (this.mode !== "fixed") {
                return "";
            }

            if (this.anchor === "bottom") {
                return "left: " + this.left + "px; width: " + this.width + "px; bottom: " + this.bottomOffset + "px;";
            }

            return "left: " + this.left + "px; width: " + this.width + "px; top: " + this.topOffset + "px;";
        },

        reset() {
            this.mode = "static";
            this.width = 0;
            this.left = 0;
            this.height = 0;
        },
    };
}
