/**
 * @see ui-core/_intersection-animator.scss
 */
export class BaseAnimator {
  constructor(
    elements,
    delay = 150,
    transitionDuration = 500,
    propertySetters = {}
  ) {
    this.elements = elements;
    this.delay = delay;
    this.transitionDuration = transitionDuration;

    // Merge custom property setters with defaults.
    this.propertySetters = {
      setInitialProperties: this.setInitialProperties.bind(this),
      setAnimatedProperties: this.setAnimatedProperties.bind(this),
      ...propertySetters
    };

    const IS_REDUCED =
      window.matchMedia(`(prefers-reduced-motion: reduce)`) === true ||
      window.matchMedia(`(prefers-reduced-motion: reduce)`).matches === true;
    if (IS_REDUCED) true;

    // Convert elements to array - handles NodeList, HTMLCollection, single element, or existing array
    const elementsArray = Array.from(elements.length !== undefined ? elements : [elements]);

    // Filter out elements that should be skipped.
    const animatableElements = elementsArray.filter(element =>
      !element.closest('[data-animator-optout]')
    );

    // Check if IntersectionObserver is available.
    if (typeof IntersectionObserver !== 'undefined') {
      const OBSERVER = new IntersectionObserver(entries => {
        entries.forEach((entry, index) => {
          if (entry.isIntersecting) {
            const element = entry.target;
            element.style.setProperty('transition-delay', `${ index * this.delay }ms`);

            // Use custom property setter for animated properties.
            this.propertySetters.setAnimatedProperties(element);

            OBSERVER.unobserve(element);
          }
        });
      }, {
        root: null,
        threshold: 0,
        rootMargin: '0% 0% -50px 0%'
      });

      animatableElements.forEach(element => {
        // Use custom property setter for initial properties.
        this.propertySetters.setInitialProperties(element);
        element.style.setProperty('transition-duration', `${ this.transitionDuration }ms`);
      });

      setTimeout(() => {
        animatableElements.forEach((element) => {
          OBSERVER.observe(element);
        });
      });
    }

    // Fallback: IntersectionObserver not available, apply animated properties immediately
    else {
      animatableElements.forEach(element => {
        // Set initial properties first.
        this.propertySetters.setInitialProperties(element);

        // Apply animated properties immediately without transitions for graceful degradation.
        this.propertySetters.setAnimatedProperties(element);
        element.style.setProperty('transition-duration', `${ this.transitionDuration }ms`);
      });
    }
  }

  /**
   * Override this method in subclasses to set initial CSS properties
   * @param {HTMLElement} element - The element to set properties on
   */
  setInitialProperties(element) { }

  /**
   * Override this method in subclasses to set initial CSS properties
   * @param {HTMLElement} element - The element to set properties on
   */
  setAnimatedProperties(element) { }
}
