import React, {memo, useCallback, useRef, useState} from 'react';
import {Pressable, View} from 'react-native';
import {Image} from 'expo-image';
import {useIsFocused} from '@react-navigation/native';
import InViewPort from '@ilyahas/react-native-inviewport';
import RNVideo, {
  IgnoreSilentSwitchType,
  type ISO639_1,
  ResizeMode,
  SelectedTrackType,
  TextTrackType,
  type VideoRef,
} from '@components/_core/RNVideo';
import {Skeleton} from '@components/_HuGo/Skeleton';
import DeleteButton from '@components/DeleteButton';
import {VideoPlayerControls} from '@components/_custom/VideoPlayerControls';
import {PHONE_LANGUAGE} from '@config/i18n';
import {useLandscapeMode} from '@hooks/useLandscapeMode';
import {useGetAuthHeaders} from '@hooks/useGetAuthHeaders';
import {useAppStateChange} from '@hooks/useAppStateChange';
import {useHasActiveCall} from '@modules/calls/store/selectors';
import {useAppMuted} from '@redux/selectors';
import {windowDimensions} from '@shared/constants';
import {isAndroid, isIos} from '@shared/utils';

import {DurationLabel} from './components/DurationLabel';
import {PlayButton} from './components/PlayButton';
import {ReplayButton} from './components/ReplayButton';
import {VolumeButton} from './components/VolumeButton';
import {BUFFER_CONFIG, ASPECT_RATIO_16_9} from './constants';
import {styles} from './styles';
import {VideoProps, VideoStatus} from './interfaces';
import {useFrozenPlaybackRecovery} from './hooks/useFrozenPlaybackRecovery';
import {useVideoPlaybackState} from './hooks/useVideoPlaybackState';

