import {useCallback, useEffect, useRef, useState} from 'react';
import {Platform} from 'react-native';
import {
  type OnLoadData,
  type OnPlaybackRateChangeData,
  type OnProgressData,
  type VideoRef,
} from '@components/_core/RNVideo';
import {VideoState} from '@components/_custom/VideoPlayerControls';

import {VideoStatus} from '../interfaces';

interface UseVideoPlaybackStateParams {
  currentVideoTimeRef: React.RefObject<number>;
  hasDeleteAction?: boolean;
  isFocused: boolean;
  isVisible: boolean;
  onEnd?: () => void;
  player: React.RefObject<VideoRef | null>;
  uri?: string;
  withAutoPlay?: boolean;
}

export function useVideoPlaybackState({
  currentVideoTimeRef,
  hasDeleteAction,
  isFocused,
  isVisible,
  onEnd,
  player,
  uri,
  withAutoPlay,
}: UseVideoPlaybackStateParams) {
  const [videoStatus, setVideoStatus] = useState(VideoStatus.Loading);
  const [initDuration, setInitDuration] = useState(0);
  const [videoHeight, setVideoHeight] = useState(0);
  const [videoDuration, setVideoDuration] = useState(0);
  const [currentVideoTime, setCurrentVideoTime] = useState(0);
  const [isInFullScreen, setIsInFullscreen] = useState(false);
  const [isLoaded, setIsLoaded] = useState(false);
  const [videoUri, setVideoUri] = useState<string>();

  const shouldPlay = !!(isVisible && withAutoPlay && isFocused);
  const videoStatusRef = useRef(videoStatus);
  videoStatusRef.current = videoStatus;

  useEffect(() => {
    if (isLoaded) {
      if (isInFullScreen) {
        return;
      }
      if (videoStatusRef.current !== VideoStatus.Finished) {
        setVideoStatus(shouldPlay ? VideoStatus.Playing : VideoStatus.Paused);
      }
      if (!shouldPlay) {
        player.current?.pause();
      }
    } else if (uri && isVisible && isFocused) {
      setVideoUri(uri);
    }
  }, [
    isFocused,
    isInFullScreen,
    isLoaded,
    isVisible,
    player,
    shouldPlay,
    uri,
    withAutoPlay,
  ]);

  useEffect(() => {
    if (isInFullScreen) {
      player.current?.presentFullscreenPlayer();
      setVideoStatus(VideoStatus.Playing);
    } else {
      player.current?.dismissFullscreenPlayer();
    }
  }, [isInFullScreen, player]);

  const showFullScreen = useCallback(() => {
    if (videoStatus === VideoStatus.Loading || hasDeleteAction) {
      return;
    }
    setIsInFullscreen(true);
  }, [hasDeleteAction, videoStatus]);

  const onFullscreenPlayerDidDismiss = useCallback(() => {
    setIsInFullscreen(false);
    if (videoStatus === VideoStatus.Playing) {
      player.current?.resume();
    }
  }, [player, videoStatus]);

  const onPlaybackRateChange = useCallback(
    ({playbackRate}: OnPlaybackRateChangeData) => {
      if (playbackRate === 0 && isInFullScreen) {
        setVideoStatus(VideoStatus.Paused);
      } else if (playbackRate > 0 && isInFullScreen) {
        setVideoStatus(VideoStatus.Playing);
      }
    },
    [isInFullScreen],
  );

  const onVideoLoad = useCallback(({duration, videoTracks}: OnLoadData) => {
    setInitDuration(duration);
    setVideoDuration(duration);
    setIsLoaded(true);
    if (Platform.OS === 'android') {
      setVideoHeight(videoTracks?.[0]?.height ?? 0);
    }
  }, []);

  const onUpdateVideoProgress = useCallback(
    ({currentTime}: OnProgressData) => {
      currentVideoTimeRef.current = currentTime;
      setVideoDuration(initDuration - currentTime);
      setCurrentVideoTime(currentTime);
    },
    [currentVideoTimeRef, initDuration],
  );

  const onVideoEnd = useCallback(() => {
    currentVideoTimeRef.current = initDuration;
    if (!isInFullScreen) {
      setVideoStatus(VideoStatus.Finished);
    }
    onEnd?.();
  }, [currentVideoTimeRef, initDuration, isInFullScreen, onEnd]);

  const onReplayPress = useCallback(() => {
    player.current?.seek(0, 0);
    player.current?.resume();
    currentVideoTimeRef.current = 0;
    setVideoStatus(VideoStatus.Playing);
    setCurrentVideoTime(0);
    setVideoDuration(initDuration);
  }, [currentVideoTimeRef, initDuration, player]);

  const onUpdateVideoState = useCallback(
    (updates: Partial<VideoState>) => {
      setVideoStatus(updates.status ?? videoStatus);
      setInitDuration(updates.duration ?? initDuration);
      setIsInFullscreen(updates.isInFullScreen ?? isInFullScreen);
      setCurrentVideoTime(updates.currentTime ?? currentVideoTime);
    },
    [currentVideoTime, initDuration, isInFullScreen, videoStatus],
  );

  const onSeekTimePress = useCallback(
    (time: number) => {
      player.current?.seek(time);
      if (videoStatus === VideoStatus.Finished) {
        player.current?.resume();
        currentVideoTimeRef.current = time;
        setVideoStatus(VideoStatus.Playing);
      }
    },
    [currentVideoTimeRef, player, videoStatus],
  );

  return {
    currentVideoTime,
    initDuration,
    isInFullScreen,
    isLoaded,
    onFullscreenPlayerDidDismiss,
    onPlaybackRateChange,
    onReplayPress,
    onSeekTimePress,
    onUpdateVideoProgress,
    onUpdateVideoState,
    onVideoEnd,
    onVideoLoad,
    showFullScreen,
    videoDuration,
    videoHeight,
    videoStatus,
    videoUri,
  };
}
