"use client";

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

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

/* ============================================================
   Shared scroll-reveal trigger factory.

   ScrollTrigger does NOT fire `onEnter` for elements that are ALREADY inside
   the viewport when the trigger is created. On taller / larger screens more
   content sits above the fold on load, so those elements stayed stuck at
   opacity:0 (invisible). This was the root cause of "animations work on my
   laptop but break on bigger screens / other devices".

   This factory fixes it by revealing any element already intersecting the
   viewport right after creation, so behaviour is identical on every screen
   size. It also honours prefers-reduced-motion by revealing instantly.
   ============================================================ */
function createRevealTrigger(opts: {
  trigger: Element;
  start?: string;
  show: () => void;
  hide: () => void;
  triggerOnce?: boolean;
}): () => void {
  const { trigger, start = "top 88%", show, hide, triggerOnce = false } = opts;

  // Reduced motion: reveal immediately, no scroll dependency.
  if (
    typeof window !== "undefined" &&
    window.matchMedia?.("(prefers-reduced-motion: reduce)").matches
  ) {
    show();
    return () => {};
  }

  const st = ScrollTrigger.create({
    trigger,
    start,
    onEnter: (self) => {
      show();
      if (triggerOnce) self.kill();
    },
    onEnterBack: () => {
      if (!triggerOnce) show();
    },
    onLeave: () => {
      if (!triggerOnce) hide();
    },
    onLeaveBack: () => {
      if (!triggerOnce) hide();
    },
  });

  // Reveal-if-already-in-view (deferred one frame so layout is measured).
  const raf = requestAnimationFrame(() => {
    const r = trigger.getBoundingClientRect();
    const vh = window.innerHeight || document.documentElement.clientHeight;
    if (r.top < vh && r.bottom > 0) {
      show();
      if (triggerOnce) st.kill();
    }
  });

  return () => {
    cancelAnimationFrame(raf);
    st.kill();
  };
}

/* ============================================================
   1. ScrollReveal
   Fade-in + slide-up on scroll. The workhorse animation.
   ============================================================ */
interface ScrollRevealProps {
  children: React.ReactNode;
  delay?: number;
  duration?: number;
  yOffset?: number;
  xOffset?: number;
  className?: string;
  triggerOnce?: boolean;
}

export function ScrollReveal({
  children,
  delay = 0,
  duration = 0.8,
  yOffset = 40,
  xOffset = 0,
  className = "",
  triggerOnce = false,
}: ScrollRevealProps) {
  const elementRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const el = elementRef.current;
    if (!el) return;

    gsap.set(el, { y: yOffset, x: xOffset, opacity: 0 });

    const show = () =>
      gsap.to(el, {
        y: 0,
        x: 0,
        opacity: 1,
        duration,
        ease: "power3.out",
        delay,
        overwrite: "auto",
      });
    const hide = () => gsap.set(el, { y: yOffset, x: xOffset, opacity: 0 });

    return createRevealTrigger({ trigger: el, show, hide, triggerOnce });
  }, [delay, duration, yOffset, xOffset, triggerOnce]);

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

/* ============================================================
   2. RevealText
   Splits text into words or characters and reveals with slide-up mask.
   ============================================================ */
interface RevealTextProps {
  children: string;
  type?: "words" | "chars";
  delay?: number;
  duration?: number;
  className?: string;
  triggerOnce?: boolean;
}

export function RevealText({
  children,
  type = "words",
  delay = 0,
  duration = 0.85,
  className = "",
  triggerOnce = false,
}: RevealTextProps) {
  const containerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const el = containerRef.current;
    if (!el) return;

    const items = el.querySelectorAll(".reveal-item");
    if (!items.length) return;

    gsap.set(items, { y: "110%", opacity: 0 });

    const show = () =>
      gsap.to(items, {
        y: "0%",
        opacity: 1,
        duration,
        ease: "power4.out",
        stagger: type === "chars" ? 0.02 : 0.04,
        delay,
        overwrite: "auto",
      });
    const hide = () => gsap.set(items, { y: "110%", opacity: 0 });

    return createRevealTrigger({ trigger: el, show, hide, triggerOnce });
  }, [children, type, delay, duration, triggerOnce]);

  const renderContent = () => {
    if (type === "chars") {
      return children.split("").map((char, i) => (
        <span key={i} className="inline-block overflow-hidden" style={{ height: "1.2em", lineHeight: "1.2em", verticalAlign: "middle" }}>
          <span className="reveal-item inline-block">{char === " " ? "\u00A0" : char}</span>
        </span>
      ));
    }
    return children.split(" ").map((word, i) => (
      <span key={i} className="inline-block overflow-hidden" style={{ height: "1.2em", lineHeight: "1.2em", marginRight: "0.25em", verticalAlign: "middle" }}>
        <span className="reveal-item inline-block">{word}</span>
      </span>
    ));
  };

  return (
    <div ref={containerRef} className={className} style={{ display: "flex", flexWrap: "wrap" }}>
      {renderContent()}
    </div>
  );
}

