import { addClass, removeClass } from '@primeuix/utils/dom';
import BaseAnimateOnScroll from './BaseAnimateOnScroll';

const AnimateOnScroll = BaseAnimateOnScroll.extend('animateonscroll', {
    created() {
        this.$value = this.$value || {};
        this.$el.style.opacity = this.$value.enterClass ? '0' : '';
    },
    mounted() {
        this.$el.setAttribute('data-pd-animateonscroll', true);

        this.bindIntersectionObserver();
    },
    unmounted() {
        this.unbindAnimationEvents();
        this.unbindIntersectionObserver();
    },
    observer: undefined,
    resetObserver: undefined,
    isObserverActive: false,
    animationState: undefined,
    animationEndListener: undefined,
    methods: {
        bindAnimationEvents() {
            if (!this.animationEndListener) {
                this.animationEndListener = () => {
                    removeClass(this.$el, [this.$value.enterClass, this.$value.leaveClass]);
                    !this.$modifiers.once && this.resetObserver.observe(this.$el);
                    this.unbindAnimationEvents();
                };

                this.$el.addEventListener('animationend', this.animationEndListener);
            }
        },
        bindIntersectionObserver() {
            const { root, rootMargin, threshold = 0.5 } = this.$value;
            const options = { root, rootMargin, threshold };

            // States
            this.observer = new IntersectionObserver(([entry]) => {
                if (this.isObserverActive) {
                    if (entry.boundingClientRect.top > 0) {
                        entry.isIntersecting ? this.enter() : this.leave();
                    }
                } else if (entry.isIntersecting) {
                    this.enter();
                }

                this.isObserverActive = true;
            }, options);

            setTimeout(() => this.observer.observe(this.$el), 0);

            // Reset
            this.resetObserver = new IntersectionObserver(
                ([entry]) => {
                    if (entry.boundingClientRect.top > 0 && !entry.isIntersecting) {
                        this.$el.style.opacity = this.$value.enterClass ? '0' : '';
                        removeClass(this.$el, [this.$value.enterClass, this.$value.leaveClass]);

                        this.resetObserver.unobserve(this.$el);
                    }

                    this.animationState = undefined;
                },
                { ...options, threshold: 0 }
            );
        },
        enter() {
            if (this.animationState !== 'enter' && this.$value.enterClass) {
                this.$el.style.opacity = '';
                removeClass(this.$el, this.$value.leaveClass);
                addClass(this.$el, this.$value.enterClass);

                this.$modifiers.once && this.unbindIntersectionObserver(this.$el);

                this.bindAnimationEvents();
                this.animationState = 'enter';
            }
        },
        leave() {
            if (this.animationState !== 'leave' && this.$value.leaveClass) {
                this.$el.style.opacity = this.$value.enterClass ? '0' : '';
                removeClass(this.$el, this.$value.enterClass);
                addClass(this.$el, this.$value.leaveClass);

                this.bindAnimationEvents();
                this.animationState = 'leave';
            }
        },
        unbindAnimationEvents() {
            if (this.animationEndListener) {
                this.$el.removeEventListener('animationend', this.animationEndListener);
                this.animationEndListener = undefined;
            }
        },
        unbindIntersectionObserver() {
            this.observer?.unobserve(this.$el);
            this.resetObserver?.unobserve(this.$el);
            this.isObserverActive = false;
        }
    }
});

export default AnimateOnScroll;