function Video({
  doNotRepeat,
  enableRotation = true,
  handleDeleteVideo,
  isVisible = true,
  onEnd,
  onLongPress,
  sending,
  smallWidth,
  style,
  thumbnailUrl,
  video,
  width = windowDimensions.width,
  withAutoPlay,
  withSmallPlayButton,
  videoStyle,
  withCustomControls = false,
  /**
   * This is to force `muted` global state and handle it by prop
   **/
  forceMuted,
  onMutedToggle,
  withAuthHeader,
  showTimeLine = true,
}: VideoProps) {
  const player = useRef<VideoRef>(null);
  const currentVideoTimeRef = useRef(0);
  const mutedVideos = useAppMuted();
  const isFocused = useIsFocused();
  const hasActiveCall = useHasActiveCall();

  const uri = video.path || video.url || video.source;
  const injectAuthHeader = withAuthHeader && uri?.startsWith('http');
  const authHeaders = useGetAuthHeaders(injectAuthHeader);
  const thumbnail = thumbnailUrl || video.thumbnailUrl;

  const {
    currentVideoTime,
    initDuration,
    isInFullScreen,
    isLoaded,
    onFullscreenPlayerDidDismiss,
    onPlaybackRateChange,
    onReplayPress,
    onSeekTimePress,
    onUpdateVideoProgress,
    onUpdateVideoState,
    onVideoEnd,
    onVideoLoad,
    showFullScreen,
    videoDuration,
    videoHeight,
    videoStatus,
    videoUri,
  } = useVideoPlaybackState({
    currentVideoTimeRef,
    hasDeleteAction: !!handleDeleteVideo,
    isFocused,
    isVisible,
    onEnd,
    player,
    uri,
    withAutoPlay,
  });

  const {
    handleEnd,
    handleFinishedSeek,
    handleProgress,
    handleReplay,
    recoverFrozenPlayback,
  } = useFrozenPlaybackRecovery({
    currentVideoTime,
    currentVideoTimeRef,
    initDuration,
    isInFullScreen,
    isLoaded,
    player,
    videoStatus,
    videoUri,
  });

  useLandscapeMode(enableRotation && isInFullScreen);

  const onChangeToActive = useCallback(() => {
    if (videoStatus !== VideoStatus.Playing) {
      return;
    }

    if (isIos && hasActiveCall && !isInFullScreen) {
      recoverFrozenPlayback();
      return;
    }

    player.current?.resume();
  }, [hasActiveCall, isInFullScreen, recoverFrozenPlayback, videoStatus]);

  const onChangeToBackground = useCallback(() => {
    player.current?.pause();
  }, []);

  useAppStateChange({
    onChangeToActive,
    onChangeToBackground,
  });

  const onDeletePress = useCallback(() => {
    handleDeleteVideo!(video);
  }, [handleDeleteVideo, video]);

  const onVideoProgress = useCallback(
    (progress: Parameters<typeof onUpdateVideoProgress>[0]) => {
      handleProgress(progress.currentTime);
      onUpdateVideoProgress(progress);
    },
    [handleProgress, onUpdateVideoProgress],
  );

  const onHandledVideoEnd = useCallback(() => {
    handleEnd();
    onVideoEnd();
  }, [handleEnd, onVideoEnd]);

  const onHandledReplayPress = useCallback(() => {
    handleReplay();
    onReplayPress();
  }, [handleReplay, onReplayPress]);

  const onHandledSeekTimePress = useCallback(
    (time: number) => {
      if (videoStatus === VideoStatus.Finished) {
        handleFinishedSeek(time);
      }
      onSeekTimePress(time);
    },
    [handleFinishedSeek, onSeekTimePress, videoStatus],
  );

  return (
    <View
      style={[
        styles.container,
        {...(smallWidth && {height: smallWidth - 20, ...styles.marginRight})},
        {width: smallWidth || width},
        style,
      ]}>
      {!isLoaded && !!videoUri && !thumbnail ? (
        <Skeleton style={styles.skeletonContainer}>
          <Skeleton.Item height="100%" width="100%" />
        </Skeleton>
      ) : !isLoaded && !videoUri && thumbnail ? (
        <Image
          source={{uri: thumbnail}}
          style={styles.video}
          contentFit="contain"
        />
      ) : null}
      {!!videoUri && (
        <RNVideo
          ref={player}
          source={{
            uri: videoUri,
            bufferConfig: BUFFER_CONFIG,
            textTracks: video.videoCaptionUrl
              ? [
                  {
                    title: 'Captions',
                    uri: video.videoCaptionUrl,
                    language: PHONE_LANGUAGE as ISO639_1,
                    type: TextTrackType.VTT,
                  },
                ]
              : undefined,
            headers: injectAuthHeader && authHeaders ? authHeaders : undefined,
          }}
          paused={videoStatus !== VideoStatus.Playing}
          onLoad={onVideoLoad}
          onPlaybackRateChange={onPlaybackRateChange}
          onProgress={onVideoProgress}
          onEnd={onHandledVideoEnd}
          onFullscreenPlayerDidDismiss={onFullscreenPlayerDidDismiss}
          resizeMode={ResizeMode.CONTAIN}
          muted={
            forceMuted !== undefined
              ? forceMuted
              : !isInFullScreen && mutedVideos
          }
          ignoreSilentSwitch={IgnoreSilentSwitchType.IGNORE}
          repeat={isInFullScreen && !doNotRepeat}
          poster={thumbnail}
          style={[styles.video, videoStyle]}
          controls={isAndroid && isInFullScreen}
          subtitleStyle={{
            ...styles.subtitleStyles,
            ...(isInFullScreen && {
              paddingBottom:
                videoHeight === ASPECT_RATIO_16_9.height
                  ? windowDimensions.height * 0.2
                  : windowDimensions.height * 0.7,
            }),
          }}
          selectedTextTrack={
            isIos && video.videoCaptionUrl
              ? {
                  type: SelectedTrackType.TITLE,
                  value: 'Captions',
                }
              : undefined
          }
        />
      )}
      <Pressable
        disabled={
          sending ||
          videoStatus === VideoStatus.Loading ||
          !isLoaded ||
          !!handleDeleteVideo
        }
        onLongPress={onLongPress}
        onPress={!withCustomControls ? showFullScreen : undefined}
        style={styles.pressableOverlay}
      />
      {!withCustomControls && (
        <>
          <VolumeButton
            show={withAutoPlay}
            onMutedToggle={onMutedToggle}
            muted={forceMuted}
          />
          <DurationLabel
            duration={videoDuration}
            show={videoStatus === VideoStatus.Playing}
          />
          <ReplayButton
            onPress={onHandledReplayPress}
            show={videoStatus === VideoStatus.Finished}
          />
          <PlayButton
            disabled={!!handleDeleteVideo}
            isSmall={withSmallPlayButton || !!smallWidth}
            onPress={showFullScreen}
            show={
              !withAutoPlay && videoStatus === VideoStatus.Paused && !sending
            }
          />
        </>
      )}
      {withCustomControls && !withSmallPlayButton && (
        <VideoPlayerControls
          videoState={{
            currentTime: currentVideoTime,
            duration: initDuration,
            status: videoStatus,
            isInFullScreen,
          }}
          onUpdateVideoState={onUpdateVideoState}
          onSeekTime={onHandledSeekTimePress}
          onReplay={onHandledReplayPress}
          onFullscreen={showFullScreen}
          showTimeLine={showTimeLine}
        />
      )}
      {!!handleDeleteVideo && (
        <DeleteButton
          onPress={onDeletePress}
          right={smallWidth ? 13 : 10}
          top={smallWidth ? 5 : 10}
        />
      )}
    </View>
  );
}

function VideoLegacy(props: VideoProps) {
  const [isVisible, setIsVisible] = useState(false);
  return (
    <InViewPort onChange={setIsVisible}>
      <Video {...props} isVisible={isVisible} withAutoPlay />
    </InViewPort>
  );
}

// From now on, use the `isVisible` prop to determine if the video should be played or not
function VideoWrapper({isVisible, withAutoPlay, ...rest}: VideoProps) {
  if (isVisible === undefined && withAutoPlay) {
    return <VideoLegacy {...rest} />;
  }
  return <Video {...rest} isVisible={isVisible} withAutoPlay={withAutoPlay} />;
}

const MemorizedVideo = memo(VideoWrapper);
export {MemorizedVideo as Video};
