"use client";

import {
  useEffect,
  useRef,
  useState,
  type ElementType,
  type ReactNode,
} from "react";
import { cn } from "@/lib/utils";

export type ScrollRevealVariant = "hero" | "card" | "fade-up" | "section";

type Props = {
  children: ReactNode;
  className?: string;
  variant?: ScrollRevealVariant;
  /** Stagger delay after element enters view (ms) */
  delay?: number;
  /** Animate on mount (for above-the-fold hero content) */
  immediate?: boolean;
  as?: ElementType;
};

const hidden: Record<ScrollRevealVariant, string> = {
  hero: "opacity-0 translate-y-10 scale-[0.98]",
  card: "opacity-0 translate-y-7",
  "fade-up": "opacity-0 translate-y-8",
  section: "opacity-0 translate-y-14",
};

const shown: Record<ScrollRevealVariant, string> = {
  hero: "opacity-100 translate-y-0 scale-100",
  card: "opacity-100 translate-y-0",
  "fade-up": "opacity-100 translate-y-0",
  section: "opacity-100 translate-y-0",
};

const duration: Record<ScrollRevealVariant, string> = {
  hero: "duration-[900ms]",
  card: "duration-[650ms]",
  "fade-up": "duration-700",
  section: "duration-[800ms]",
};

export function ScrollReveal({
  children,
  className,
  variant = "fade-up",
  delay = 0,
  immediate = false,
  as: Tag = "div",
}: Props) {
  const ref = useRef<HTMLElement>(null);
  const [visible, setVisible] = useState(false);

  useEffect(() => {
    if (immediate) {
      const id = requestAnimationFrame(() => setVisible(true));
      return () => cancelAnimationFrame(id);
    }

    const el = ref.current;
    if (!el) return;

    const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
    if (mq.matches) {
      setVisible(true);
      return;
    }

    const obs = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setVisible(true);
          obs.disconnect();
        }
      },
      { threshold: 0.1, rootMargin: "0px 0px -6% 0px" }
    );

    obs.observe(el);
    return () => obs.disconnect();
  }, [immediate]);

  return (
    <Tag
      ref={ref}
      className={cn(
        "ease-[cubic-bezier(0.22,1,0.36,1)] will-change-[opacity,transform]",
        duration[variant],
        "motion-reduce:!translate-y-0 motion-reduce:!scale-100 motion-reduce:!opacity-100 motion-reduce:!transition-none",
        visible ? shown[variant] : hidden[variant],
        visible ? "transition-[opacity,transform]" : "",
        variant === "card" && "topo-card-layer relative z-20 isolate",
        className
      )}
      style={{ transitionDelay: visible ? `${delay}ms` : "0ms" }}
    >
      {children}
    </Tag>
  );
}
