import {
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
  type MutableRefObject,
  type RefObject,
} from 'react';

interface UseReversePlaybackOptions {
  videoRef: RefObject<HTMLVideoElement | null>;
  duration: number;
  onComplete: () => void;
  preloadedUrls?: Set<string>;
  videoUrl?: string;
}

interface UseReversePlaybackResult {
  startReverse: () => Promise<void>;
  stopReverse: () => void;
  isReversing: boolean;
  canUseNativeReverse: boolean;
}

// Feature detection for native reverse playback (Chrome 141+, Safari 16+)
function checkNativeReverseSupport(): boolean {
  if (typeof window === 'undefined') return false;
  try {
    const video = document.createElement('video');
    video.playbackRate = -1;
    return video.playbackRate === -1;
  } catch {
    return false;
  }
}

// Native playbackRate = -1 (Chrome 141+, Safari 16+)
async function startNativeReverse(
  video: HTMLVideoElement,
  duration: number,
  onComplete: () => void
): Promise<void> {
  return new Promise((resolve) => {
    // Seek to end
    video.currentTime = duration;
    video.playbackRate = -1;

    const cleanup = () => {
      video.removeEventListener('timeupdate', onTimeUpdate);
      video.playbackRate = 1;
    };

    const onTimeUpdate = () => {
      if (video.currentTime <= 0.05) {
        cleanup();
        video.pause();
        video.currentTime = 0;
        onComplete();
        resolve();
      }
    };

    video.addEventListener('timeupdate', onTimeUpdate);

    video.play().catch(() => {
      // Fallback if native reverse fails
      cleanup();
      resolve();
    });
  });
}

// When video is already cached, seeking to end is instant
async function startPreloadedReverse(
  video: HTMLVideoElement,
  duration: number,
  onComplete: () => void,
  intervalRef: MutableRefObject<number | null>
): Promise<void> {
  return new Promise((resolve) => {
    video.pause();
    video.currentTime = duration; // Instant seek (video is cached)

    // Use 30 fps for smoother reverse when video is preloaded
    const fps = 30;
    const stepSize = 1 / fps;

    intervalRef.current = window.setInterval(() => {
      const newTime = video.currentTime - stepSize;

      if (newTime <= 0) {
        if (intervalRef.current) {
          clearInterval(intervalRef.current);
          intervalRef.current = null;
        }
        video.currentTime = 0;
        onComplete();
        resolve();
      } else {
        video.currentTime = newTime;
      }
    }, 1000 / fps);
  });
}

// For non-preloaded videos, wait for canplaythrough then reverse
async function startBufferedReverse(
  video: HTMLVideoElement,
  duration: number,
  onComplete: () => void,
  intervalRef: MutableRefObject<number | null>
): Promise<void> {
  return new Promise((resolve) => {
    const startFrameStepping = () => {
      video.pause();
      video.currentTime = duration;

      // Lower fps for non-cached videos (less seeking overhead)
      const fps = 15;
      const stepSize = 1 / fps;

      intervalRef.current = window.setInterval(() => {
        const newTime = video.currentTime - stepSize;

        if (newTime <= 0) {
          if (intervalRef.current) {
            clearInterval(intervalRef.current);
            intervalRef.current = null;
          }
          video.currentTime = 0;
          onComplete();
          resolve();
        } else {
          video.currentTime = newTime;
        }
      }, 1000 / fps);
    };

    // Check if video is already buffered enough
    const isFullyBuffered =
      video.readyState >= 4 ||
      (video.buffered.length > 0 &&
        video.buffered.end(video.buffered.length - 1) >= duration - 0.1);

    if (isFullyBuffered) {
      startFrameStepping();
      return;
    }

    // Wait for buffering to complete
    const onCanPlayThrough = () => {
      video.removeEventListener('canplaythrough', onCanPlayThrough);
      startFrameStepping();
    };

    video.addEventListener('canplaythrough', onCanPlayThrough);

    // Also try seeking to end to force buffering
    video.currentTime = duration;
    video.play().catch(() => {
      // If play fails, just wait for canplaythrough
    });

    // Timeout fallback - if canplaythrough doesn't fire within 3s, start anyway
    setTimeout(() => {
      video.removeEventListener('canplaythrough', onCanPlayThrough);
      if (!intervalRef.current) {
        startFrameStepping();
      }
    }, 3000);
  });
}

export function useReversePlayback(
  options: UseReversePlaybackOptions
): UseReversePlaybackResult {
  const [isReversing, setIsReversing] = useState(false);
  const intervalRef = useRef<number | null>(null);

  // Feature detection for native reverse
  const canUseNativeReverse = useMemo(() => checkNativeReverseSupport(), []);

  const stopReverse = useCallback(() => {
    if (intervalRef.current) {
      clearInterval(intervalRef.current);
      intervalRef.current = null;
    }
    setIsReversing(false);

    // Reset playback rate if it was modified
    const video = options.videoRef.current;
    if (video && video.playbackRate !== 1) {
      video.playbackRate = 1;
    }
  }, [options.videoRef]);

  const startReverse = useCallback(async () => {
    const video = options.videoRef.current;
    if (!video) return;

    // Stop any existing reverse playback
    stopReverse();

    setIsReversing(true);

    const { duration, onComplete, preloadedUrls, videoUrl } = options;

    // Strategy 1: Native playbackRate = -1 (smoothest)
    if (canUseNativeReverse) {
      await startNativeReverse(video, duration, () => {
        setIsReversing(false);
        onComplete();
      });
      return;
    }

    // Strategy 2: Check if video is preloaded (instant seek)
    const isPreloaded = videoUrl && preloadedUrls?.has(videoUrl);
    if (isPreloaded) {
      await startPreloadedReverse(video, duration, () => {
        setIsReversing(false);
        onComplete();
      }, intervalRef);
      return;
    }

    // Strategy 3: Wait for buffering then reverse
    await startBufferedReverse(video, duration, () => {
      setIsReversing(false);
      onComplete();
    }, intervalRef);
  }, [canUseNativeReverse, options, stopReverse]);

  // Cleanup on unmount
  useEffect(() => () => stopReverse(), [stopReverse]);

  return { startReverse, stopReverse, isReversing, canUseNativeReverse };
}
