"use client";

import { motion, type Variants } from "framer-motion";
import type { ReactNode, CSSProperties } from "react";

/* Mirrors --ease-out in globals.css. The previous [0.25,0.1,0.25,1] is
   bit-for-bit the CSS `ease` keyword — the browser default wearing a name. */
const EASE = [0.16, 1, 0.3, 1] as const;

/* ──────────────────────────────────────────────
   Shared viewport config - trigger once when
   20% of the element enters the viewport
   ────────────────────────────────────────────── */
// 0.4 rather than 0.2: at first contact every section fires at once and the
// page reads as continuously reassembling. Half-visible is the honest trigger.
const viewport = { once: true, amount: 0.4 } as const;

/* ──────────────────────────────────────────────
   Variant presets
   ────────────────────────────────────────────── */
const fadeUp: Variants = {
  hidden: { opacity: 0, y: 10 },
  visible: { opacity: 1, y: 0 },
};

const fadeIn: Variants = {
  hidden: { opacity: 0 },
  visible: { opacity: 1 },
};

const scaleIn: Variants = {
  hidden: { opacity: 0, scale: 0.92 },
  visible: { opacity: 1, scale: 1 },
};

const slideLeft: Variants = {
  hidden: { opacity: 0, x: 40 },
  visible: { opacity: 1, x: 0 },
};

const slideRight: Variants = {
  hidden: { opacity: 0, x: -40 },
  visible: { opacity: 1, x: 0 },
};

const VARIANTS = { fadeUp, fadeIn, scaleIn, slideLeft, slideRight } as const;

type VariantName = keyof typeof VARIANTS;

/* ──────────────────────────────────────────────
   FadeUp - most common: element fades in + slides
   up on scroll. Supports stagger delay.
   ────────────────────────────────────────────── */
interface FadeUpProps {
  children: ReactNode;
  delay?: number;
  duration?: number;
  className?: string;
  style?: CSSProperties;
  as?: keyof typeof motion;
  variant?: VariantName;
  immediate?: boolean;
}

export function FadeUp({
  children,
  delay = 0,
  duration = 0.4,
  className,
  style,
  as = "div",
  variant = "fadeUp",
  immediate = false,
}: FadeUpProps) {
  const Tag = motion[as] as typeof motion.div;
  return (
    <Tag
      variants={VARIANTS[variant]}
      initial="hidden"
      {...(immediate
        ? { animate: "visible" }
        : { whileInView: "visible", viewport })}
      transition={{ duration, delay, ease: EASE }}
      className={className}
      style={style}
    >
      {children}
    </Tag>
  );
}

/* ──────────────────────────────────────────────
   StaggerContainer-— wraps children to stagger
   their entrance. Each direct child should be a
   motion element using `variants`.
   ────────────────────────────────────────────── */
interface StaggerProps {
  children: ReactNode;
  stagger?: number;
  className?: string;
  style?: CSSProperties;
  as?: keyof typeof motion;
}

export function Stagger({
  children,
  stagger = 0.05,
  className,
  style,
  as = "div",
}: StaggerProps) {
  const Tag = motion[as] as typeof motion.div;
  return (
    <Tag
      initial="hidden"
      whileInView="visible"
      viewport={viewport}
      transition={{ staggerChildren: stagger }}
      className={className}
      style={style}
    >
      {children}
    </Tag>
  );
}

/* ──────────────────────────────────────────────
   StaggerItem - used inside Stagger container
   ────────────────────────────────────────────── */
interface StaggerItemProps {
  children: ReactNode;
  className?: string;
  style?: CSSProperties;
  as?: keyof typeof motion;
  variant?: VariantName;
}

export function StaggerItem({
  children,
  className,
  style,
  as = "div",
  variant = "fadeUp",
}: StaggerItemProps) {
  const Tag = motion[as] as typeof motion.div;
  return (
    <Tag
      variants={VARIANTS[variant]}
      transition={{ duration: 0.32, ease: EASE }}
      className={className}
      style={style}
    >
      {children}
    </Tag>
  );
}