/* ============================================================
   3. Magnetic
   Cursor-tracking magnetic pull on elements.
   ============================================================ */
interface MagneticProps {
  children: React.ReactElement;
  range?: number;
  strength?: number;
}

export function Magnetic({ children, range = 45, strength = 0.35 }: MagneticProps) {
  const containerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const el = containerRef.current;
    if (!el) return;
    const child = el.firstElementChild as HTMLElement;
    if (!child) return;

    const handleMouseMove = (e: MouseEvent) => {
      const rect = el.getBoundingClientRect();
      const relX = e.clientX - (rect.left + rect.width / 2);
      const relY = e.clientY - (rect.top + rect.height / 2);
      const dist = Math.sqrt(relX * relX + relY * relY);

      if (dist < range) {
        gsap.to(child, { x: relX * strength, y: relY * strength, ease: "power2.out", duration: 0.4 });
      } else {
        gsap.to(child, { x: 0, y: 0, ease: "elastic.out(1.2, 0.4)", duration: 0.8 });
      }
    };

    const handleMouseLeave = () => {
      gsap.to(child, { x: 0, y: 0, ease: "elastic.out(1.2, 0.4)", duration: 0.8 });
    };

    el.addEventListener("mousemove", handleMouseMove);
    el.addEventListener("mouseleave", handleMouseLeave);
    return () => {
      el.removeEventListener("mousemove", handleMouseMove);
      el.removeEventListener("mouseleave", handleMouseLeave);
    };
  }, [range, strength]);

  return (
    <div ref={containerRef} className="inline-block">
      {children}
    </div>
  );
}

/* ============================================================
   4. ShimmerText
   Premium shimmering gradient sweep on text.
   ============================================================ */
interface ShimmerTextProps {
  children: string;
  className?: string;
}

export function ShimmerText({ children, className = "" }: ShimmerTextProps) {
  return (
    <span
      className={`inline-block bg-clip-text text-transparent bg-[linear-gradient(110deg,#ffffff,45%,#c01227,55%,#ffffff)] bg-[length:250%_100%] animate-[shimmer-sweep_6s_infinite_linear] ${className}`}
      style={{ WebkitBackgroundClip: "text", backgroundClip: "text" }}
    >
      {children}
    </span>
  );
}

/* ============================================================
   5. StaggerChildren
   Wraps children and reveals them with staggered scroll animation.
   ============================================================ */
interface StaggerChildrenProps {
  children: React.ReactNode;
  stagger?: number;
  duration?: number;
  yOffset?: number;
  className?: string;
  triggerOnce?: boolean;
}

export function StaggerChildren({
  children,
  stagger = 0.1,
  duration = 0.7,
  yOffset = 35,
  className = "",
  triggerOnce = false,
}: StaggerChildrenProps) {
  const containerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const el = containerRef.current;
    if (!el) return;

    const items = el.children;
    if (!items.length) return;

    gsap.set(items, { y: yOffset, opacity: 0 });

    const show = () =>
      gsap.to(items, {
        y: 0,
        opacity: 1,
        duration,
        ease: "power3.out",
        stagger,
        overwrite: "auto",
      });
    const hide = () => gsap.set(items, { y: yOffset, opacity: 0 });

    return createRevealTrigger({ trigger: el, start: "top 85%", show, hide, triggerOnce });
  }, [stagger, duration, yOffset, triggerOnce]);

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

/* ============================================================
   6. ScaleReveal
   Scales from 0.85 → 1.0 + fades in on scroll. Great for cards/images.
   ============================================================ */
interface ScaleRevealProps {
  children: React.ReactNode;
  delay?: number;
  duration?: number;
  className?: string;
  triggerOnce?: boolean;
}

export function ScaleReveal({
  children,
  delay = 0,
  duration = 0.8,
  className = "",
  triggerOnce = false,
}: ScaleRevealProps) {
  const elementRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const el = elementRef.current;
    if (!el) return;

    gsap.set(el, { scale: 0.85, opacity: 0 });

    const show = () =>
      gsap.to(el, {
        scale: 1,
        opacity: 1,
        duration,
        ease: "power3.out",
        delay,
        overwrite: "auto",
      });
    const hide = () => gsap.set(el, { scale: 0.85, opacity: 0 });

    return createRevealTrigger({ trigger: el, show, hide, triggerOnce });
  }, [delay, duration, triggerOnce]);

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

/* ============================================================
   7. SlideReveal
   Slides from left or right with smooth reveal.
   ============================================================ */
