import { useEffect, useRef, useState } from 'react';
import { AnimatePresence, motion, useReducedMotion } from 'framer-motion';
import { Check, Loader2, ShoppingCart } from 'lucide-react';
import { cn } from '@/lib/utils';

/**
 * Add-to-cart button with an explicit confirmation state.
 *
 * The most expensive hesitation in a webshop is "did that work?". A shopper who
 * is unsure adds the item again or leaves to check, and both cost orders. This
 * button answers the question the moment it is asked: it locks while the request
 * is in flight, flips to a green check with a light sweep when the item is
 * really in the cart, then resets. The caller opens the mini cart at the same
 * time, so the eye is already travelling towards checkout.
 *
 * The state machine lives here so every caller behaves identically; the caller
 * keeps its own business rules (variation pickers, bundle errors, ...) and just
 * has to throw when the product did not land in the cart.
 */

/** How long the green "added" state stays on screen before the button resets. */
const ADDED_MS = 1600;

type Status = 'idle' | 'pending' | 'added';

interface AddToCartButtonProps {
  /** Runs the real mutation. Must throw when the item did not reach the cart. */
  onAdd: () => Promise<void>;
  label: string;
  pendingLabel?: string;
  addedLabel?: string;
  size?: 'compact' | 'large' | 'inline';
  disabled?: boolean;
  className?: string;
  'aria-label'?: string;
}

const AddToCartButton = ({
  onAdd,
  label,
  pendingLabel = label,
  addedLabel,
  size = 'large',
  disabled = false,
  className,
  'aria-label': ariaLabel,
}: AddToCartButtonProps) => {
  const [status, setStatus] = useState<Status>('idle');
  const resetTimer = useRef<number | null>(null);
  const reduced = useReducedMotion();

  useEffect(
    () => () => {
      if (resetTimer.current !== null) window.clearTimeout(resetTimer.current);
    },
    [],
  );

  const handleClick = async (event: React.MouseEvent<HTMLButtonElement>) => {
    // The card is wrapped in a <Link>; keep the click out of it.
    event.preventDefault();
    event.stopPropagation();
    if (status === 'pending' || disabled) return;

    setStatus('pending');
    try {
      await onAdd();
      setStatus('added');
      if (resetTimer.current !== null) window.clearTimeout(resetTimer.current);
      resetTimer.current = window.setTimeout(() => setStatus('idle'), ADDED_MS);
    } catch {
      // The caller already surfaced the reason; just become clickable again.
      setStatus('idle');
    }
  };

  const isAdded = status === 'added';
  const isPending = status === 'pending';

  return (
    <motion.button
      type="button"
      onClick={handleClick}
      disabled={disabled || isPending}
      aria-label={ariaLabel}
      aria-live="polite"
      whileTap={reduced ? undefined : { scale: 0.97 }}
      className={cn(
        'relative flex items-center justify-center gap-2 overflow-hidden font-bold transition-colors',
        size === 'large'
          ? 'w-full py-3.5 px-4 rounded-xl text-sm uppercase tracking-wide'
          : size === 'compact'
            ? 'w-full py-2.5 rounded-xl text-xs'
            : 'shrink-0 px-5 py-3 rounded-xl text-sm uppercase tracking-wide',
        isAdded ? 'bg-success text-success-foreground' : 'gradient-primary text-white',
        isPending ? 'cursor-wait' : 'hover:opacity-95',
        disabled && 'opacity-60 cursor-not-allowed',
        className,
      )}
    >
      <AnimatePresence initial={false} mode="wait">
        <motion.span
          key={status}
          initial={reduced ? false : { opacity: 0, y: 6 }}
          animate={{ opacity: 1, y: 0 }}
          exit={reduced ? undefined : { opacity: 0, y: -6 }}
          transition={{ duration: 0.14, ease: 'easeOut' }}
          className="relative z-10 inline-flex min-w-0 items-center justify-center gap-2"
        >
          {isPending ? (
            <Loader2 className="h-4 w-4 shrink-0 animate-spin" aria-hidden="true" />
          ) : isAdded ? (
            <Check className="h-4 w-4 shrink-0" aria-hidden="true" />
          ) : (
            <ShoppingCart className="h-4 w-4 shrink-0" aria-hidden="true" />
          )}
          <span className="truncate">{isPending ? pendingLabel : isAdded ? addedLabel ?? label : label}</span>
        </motion.span>
      </AnimatePresence>
      {/* Light sweep across the button, only at the moment the item lands. */}
      {isAdded && !reduced && <span className="pw-sheen" aria-hidden="true" />}
    </motion.button>
  );
};

export default AddToCartButton;
