import {useCallback, useEffect, useRef} from 'react';
import type {VideoRef} from '@components/_core/RNVideo';

import {
  FROZEN_PLAYBACK_END_TOLERANCE_SECONDS,
  FROZEN_PLAYBACK_PROGRESS_TOLERANCE_SECONDS,
  FROZEN_PLAYBACK_TIMEOUT_MS,
} from '../constants';
import {VideoStatus} from '../interfaces';

interface UseFrozenPlaybackRecoveryParams {
  currentVideoTime: number;
  currentVideoTimeRef: React.RefObject<number>;
  initDuration: number;
  isInFullScreen: boolean;
  isLoaded: boolean;
  player: React.RefObject<VideoRef | null>;
  videoStatus: VideoStatus;
  videoUri?: string;
}

export function useFrozenPlaybackRecovery({
  currentVideoTime,
  currentVideoTimeRef,
  initDuration,
  isInFullScreen,
  isLoaded,
  player,
  videoStatus,
  videoUri,
}: UseFrozenPlaybackRecoveryParams) {
  const frozenPlaybackTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(
    null,
  );
  const frozenPlaybackBaselineRef = useRef<number | null>(null);
  const hasRecoveredFrozenPlaybackRef = useRef(false);

  const isAtVideoEnd =
    initDuration > 0 &&
    currentVideoTimeRef.current >=
      initDuration - FROZEN_PLAYBACK_END_TOLERANCE_SECONDS;

  const clearFrozenPlaybackTimeout = useCallback(() => {
    if (frozenPlaybackTimeoutRef.current) {
      clearTimeout(frozenPlaybackTimeoutRef.current);
      frozenPlaybackTimeoutRef.current = null;
    }
  }, []);

  const recoverFrozenPlayback = useCallback(() => {
    if (
      !videoUri ||
      !isLoaded ||
      isInFullScreen ||
      videoStatus === VideoStatus.Finished ||
      isAtVideoEnd
    ) {
      return;
    }

    hasRecoveredFrozenPlaybackRef.current = true;
    clearFrozenPlaybackTimeout();
    player.current?.reload();
  }, [
    clearFrozenPlaybackTimeout,
    isAtVideoEnd,
    isInFullScreen,
    isLoaded,
    player,
    videoStatus,
    videoUri,
  ]);

  useEffect(() => {
    if (
      videoStatus !== VideoStatus.Playing ||
      !isLoaded ||
      !videoUri ||
      isInFullScreen ||
      isAtVideoEnd
    ) {
      clearFrozenPlaybackTimeout();
      frozenPlaybackBaselineRef.current = null;
      if (videoStatus !== VideoStatus.Playing) {
        hasRecoveredFrozenPlaybackRef.current = false;
      }
      return;
    }

    if (hasRecoveredFrozenPlaybackRef.current) {
      return;
    }

    const baselineTime = currentVideoTimeRef.current;
    frozenPlaybackBaselineRef.current = baselineTime;
    clearFrozenPlaybackTimeout();
    frozenPlaybackTimeoutRef.current = setTimeout(() => {
      const now = currentVideoTimeRef.current;
      if (
        Math.abs(now - baselineTime) <
        FROZEN_PLAYBACK_PROGRESS_TOLERANCE_SECONDS
      ) {
        recoverFrozenPlayback();
      }
    }, FROZEN_PLAYBACK_TIMEOUT_MS);

    return clearFrozenPlaybackTimeout;
  }, [
    clearFrozenPlaybackTimeout,
    currentVideoTime,
    isAtVideoEnd,
    isInFullScreen,
    isLoaded,
    currentVideoTimeRef,
    recoverFrozenPlayback,
    videoStatus,
    videoUri,
  ]);

  useEffect(() => clearFrozenPlaybackTimeout, [clearFrozenPlaybackTimeout]);

  const handleProgress = useCallback(
    (time: number) => {
      currentVideoTimeRef.current = time;

      if (
        videoStatus === VideoStatus.Playing &&
        frozenPlaybackBaselineRef.current !== null &&
        time >
          frozenPlaybackBaselineRef.current +
            FROZEN_PLAYBACK_PROGRESS_TOLERANCE_SECONDS
      ) {
        clearFrozenPlaybackTimeout();
        frozenPlaybackBaselineRef.current = null;
        hasRecoveredFrozenPlaybackRef.current = false;
      }
    },
    [clearFrozenPlaybackTimeout, currentVideoTimeRef, videoStatus],
  );

  const handleEnd = useCallback(() => {
    clearFrozenPlaybackTimeout();
    frozenPlaybackBaselineRef.current = null;
    hasRecoveredFrozenPlaybackRef.current = false;
    currentVideoTimeRef.current = initDuration;
  }, [clearFrozenPlaybackTimeout, currentVideoTimeRef, initDuration]);

  const handleReplay = useCallback(() => {
    currentVideoTimeRef.current = 0;
    frozenPlaybackBaselineRef.current = null;
    hasRecoveredFrozenPlaybackRef.current = false;
  }, [currentVideoTimeRef]);

  const handleFinishedSeek = useCallback(
    (time: number) => {
      currentVideoTimeRef.current = time;
      frozenPlaybackBaselineRef.current = null;
      hasRecoveredFrozenPlaybackRef.current = false;
    },
    [currentVideoTimeRef],
  );

  return {
    handleEnd,
    handleFinishedSeek,
    handleProgress,
    handleReplay,
    recoverFrozenPlayback,
  };
}
