"use client";
/* eslint-disable react-hooks/set-state-in-effect */

import { useEffect, useRef, useState } from "react";
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";

// Register GSAP plugins
if (typeof window !== "undefined") {
  gsap.registerPlugin(ScrollTrigger);
}

// Helper: bail out entirely if the user has asked for reduced motion.
// We check this at the top of every effect so that the motion pass is
// JS-side as well as CSS-side (CSS can only do so much — scroll-triggered
// transforms come from GSAP/JS, so we have to opt out here too).
function prefersReducedMotion(): boolean {
  if (typeof window === "undefined" || !window.matchMedia) return false;
  return window.matchMedia("(prefers-reduced-motion: reduce)").matches === true;
}

interface StickyScrollSectionProps {
  children: React.ReactNode;
  height?: string;
  className?: string;
}

export default function StickyScrollSection({
  children,
  height = "300vh",
  className = "",
}: StickyScrollSectionProps) {
  const containerRef = useRef<HTMLDivElement>(null);
  const [isStuck, setIsStuck] = useState(false);

  useEffect(() => {
    if (typeof window === "undefined" || !containerRef.current) return;
    if (prefersReducedMotion()) {
      // With reduced motion, just show the content normally — no pinning.
      setIsStuck(true);
      return;
    }

    const ctx = gsap.context(() => {
      const trigger = ScrollTrigger.create({
        trigger: containerRef.current,
        start: "top top",
        end: "bottom bottom",
        pin: true,
        pinSpacing: false,
        onToggle: (self) => {
          setIsStuck(self.isActive);
        },
      });

      return () => trigger.kill();
    }, containerRef);

    return () => ctx.revert();
  }, []);

  return (
    <div
      ref={containerRef}
      className={`relative ${className}`}
      style={{ height }}
    >
      <div
        className={`sticky top-0 h-screen overflow-hidden ${
          isStuck ? "opacity-100" : "opacity-0"
        }`}
      >
        {children}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// SCROLL-REVEAL WRAPPER - Layers content as user scrolls
// Motion-reduced: max 12px translation (was 60px), 320ms (was 1s),
// power1.out (was power3.out — no overshoot).
// ─────────────────────────────────────────────────────────────────────────────
interface ScrollRevealProps {
  children: React.ReactNode;
  className?: string;
  stagger?: boolean;
  direction?: "up" | "down" | "left" | "right" | "scale";
  from?: "up" | "down" | "left" | "right" | "scale";
  delay?: number;
}

export function ScrollReveal({
  children,
  className = "",
  stagger = false,
  direction = "up",
  from,
  delay = 0,
}: ScrollRevealProps) {
  const containerRef = useRef<HTMLDivElement>(null);
  const effectiveDirection = from ?? direction;

  useEffect(() => {
    if (typeof window === "undefined" || !containerRef.current) return;
    if (prefersReducedMotion()) return;

    const ctx = gsap.context(() => {
      const elements = containerRef.current?.querySelectorAll("[data-reveal]");

      if (!elements?.length) {
        // Single child reveal — small Y, short duration, light ease.
        gsap.fromTo(
          containerRef.current,
          { opacity: 0, y: 12 },
          {
            opacity: 1,
            y: 0,
            duration: 0.32,
            ease: "power1.out",
            delay,
            scrollTrigger: {
              trigger: containerRef.current,
              start: "top 88%",
              toggleActions: "play none none none",
            },
          }
        );
        return;
      }

      // Staggered reveal
      const animProps: gsap.TweenVars = { opacity: 0 };

      switch (effectiveDirection) {
        case "up":
          animProps.y = 12;
          break;
        case "down":
          animProps.y = -12;
          break;
        case "left":
          animProps.x = 12;
          break;
        case "right":
          animProps.x = -12;
          break;
        case "scale":
          // Drop the scale-on-reveal; pure opacity is enough.
          break;
      }

      gsap.fromTo(
        elements,
        animProps,
        {
          opacity: 1,
          x: 0,
          y: 0,
          duration: 0.32,
          stagger: stagger ? 0.05 : 0,
          ease: "power1.out",
          scrollTrigger: {
            trigger: containerRef.current,
            start: "top 88%",
            toggleActions: "play none none none",
          },
        }
      );
    }, containerRef);

    return () => ctx.revert();
  }, [effectiveDirection, stagger, delay]);

  return (
    <div ref={containerRef} className={className}>
      {children}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// PARALLAX LAYER - Depth effect on scroll
// Motion-reduced: parallax is the #1 vestibular trigger. We still render
// the children (so layout doesn't break) but skip the scroll-driven Y
// shift entirely. If callers need depth they can use color/opacity.
// ─────────────────────────────────────────────────────────────────────────────
interface ParallaxLayerProps {
  children: React.ReactNode;
  speed?: number; // kept for API compatibility — ignored when reduced motion is on
  className?: string;
  style?: React.CSSProperties;
}

export function ParallaxLayer({
  children,
  speed: _speed = 0.5,
  className = "",
  style,
}: ParallaxLayerProps) {
  const layerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (typeof window === "undefined" || !layerRef.current) return;
    if (prefersReducedMotion()) return;

    // Even with motion enabled, parallax is the worst offender. Cap the
    // maximum travel at 24px (was 150px) and only if explicitly opted in
    // via a data attribute — otherwise skip the scroll effect.
    const node = layerRef.current;
    const enabled = node.dataset.parallax === "true";
    if (!enabled) return;

    const ctx = gsap.context(() => {
      const moveDistance = 24; // hard cap, ignores speed prop for safety

      gsap.to(node, {
        y: -moveDistance,
        ease: "none",
        scrollTrigger: {
          trigger: node,
          start: "top bottom",
          end: "bottom top",
          scrub: 1,
        },
      });
    }, layerRef);

    return () => ctx.revert();
  }, []);

  return (
    <div ref={layerRef} className={className} style={style}>
      {children}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// PROGRESS INDICATOR - Shows scroll progress through section
// Motion-reduced: no scrub (so the bar doesn't continuously chase the scroll
// position); renders at 0 instead.
// ─────────────────────────────────────────────────────────────────────────────
interface ProgressIndicatorProps {
  className?: string;
  color?: string;
}

export function ProgressIndicator({
  className = "",
  color = "#10b981",
}: ProgressIndicatorProps) {
  const containerRef = useRef<HTMLDivElement>(null);
  const [progress, setProgress] = useState(0);

  useEffect(() => {
    if (typeof window === "undefined" || !containerRef.current) return;
    if (prefersReducedMotion()) return;

    const ctx = gsap.context(() => {
      ScrollTrigger.create({
        trigger: containerRef.current,
        start: "top top",
        end: "bottom bottom",
        scrub: true,
        onUpdate: (self) => {
          setProgress(self.progress);
        },
      });
    }, containerRef);

    return () => ctx.revert();
  }, []);

  return (
    <div ref={containerRef} className={`relative ${className}`}>
      <div className="sticky top-0 h-screen flex items-center justify-center">
        <div
          className="w-1 h-64 bg-white/10 rounded-full overflow-hidden"
        >
          <div
            className="w-full rounded-full"
            style={{
              height: `${progress * 100}%`,
              background: color,
              boxShadow: `0 0 20px ${color}`,
            }}
          />
        </div>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// FADE ON SCROLL - Elements that fade as user scrolls into view
// Motion-reduced: capped at 16px translation (was 100px), 320ms (was 1s),
// power1.out. toggleActions "play none none none" so the element doesn't
// re-animate when you scroll back up (less positional movement overall).
// ─────────────────────────────────────────────────────────────────────────────
interface FadeOnScrollProps {
  children: React.ReactNode;
  className?: string;
  from?: "top" | "bottom" | "left" | "right" | "up" | "down";
  to?: "top" | "bottom" | "left" | "right";
  distance?: number;
  duration?: number;
  delay?: number;
}

export function FadeOnScroll({
  children,
  className = "",
  from = "bottom",
  to,
  distance = 16,
  duration = 0.32,
  delay = 0,
}: FadeOnScrollProps) {
  const elementRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (typeof window === "undefined" || !elementRef.current) return;
    if (prefersReducedMotion()) return;

    // Cap distance regardless of caller — protects against accidental
    // 100+ px translations in the future.
    const cappedDistance = Math.min(distance, 16);
    const cappedDuration = Math.min(duration, 0.4);

    const ctx = gsap.context(() => {
      const fromProps: gsap.TweenVars = { opacity: 0 };
      let fromY = 0;
      let fromX = 0;

      switch (from) {
        case "top":
          fromY = cappedDistance;
          break;
        case "bottom":
          fromY = -cappedDistance;
          break;
        case "left":
          fromX = cappedDistance;
          break;
        case "right":
          fromX = -cappedDistance;
          break;
      }

      fromProps.y = fromY;
      fromProps.x = fromX;

      gsap.fromTo(
        elementRef.current,
        fromProps,
        {
          opacity: 1,
          x: 0,
          y: 0,
          duration: cappedDuration,
          ease: "power1.out",
          delay,
          scrollTrigger: {
            trigger: elementRef.current,
            start: "top 90%",
            toggleActions: "play none none none",
          },
        }
      );
    }, elementRef);

    return () => ctx.revert();
  }, [from, distance, duration, delay]);

  return (
    <div ref={elementRef} className={className}>
      {children}
    </div>
  );
}