// animations.jsx — GSAP + ScrollTrigger premium scroll animation system
// Requires: gsap.min.js + ScrollTrigger.min.js loaded before this file
// Called from HomePage via window.initGSAPAnimations()

(function buildAnimationsModule() {

  // ─── Reduced motion guard ─────────────────────────────────────────────────
  const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

  // ─── Plugin registration ──────────────────────────────────────────────────
  function ensureGSAP() {
    if (!window.gsap || !window.ScrollTrigger) {
      console.warn('[animations] GSAP or ScrollTrigger not found — animations skipped.');
      return false;
    }
    window.gsap.registerPlugin(window.ScrollTrigger);
    return true;
  }

  // ─── Utility: safe querySelectorAll ──────────────────────────────────────
  function $$(sel, ctx) {
    return Array.from((ctx || document).querySelectorAll(sel));
  }

  // ─────────────────────────────────────────────────────────────────────────
  // 1. HERO — blob multi-speed parallax + content cinematic scroll-out
  // No conflict: aurora-blob and aurora-content are not .reveal elements
  // Replaces the manual window.scroll listener previously in AuroraHero
  // ─────────────────────────────────────────────────────────────────────────
  function animateHero(gsap, ScrollTrigger) {
    const hero = document.querySelector('.aurora-hero');
    if (!hero) return;

    const content  = hero.querySelector('.aurora-content');
    const blobs    = hero.querySelectorAll('.aurora-blob');
    const hFooter  = hero.querySelector('.aurora-footer');

    // ── Blobs: three independent parallax speeds ──────────────────────────
    // Each blob drifts at a different rate, amplifying the depth illusion.
    const blobSpeeds = [110, -70, 160]; // px to travel over full hero scroll distance
    blobs.forEach((blob, i) => {
      gsap.to(blob, {
        y: blobSpeeds[i],
        ease: 'none',
        scrollTrigger: {
          trigger: hero,
          start: 'top top',
          end: 'bottom top',
          scrub: 2.8,
          invalidateOnRefresh: true,
        },
      });
    });

    // ── Content: parallax up + opacity fade as hero exits ─────────────────
    // Starts after 15% of hero has scrolled; finishes when hero is fully gone.
    if (content) {
      gsap.to(content, {
        y: () => -(window.innerHeight * 0.28),
        opacity: 0,
        ease: 'power1.in',
        scrollTrigger: {
          trigger: hero,
          start: '15% top',
          end: 'bottom top',
          scrub: 1.6,
          invalidateOnRefresh: true,
        },
      });
    }

    // ── Footer scroll cue: fade early so it doesn't linger ───────────────
    if (hFooter) {
      gsap.to(hFooter, {
        opacity: 0,
        ease: 'none',
        scrollTrigger: {
          trigger: hero,
          start: '8% top',
          end: '22% top',
          scrub: 1,
        },
      });
    }
  }

  // ─────────────────────────────────────────────────────────────────────────
  // 2. CAPABILITIES STACK — clip-path wipe, alternating left/right direction
  // .svc-stack-item has .reveal class, so we use clipPath (safe, no conflict).
  // ─────────────────────────────────────────────────────────────────────────
  function animateCapabilities(gsap) {
    $$('.svc-stack-item').forEach((item, i) => {
      const fromLeft = i % 2 === 0;
      gsap.fromTo(
        item,
        { clipPath: fromLeft ? 'inset(0 100% 0 0)' : 'inset(0 0 0 100%)' },
        {
          clipPath: 'inset(0 0% 0 0%)',
          duration: 1.0,
          ease: 'expo.out',
          // immediateRender keeps initial state even before trigger fires
          immediateRender: true,
          scrollTrigger: {
            trigger: item,
            start: 'top 90%',
          },
        }
      );
    });
  }

  // ─────────────────────────────────────────────────────────────────────────
  // 3. WORK LIST — thumbnail clip-path wipe from bottom
  // .wl-thumb is NOT a .reveal element — fully safe.
  // Note: the img itself is NOT GSAP-animated to preserve the CSS hover scale
  // (`.wl-item:hover .wl-thumb img { transform: scale(1.07) }`) — a GSAP
  // scrub on the img would set inline transform and break that hover state.
  // ─────────────────────────────────────────────────────────────────────────
  function animateWorkItems(gsap, ScrollTrigger) {
    $$('.wl-thumb').forEach((thumb) => {
      const item = thumb.closest('.wl-item') || thumb.parentElement;

      // Thumbnail: clip-path wipe from bottom — reveals as item enters viewport
      gsap.fromTo(
        thumb,
        { clipPath: 'inset(100% 0 0 0)' },
        {
          clipPath: 'inset(0% 0 0 0)',
          duration: 0.95,
          ease: 'power3.out',
          immediateRender: true,
          scrollTrigger: {
            trigger: item,
            start: 'top 88%',
          },
        }
      );
    });
  }

  // ─────────────────────────────────────────────────────────────────────────
  // 4. PROCESS — step numbers slide in + accent timeline line draws itself
  // .step has .reveal class → only use `x` on children (.step-num).
  // Drawing line uses a dynamically inserted element — no conflict.
  // ─────────────────────────────────────────────────────────────────────────
  function animateProcess(gsap, ScrollTrigger) {
    // Step numbers slide in from the left (x only — no opacity, avoids reveal clash)
    $$('.step-num').forEach((num, i) => {
      gsap.fromTo(
        num,
        { x: -50, scale: 0.8 },
        {
          x: 0,
          scale: 1,
          duration: 1.0,
          ease: 'expo.out',
          immediateRender: true,
          scrollTrigger: {
            trigger: num.closest('.step') || num,
            start: 'top 88%',
          },
        }
      );
    });

    // Accent line that draws across the top of the process steps grid.
    // Guard against duplicate inserts on route-change remounts.
    const stepsGrid = document.querySelector('.process-steps');
    if (stepsGrid) {
      const existing = stepsGrid.parentElement.querySelector('.gsap-process-line');
      if (existing) existing.remove();
      const line = document.createElement('div');
      line.className = 'gsap-process-line';
      stepsGrid.parentElement.insertBefore(line, stepsGrid);

      gsap.fromTo(
        line,
        { scaleX: 0 },
        {
          scaleX: 1,
          ease: 'none',
          scrollTrigger: {
            trigger: stepsGrid,
            start: 'top 78%',
            end: 'top 30%',
            scrub: 1.2,
            invalidateOnRefresh: true,
          },
        }
      );
    }
  }

  // ─────────────────────────────────────────────────────────────────────────
  // 5. METRICS — skipped intentionally.
  // .mt-cell already transitions opacity+transform via seen-state CSS.
  // Adding GSAP transform on the same element would conflict with the CSS.
  // The existing CountUp + CSS stagger already creates a premium reveal.
  // ─────────────────────────────────────────────────────────────────────────

  // ─────────────────────────────────────────────────────────────────────────
  // 6. CTA BAND — cinematic scale entrance + wordmark subtle parallax
  // .cta-band itself is NOT a .reveal element — full freedom.
  // h2 and btns are children of inner .reveal but properties won't clash.
  // ─────────────────────────────────────────────────────────────────────────
  function animateCTA(gsap) {
    const cta = document.querySelector('.cta-band');
    if (!cta) return;

    // Entire section: scale from 0.95 → 1 on entry for cinematic feel
    gsap.fromTo(
      cta,
      { scale: 0.95 },
      {
        scale: 1,
        duration: 1.4,
        ease: 'expo.out',
        immediateRender: true,
        scrollTrigger: {
          trigger: cta,
          start: 'top 85%',
        },
      }
    );

    // h2: rises from below with slight scale.
    // Trigger at top 92% to fire ≈ same time as IntersectionObserver reveal
    // on the parent .reveal div, so the two effects layer without a gap.
    const h2 = cta.querySelector('h2');
    if (h2) {
      gsap.fromTo(
        h2,
        { y: 60, scale: 0.94 },
        {
          y: 0,
          scale: 1,
          duration: 1.3,
          ease: 'expo.out',
          immediateRender: true,
          scrollTrigger: {
            trigger: cta,
            start: 'top 92%',
          },
        }
      );
    }

    // Wordmark: subtle clockwise rotation on scroll
    const wmImg = cta.querySelector('.wordmark img');
    if (wmImg) {
      gsap.fromTo(
        wmImg,
        { rotation: -2, scale: 0.95 },
        {
          rotation: 1,
          scale: 1,
          ease: 'none',
          immediateRender: true,
          scrollTrigger: {
            trigger: cta,
            start: 'top bottom',
            end: 'bottom top',
            scrub: 2,
            invalidateOnRefresh: true,
          },
        }
      );
    }
  }

  // ─────────────────────────────────────────────────────────────────────────
  // 7. SECTION DIVIDERS / STRIP — subtle scale on the client strip
  // .strip is a separate, non-reveal element — safe to animate.
  // ─────────────────────────────────────────────────────────────────────────
  function animateStrip(gsap) {
    const strip = document.querySelector('.strip');
    if (!strip) return;

    gsap.fromTo(
      strip,
      { opacity: 0, y: 20 },
      {
        opacity: 1,
        y: 0,
        duration: 0.9,
        ease: 'power2.out',
        immediateRender: true,
        scrollTrigger: {
          trigger: strip,
          start: 'top 92%',
        },
      }
    );
  }

  // ─────────────────────────────────────────────────────────────────────────
  // 8. TESTIMONIALS — slider container subtle scale + depth
  // ─────────────────────────────────────────────────────────────────────────
  function animateTestimonials(gsap) {
    const slider = document.querySelector('.t-slider');
    if (!slider) return;

    gsap.fromTo(
      slider,
      { scale: 0.96, opacity: 0 },
      {
        scale: 1,
        opacity: 1,
        duration: 1.1,
        ease: 'power3.out',
        immediateRender: true,
        scrollTrigger: {
          trigger: slider,
          start: 'top 82%',
        },
      }
    );
  }

  // ─────────────────────────────────────────────────────────────────────────
  // 9. FAQ — skipped intentionally.
  // .faq-item lives inside .faq-list.reveal; adding a GSAP opacity/y on
  // children while the parent IO reveal also fires creates a visible race.
  // The FAQ accordion's own CSS transitions are already clean.
  // ─────────────────────────────────────────────────────────────────────────

  // ─────────────────────────────────────────────────────────────────────────
  // 10. SCROLL DEPTH EFFECTS — hero letter-spacing breathes on scroll
  // Gives the large headline kinetic life as the user starts scrolling.
  // ─────────────────────────────────────────────────────────────────────────
  function animateHeroTypography(gsap, ScrollTrigger) {
    const h1 = document.querySelector('.aurora-h1');
    if (!h1) return;

    // Letter-spacing contracts as you scroll (feels like zooming in)
    gsap.to(h1, {
      letterSpacing: '-0.05em',
      ease: 'none',
      scrollTrigger: {
        trigger: '.aurora-hero',
        start: 'top top',
        end: '45% top',
        scrub: 1.2,
      },
    });
  }

  // ─────────────────────────────────────────────────────────────────────────
  // 11. LAYERED DEPTH — sections scale slightly as they enter mid-viewport
  // Creates a subtle "coming into focus" feel between sections.
  // Targets non-reveal containers only (process, strip, metrics sections).
  // ─────────────────────────────────────────────────────────────────────────
  function animateSectionDepth(gsap, ScrollTrigger) {
    // Process section container
    const processSection = document.querySelector('.process');
    if (processSection) {
      gsap.fromTo(
        processSection,
        { scale: 0.97 },
        {
          scale: 1,
          duration: 1.2,
          ease: 'power2.out',
          immediateRender: true,
          scrollTrigger: {
            trigger: processSection,
            start: 'top 85%',
          },
        }
      );
    }
  }

  // ─────────────────────────────────────────────────────────────────────────
  // 12. WORK SECTION HEADER — wl-head slides in from left
  // .wl-head is wrapped in <Reveal> but wl-head-label and wl-head-count
  // are direct children — use x movement on those (no opacity conflict).
  // ─────────────────────────────────────────────────────────────────────────
  function animateWorkHeader(gsap) {
    const label = document.querySelector('.wl-head-label');
    const count = document.querySelector('.wl-head-count');

    if (label) {
      gsap.fromTo(label,
        { x: -30 },
        {
          x: 0, duration: 0.8, ease: 'power2.out', immediateRender: true,
          scrollTrigger: { trigger: label.closest('.wl-head') || label, start: 'top 90%' },
        }
      );
    }
    if (count) {
      gsap.fromTo(count,
        { x: 30 },
        {
          x: 0, duration: 0.8, ease: 'power2.out', immediateRender: true,
          scrollTrigger: { trigger: count.closest('.wl-head') || count, start: 'top 90%' },
        }
      );
    }
  }

  // ─────────────────────────────────────────────────────────────────────────
  // 13. PAGE HEAD — inner-page hero: eyebrow slides from left, h1 rises,
  //     deck fades in. Delay-based (no ScrollTrigger) — always in viewport.
  //     h1 uses `y` only (no opacity) so TypeReveal char animation is unaffected.
  // ─────────────────────────────────────────────────────────────────────────
  function animatePageHead(gsap) {
    const head = document.querySelector('.page-head');
    if (!head) return;

    const eyebrow = head.querySelector('.eyebrow');
    const h1      = head.querySelector('h1');
    const deck    = head.querySelector('.deck');

    if (eyebrow) {
      gsap.fromTo(eyebrow,
        { x: -24, opacity: 0 },
        { x: 0, opacity: 1, duration: 0.7, delay: 0.05, ease: 'power2.out', immediateRender: true });
    }
    if (h1) {
      gsap.fromTo(h1,
        { y: 50 },
        { y: 0, duration: 1.0, delay: 0.1, ease: 'expo.out', immediateRender: true });
    }
    if (deck) {
      gsap.fromTo(deck,
        { y: 28, opacity: 0 },
        { y: 0, opacity: 1, duration: 0.9, delay: 0.3, ease: 'power3.out', immediateRender: true });
    }
  }

  // ─────────────────────────────────────────────────────────────────────────
  // 14. CASE STUDY HERO — section scale entrance + breadcrumb slide-in.
  //     CaseStudyTitle manages its own word-reveal via IntersectionObserver;
  //     cs-deck is inside LineReveal; cs-meta cells are .reveal — all skipped.
  // ─────────────────────────────────────────────────────────────────────────
  function animateCaseStudyHero(gsap) {
    const hero = document.querySelector('.cs-hero');
    if (!hero) return;

    gsap.fromTo(hero,
      { scale: 0.97 },
      { scale: 1, duration: 1.2, ease: 'expo.out', immediateRender: true });

    const breadcrumb = hero.querySelector('.cs-breadcrumb');
    if (breadcrumb) {
      gsap.fromTo(breadcrumb,
        { x: -24, opacity: 0 },
        { x: 0, opacity: 1, duration: 0.7, delay: 0.1, ease: 'power2.out', immediateRender: true });
    }
  }

  // ─────────────────────────────────────────────────────────────────────────
  // 15. WORK GRID — .work-card clip-path wipe from bottom on scroll entry.
  //     .work-card has .reveal (opacity/transform via IO CSS), so clipPath
  //     is the only safe GSAP property to use here.
  // ─────────────────────────────────────────────────────────────────────────
  function animateWorkGrid(gsap) {
    $$('.work-card').forEach((card) => {
      gsap.fromTo(
        card,
        { clipPath: 'inset(0 0 100% 0)' },
        {
          clipPath: 'inset(0 0 0% 0)',
          duration: 0.9,
          ease: 'expo.out',
          immediateRender: true,
          scrollTrigger: {
            trigger: card,
            start: 'top 88%',
          },
        }
      );
    });
  }

  // ─────────────────────────────────────────────────────────────────────────
  // MAIN INITIALIZER — exported to window, called by App useEffect on [route]
  // ─────────────────────────────────────────────────────────────────────────
  function initGSAPAnimations() {
    // Bail on reduced motion — respect user preference
    if (prefersReducedMotion) return () => {};

    if (!ensureGSAP()) return () => {};

    const { gsap, ScrollTrigger } = window;

    // Kill any leftover ScrollTriggers from prior mount (route navigation)
    ScrollTrigger.getAll().forEach(t => t.kill());

    // Configure ScrollTrigger for performance
    ScrollTrigger.config({ limitCallbacks: true, syncInterval: 40 });

    // ── Run all animation groups ──────────────────────────────────────────
    animateHero(gsap, ScrollTrigger);           // blobs + content scroll-out
    animateHeroTypography(gsap, ScrollTrigger); // letter-spacing scrub
    animateCapabilities(gsap);                  // clip-path wipe alternating
    animateWorkItems(gsap, ScrollTrigger);      // thumb wipe + image parallax
    animateWorkHeader(gsap);                    // label/count slide from sides
    animateProcess(gsap, ScrollTrigger);        // step-num slide + accent line
    animateCTA(gsap);                           // section scale + h2 rise
    animateStrip(gsap);                         // client strip entrance
    animateTestimonials(gsap);                  // slider depth entrance
    animateSectionDepth(gsap, ScrollTrigger);   // process section scale-in

    // ── Inner page animations (silently no-op on home) ────────────────────
    animatePageHead(gsap);                      // inner page hero entrance
    animateCaseStudyHero(gsap);                 // case study hero scale + breadcrumb
    animateWorkGrid(gsap);                      // work page grid clip-path wipe

    // Recalculate positions after all triggers are set
    ScrollTrigger.refresh();

    // Cleanup function returned so App useEffect can call it on route change
    return function cleanup() {
      ScrollTrigger.getAll().forEach(t => t.kill());
    };
  }

  // Expose to React components
  window.initGSAPAnimations = initGSAPAnimations;

})(); // end module IIFE