interface SlideRevealProps {
  children: React.ReactNode;
  direction?: "left" | "right";
  delay?: number;
  duration?: number;
  className?: string;
  triggerOnce?: boolean;
}

export function SlideReveal({
  children,
  direction = "left",
  delay = 0,
  duration = 0.9,
  className = "",
  triggerOnce = false,
}: SlideRevealProps) {
  const elementRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const el = elementRef.current;
    if (!el) return;

    const xStart = direction === "left" ? -60 : 60;
    gsap.set(el, { x: xStart, opacity: 0 });

    const show = () =>
      gsap.to(el, {
        x: 0,
        opacity: 1,
        duration,
        ease: "power3.out",
        delay,
        overwrite: "auto",
      });
    const hide = () => gsap.set(el, { x: xStart, opacity: 0 });

    return createRevealTrigger({ trigger: el, show, hide, triggerOnce });
  }, [direction, delay, duration, triggerOnce]);

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

/* ============================================================
   8. ParallaxSection
   Subtle parallax depth on scroll for images/backgrounds.
   ============================================================ */
interface ParallaxSectionProps {
  children: React.ReactNode;
  speed?: number;
  className?: string;
}

export function ParallaxSection({
  children,
  speed = 0.15,
  className = "",
}: ParallaxSectionProps) {
  const elementRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const el = elementRef.current;
    if (!el) return;

    // Check for reduced motion preference
    if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;

    gsap.to(el, {
      y: () => -ScrollTrigger.maxScroll(window) * speed * 0.1,
      ease: "none",
      scrollTrigger: {
        trigger: el,
        start: "top bottom",
        end: "bottom top",
        scrub: 1.5,
        invalidateOnRefresh: true,
      },
    });

    return () => {
      ScrollTrigger.getAll().forEach((t) => {
        if (t.trigger === el) t.kill();
      });
    };
  }, [speed]);

  return (
    <div ref={elementRef} className={className} style={{ willChange: "transform" }}>
      {children}
    </div>
  );
}

/* ============================================================
   9. CountUp
   Animated number counter that triggers on scroll.
   ============================================================ */
interface CountUpProps {
  end: number;
  start?: number;
  duration?: number;
  decimals?: number;
  suffix?: string;
  prefix?: string;
  className?: string;
  triggerOnce?: boolean;
}

export function CountUp({
  end,
  start = 0,
  duration = 2,
  decimals = 0,
  suffix = "",
  prefix = "",
  className = "",
  triggerOnce = false,
}: CountUpProps) {
  const [value, setValue] = useState(start);
  const elementRef = useRef<HTMLSpanElement>(null);

  useEffect(() => {
    const el = elementRef.current;
    if (!el) return;

    const show = () => {
      const obj = { val: start };
      gsap.to(obj, {
        val: end,
        duration,
        ease: "power2.out",
        onUpdate: () => setValue(obj.val),
      });
    };
    const hide = () => setValue(start);

    return createRevealTrigger({ trigger: el, start: "top 90%", show, hide, triggerOnce });
  }, [start, end, duration, triggerOnce]);

  return (
    <span ref={elementRef} className={className}>
      {prefix}{value.toFixed(decimals)}{suffix}
    </span>
  );
}

/* ============================================================
   10. Tilt3D
   Subtle 3D perspective tilt following mouse on hover.
   ============================================================ */
interface Tilt3DProps {
  children: React.ReactNode;
  intensity?: number;
  className?: string;
}

export function Tilt3D({
  children,
  intensity = 8,
  className = "",
}: Tilt3DProps) {
  const containerRef = useRef<HTMLDivElement>(null);

  const handleMouseMove = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
    const el = containerRef.current;
    if (!el) return;

    const rect = el.getBoundingClientRect();
    const x = (e.clientX - rect.left) / rect.width - 0.5;
    const y = (e.clientY - rect.top) / rect.height - 0.5;

    gsap.to(el, {
      rotateY: x * intensity,
      rotateX: -y * intensity,
      duration: 0.5,
      ease: "power2.out",
    });
  }, [intensity]);

  const handleMouseLeave = useCallback(() => {
    const el = containerRef.current;
    if (!el) return;

    gsap.to(el, {
      rotateY: 0,
      rotateX: 0,
      duration: 0.8,
      ease: "elastic.out(1, 0.5)",
    });
  }, []);

  return (
    <div style={{ perspective: "1000px" }}>
      <div
        ref={containerRef}
        className={className}
        onMouseMove={handleMouseMove}
        onMouseLeave={handleMouseLeave}
        style={{ transformStyle: "preserve-3d", willChange: "transform" }}
      >
        {children}
      </div>
    </div>
  );
}
