import React from 'react';
import { ArrowDown, Loader2 } from 'lucide-react';
import { cn } from '@/lib/utils';
import { triggerHaptic } from '../lib/haptics';

const MAX_PULL = 96;
const TRIGGER_PULL = 72;
const DAMPING = 0.55;

type PullToRefreshProps = {
  onRefresh: () => Promise<void> | void;
  disabled?: boolean;
  className?: string;
  pullLabel?: string;
  releaseLabel?: string;
  refreshingLabel?: string;
  children: React.ReactNode;
};

export default function PullToRefresh({
  onRefresh,
  disabled = false,
  className,
  pullLabel = 'Pull to refresh',
  releaseLabel = 'Release to refresh',
  refreshingLabel = 'Refreshing…',
  children,
}: PullToRefreshProps) {
  const containerRef = React.useRef<HTMLDivElement | null>(null);
  const startYRef = React.useRef<number | null>(null);
  const pullDistanceRef = React.useRef(0);
  const readyRef = React.useRef(false);
  const [pullDistance, setPullDistance] = React.useState(0);
  const [dragging, setDragging] = React.useState(false);
  const [refreshing, setRefreshing] = React.useState(false);

  const updatePull = React.useCallback((value: number) => {
    pullDistanceRef.current = value;
    setPullDistance(value);
  }, []);

  React.useEffect(() => {
    const container = containerRef.current;
    if (!container || disabled) {
      return;
    }

    const handleStart = (event: TouchEvent) => {
      if (refreshing || window.scrollY > 0) {
        return;
      }
      startYRef.current = event.touches[0]?.clientY ?? null;
      setDragging(true);
    };

    const handleMove = (event: TouchEvent) => {
      if (refreshing || startYRef.current === null) {
        return;
      }
      if (window.scrollY > 0) {
        startYRef.current = null;
        updatePull(0);
        setDragging(false);
        return;
      }
      const currentY = event.touches[0]?.clientY ?? 0;
      const delta = currentY - startYRef.current;
      if (delta <= 0) {
        updatePull(0);
        return;
      }
      event.preventDefault();
      const next = Math.min(MAX_PULL, delta * DAMPING);
      updatePull(next);
      const isReady = next >= TRIGGER_PULL;
      if (isReady && !readyRef.current) {
        readyRef.current = true;
        triggerHaptic('selection');
      } else if (!isReady && readyRef.current) {
        readyRef.current = false;
      }
    };

    const handleEnd = async () => {
      if (startYRef.current === null) {
        return;
      }
      startYRef.current = null;
      setDragging(false);
      readyRef.current = false;

      if (pullDistanceRef.current >= TRIGGER_PULL) {
        triggerHaptic('medium');
        setRefreshing(true);
        updatePull(TRIGGER_PULL);
        try {
          await onRefresh();
        } finally {
          setRefreshing(false);
          updatePull(0);
        }
        return;
      }

      updatePull(0);
    };

    container.addEventListener('touchstart', handleStart, { passive: true });
    container.addEventListener('touchmove', handleMove, { passive: false });
    container.addEventListener('touchend', handleEnd);
    container.addEventListener('touchcancel', handleEnd);

    return () => {
      container.removeEventListener('touchstart', handleStart);
      container.removeEventListener('touchmove', handleMove);
      container.removeEventListener('touchend', handleEnd);
      container.removeEventListener('touchcancel', handleEnd);
    };
  }, [disabled, onRefresh, refreshing, updatePull]);

  const progress = Math.min(pullDistance / TRIGGER_PULL, 1);
  const ready = pullDistance >= TRIGGER_PULL;
  const indicatorLabel = refreshing
    ? refreshingLabel
    : ready
      ? releaseLabel
      : pullLabel;

  return (
    <div ref={containerRef} className={cn('relative', className)}>
      <div
        className="pointer-events-none absolute left-0 right-0 top-2 flex h-10 items-center justify-center"
        style={{
          opacity: progress,
          transform: `translateY(${Math.min(pullDistance, TRIGGER_PULL) - 48}px)`,
          transition: dragging ? 'none' : 'transform 200ms ease-out, opacity 200ms ease-out',
        }}
      >
        <div className="flex items-center gap-2 rounded-full border border-white/30 bg-white/90 px-3 py-1 text-xs font-semibold text-slate-700 shadow-sm dark:border-white/10 dark:bg-slate-900/80 dark:text-slate-100">
          {refreshing ? (
            <Loader2 className="h-4 w-4 animate-spin" aria-hidden />
          ) : (
            <ArrowDown
              className={cn('h-4 w-4 transition-transform duration-200', ready && 'rotate-180')}
              aria-hidden
            />
          )}
          <span>{indicatorLabel}</span>
        </div>
      </div>

      <div
        className={cn('will-change-transform', !dragging && 'transition-transform duration-200 ease-out')}
        style={{ transform: `translateY(${pullDistance}px)` }}
      >
        {children}
      </div>
    </div>
  );
}
