/**
 * @sc4rfurryx/proteusjs/scroll
 * Scroll-driven animations with CSS Scroll-Linked Animations
 * 
 * @version 2.0.0
 * @author sc4rfurry
 * @license MIT
 */

export interface ScrollAnimateOptions {
  keyframes: Keyframe[];
  range?: [string, string];
  timeline?: {
    axis?: 'block' | 'inline';
    start?: string;
    end?: string;
  };
  fallback?: 'io' | false;
}

/**
 * Zero-boilerplate setup for CSS Scroll-Linked Animations with fallbacks
 */
export function scrollAnimate(
  target: Element | string,
  opts: ScrollAnimateOptions
): void {
  const targetEl = typeof target === 'string' ? document.querySelector(target) : target;
  if (!targetEl) {
    throw new Error('Target element not found');
  }

  const {
    keyframes,
    range = ['0%', '100%'],
    timeline = {},
    fallback = 'io'
  } = opts;

  const {
    axis = 'block',
    start = '0%',
    end = '100%'
  } = timeline;

  // Check for CSS Scroll-Linked Animations support
  const hasScrollTimeline = 'CSS' in window && CSS.supports('animation-timeline', 'scroll()');

  // Check for reduced motion preference
  const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

  if (prefersReducedMotion) {
    // Respect user preference - either disable or reduce animation
    if (fallback === false) return;
    
    // Apply only the end state for reduced motion
    const endKeyframe = keyframes[keyframes.length - 1];
    Object.assign((targetEl as HTMLElement).style, endKeyframe);
    return;
  }

  if (hasScrollTimeline) {
    // Use native CSS Scroll-Linked Animations
    const timelineName = `scroll-timeline-${Math.random().toString(36).substr(2, 9)}`;
    
    // Create scroll timeline
    const style = document.createElement('style');
    style.textContent = `
      @scroll-timeline ${timelineName} {
        source: nearest;
        orientation: ${axis};
        scroll-offsets: ${start}, ${end};
      }
      
      .scroll-animate-${timelineName} {
        animation-timeline: ${timelineName};
        animation-duration: 1ms; /* Required but ignored */
        animation-fill-mode: both;
      }
    `;
    document.head.appendChild(style);

    // Apply animation class
    targetEl.classList.add(`scroll-animate-${timelineName}`);

    // Create Web Animations API animation
    const animation = targetEl.animate(keyframes, {
      duration: 1, // Required but ignored with scroll timeline
      fill: 'both'
    });

    // Set scroll timeline (when supported)
    if ('timeline' in animation) {
      (animation as any).timeline = new (window as any).ScrollTimeline({
        source: document.scrollingElement,
        orientation: axis,
        scrollOffsets: [
          { target: targetEl, edge: 'start', threshold: parseFloat(start) / 100 },
          { target: targetEl, edge: 'end', threshold: parseFloat(end) / 100 }
        ]
      });
    }

  } else if (fallback === 'io') {
    // Fallback using Intersection Observer
    let animation: Animation | null = null;

    const observer = new IntersectionObserver(
      (entries) => {
        entries.forEach(entry => {
          const progress = Math.max(0, Math.min(1, entry.intersectionRatio));
          
          if (!animation) {
            animation = targetEl.animate(keyframes, {
              duration: 1000,
              fill: 'both'
            });
            animation.pause();
          }

          // Update animation progress based on intersection
          animation.currentTime = progress * 1000;
        });
      },
      {
        threshold: Array.from({ length: 101 }, (_, i) => i / 100) // 0 to 1 in 0.01 steps
      }
    );

    observer.observe(targetEl);

    // Store cleanup function
    (targetEl as any)._scrollAnimateCleanup = () => {
      observer.disconnect();
      if (animation) {
        animation.cancel();
      }
    };
  }
}

/**
 * Create a scroll-triggered animation that plays once when element enters viewport
 */
export function scrollTrigger(
  target: Element | string,
  keyframes: Keyframe[],
  options: KeyframeAnimationOptions = {}
): void {
  const targetEl = typeof target === 'string' ? document.querySelector(target) : target;
  if (!targetEl) {
    throw new Error('Target element not found');
  }

  // Check for reduced motion preference
  const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

  if (prefersReducedMotion) {
    // Apply end state immediately
    const endKeyframe = keyframes[keyframes.length - 1];
    Object.assign((targetEl as HTMLElement).style, endKeyframe);
    return;
  }

  const observer = new IntersectionObserver(
    (entries) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          // Play animation
          targetEl.animate(keyframes, {
            duration: 600,
            easing: 'ease-out',
            fill: 'forwards',
            ...options
          });

          // Disconnect observer after first trigger
          observer.disconnect();
        }
      });
    },
    {
      threshold: 0.1,
      rootMargin: '0px 0px -10% 0px'
    }
  );

  observer.observe(targetEl);
}

/**
 * Parallax effect using scroll-driven animations
 */
export function parallax(
  target: Element | string,
  speed: number = 0.5
): void {
  const targetEl = typeof target === 'string' ? document.querySelector(target) : target;
  if (!targetEl) {
    throw new Error('Target element not found');
  }

  // Check for reduced motion preference
  const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
  if (prefersReducedMotion) return;

  const keyframes = [
    { transform: `translateY(${-100 * speed}px)` },
    { transform: `translateY(${100 * speed}px)` }
  ];

  scrollAnimate(targetEl, {
    keyframes,
    range: ['0%', '100%'],
    timeline: { axis: 'block' },
    fallback: 'io'
  });
}

/**
 * Cleanup function to remove scroll animations
 */
export function cleanup(target: Element | string): void {
  const targetEl = typeof target === 'string' ? document.querySelector(target) : target;
  if (!targetEl) return;

  // Call stored cleanup function if it exists
  if ((targetEl as any)._scrollAnimateCleanup) {
    (targetEl as any)._scrollAnimateCleanup();
    delete (targetEl as any)._scrollAnimateCleanup;
  }

  // Remove animation classes
  targetEl.classList.forEach(className => {
    if (className.startsWith('scroll-animate-')) {
      targetEl.classList.remove(className);
    }
  });

  // Cancel any running animations
  const animations = targetEl.getAnimations();
  animations.forEach(animation => animation.cancel());
}

// Export default object for convenience
export default {
  scrollAnimate,
  scrollTrigger,
  parallax,
  cleanup
};
