import {useState, useCallback, useRef} from 'react';
import {
  Image,
  Modal,
  TouchableOpacity,
  View,
  Pressable,
  Text,
  useWindowDimensions,
} from 'react-native';
import {useTranslation} from 'react-i18next';
import {TextInput} from 'react-native-gesture-handler';
import Animated, {
  useSharedValue,
  useAnimatedProps,
  useAnimatedStyle,
  withTiming,
} from 'react-native-reanimated';
import {IconX} from '@tabler/icons-react-native';
import {
  SafeAreaProvider,
  useSafeAreaInsets,
} from 'react-native-safe-area-context';
import {PortalHost} from '@gorhom/portal';
import {DismissKeyboard, SnackbarStack} from '@components';
import {
  RNVideo as Video,
  type OnLoadData,
  type OnProgressData,
  type VideoRef,
} from '@components/_core/RNVideo';
import {IconPlayVideo, IconPauseVideo} from '@modules/chats/components';
import {SPACING} from '@shared/theme';
import {isIos} from '@shared/utils';

import {styles, PLAY_PAUSE_BUTTON_SIZE} from './styles';

Animated.addWhitelistedNativeProps({text: true});
const AnimatedTextInput = Animated.createAnimatedComponent(TextInput);

interface CameraPreviewModalProps {
  previewVisible: boolean;
  onRetake: () => void;
  onAccept: (caption: string) => void;
  photoUri: string;
  isVideo: boolean;
  imageRotation?: number;
}

const MODAL_SUPPORTED_ORIENTATIONS: ('portrait' | 'landscape')[] = [
  'portrait',
  'landscape',
];
const CameraPreviewModalContent = ({
  onRetake,
  onAccept,
  photoUri,
  isVideo,
  imageRotation = 0,
}: Omit<CameraPreviewModalProps, 'previewVisible'>) => {
  const shouldSwapImageSize = isIos && Math.abs(imageRotation) === 90;
  const {t} = useTranslation();
  const {top, bottom, left} = useSafeAreaInsets();
  const {width, height} = useWindowDimensions();

  const safeTop = Math.max(top, SPACING.x1);
  const safeLeft = Math.max(left, SPACING.x1);
  const videoRef = useRef<VideoRef>(null);

  const [isPlaying, setIsPlaying] = useState(false);
  const [showControls, setShowControls] = useState(true);

  const durationSV = useSharedValue(0);
  const currentTimeSV = useSharedValue(0);
  const progressPercentSV = useSharedValue(0);
  const isPlayingSV = useSharedValue(0);

  const handleLoad = useCallback(
    (data: OnLoadData) => {
      durationSV.value = data.duration;
    },
    [durationSV],
  );

  const handleProgress = useCallback(
    (data: OnProgressData) => {
      currentTimeSV.value = data.currentTime;
      if (durationSV.value > 0) {
        progressPercentSV.value = withTiming(
          (data.currentTime / durationSV.value) * 100,
          {duration: 10},
        );
      }
    },
    [currentTimeSV, durationSV, progressPercentSV],
  );

  const handleEnd = useCallback(() => {
    setIsPlaying(false);
    isPlayingSV.value = 0;
    setShowControls(true);
    videoRef.current?.seek(0);
  }, [isPlayingSV]);

  const handlePlayPause = useCallback(() => {
    setIsPlaying(prev => {
      const nextIsPlaying = !prev;
      isPlayingSV.value = nextIsPlaying ? 1 : 0;
      if (nextIsPlaying && currentTimeSV.value >= durationSV.value) {
        videoRef.current?.seek(0);
        currentTimeSV.value = 0;
        progressPercentSV.value = 0;
      }
      return nextIsPlaying;
    });
    setShowControls(true);
  }, [currentTimeSV, durationSV, progressPercentSV, isPlayingSV]);

  const handleVideoPress = useCallback(() => {
    if (isPlaying) {
      setShowControls(prev => !prev);
    }
  }, [isPlaying]);

  const animatedDurationProps = useAnimatedProps(() => {
    const timeValue =
      isPlayingSV.value === 1 ? currentTimeSV.value : durationSV.value;
    const minutes = Math.floor(timeValue / 60);
    const seconds = Math.floor(timeValue % 60);
    const formattedTime = `${minutes.toString().padStart(2, '0')}:${seconds
      .toString()
      .padStart(2, '0')}`;
    return {
      text: formattedTime,
      value: formattedTime,
    };
  });

  const animatedRemainingTimeProps = useAnimatedProps(() => {
    const remainingTime = Math.max(0, durationSV.value - currentTimeSV.value);
    const minutes = Math.floor(remainingTime / 60);
    const seconds = Math.floor(remainingTime % 60);
    const formattedTime = `-${minutes.toString().padStart(2, '0')}:${seconds
      .toString()
      .padStart(2, '0')}`;
    return {
      text: formattedTime,
      value: formattedTime,
    };
  });

  const animatedProgressStyle = useAnimatedStyle(() => ({
    width: `${progressPercentSV.value}%`,
  }));

  /** Workaround for iOS preview rotation issue
   * Rotate the image preview if needed to match the orientation of the captured photo,
   * since iOS camera output may not be auto-rotated based on EXIF data.
   */
  const PreviewContent = isIos ? (
    <View style={styles.center}>
      <Image
        source={{uri: photoUri}}
        style={[
          styles.previewImage,
          {
            width: shouldSwapImageSize ? height : width,
            height: shouldSwapImageSize ? width : height,
            transform: [{rotate: `${imageRotation}deg`}],
          },
        ]}
        resizeMode="contain"
      />
    </View>
  ) : (
    <Image
      source={{uri: photoUri}}
      style={[styles.previewImage, {width, height}]}
      resizeMode="contain"
    />
  );

  return (
    <DismissKeyboard style={styles.flex}>
      <TouchableOpacity
        onPress={onRetake}
        style={[styles.button, {top: safeTop, left: safeLeft}]}>
        <IconX size={24} color="white" />
      </TouchableOpacity>
      <View style={styles.previewContainer}>
        {photoUri ? (
          isVideo ? (
            <>
              <Pressable
                onPress={handleVideoPress}
                style={[styles.previewImage, {width, height}]}>
                <Video
                  ref={videoRef}
                  source={{uri: photoUri}}
                  style={[styles.previewImage, {width, height}]}
                  resizeMode="contain"
                  controls={false}
                  paused={!isPlaying}
                  onLoad={handleLoad}
                  onProgress={handleProgress}
                  onEnd={handleEnd}
                />
              </Pressable>

              {!isPlaying && (
                <AnimatedTextInput
                  editable={false}
                  pointerEvents="none"
                  animatedProps={animatedDurationProps}
                  style={[styles.videoDurationText, {top: safeTop + 5}]}
                />
              )}

              {(!isPlaying || showControls) && (
                <Pressable
                  onPress={handlePlayPause}
                  style={[
                    styles.playPauseButton,
                    {
                      left: (width - PLAY_PAUSE_BUTTON_SIZE) / 2,
                      top: (height - PLAY_PAUSE_BUTTON_SIZE) / 2,
                    },
                  ]}>
                  <View style={styles.playPauseCircle}>
                    {isPlaying ? (
                      <IconPauseVideo size="lg" />
                    ) : (
                      <IconPlayVideo size="lg" />
                    )}
                  </View>
                </Pressable>
              )}

              <View style={[styles.seekerContainer, {bottom: bottom + 80}]}>
                <View style={styles.seekerTrack}>
                  <Animated.View
                    style={[styles.seekerProgress, animatedProgressStyle]}
                  />
                </View>
                <AnimatedTextInput
                  editable={false}
                  pointerEvents="none"
                  animatedProps={animatedRemainingTimeProps}
                  style={styles.remainingTimeText}
                />
              </View>
            </>
          ) : (
            PreviewContent
          )
        ) : (
          <View style={styles.center}>
            <Text>Preview unavailable</Text>
          </View>
        )}

        <View
          style={[
            styles.bottomBar,
            {paddingBottom: Math.max(bottom, SPACING.x1)},
          ]}>
          <TouchableOpacity onPress={onRetake} style={styles.bottomButton}>
            <Text style={styles.bottomButtonText}>{t('chat.camera.back')}</Text>
          </TouchableOpacity>
          <TouchableOpacity
            onPress={() => onAccept('')}
            style={styles.bottomButton}>
            <Text style={styles.bottomButtonText}>{t('chat.camera.next')}</Text>
          </TouchableOpacity>
        </View>
      </View>
    </DismissKeyboard>
  );
};

export const CameraPreviewModal = ({
  previewVisible,
  onRetake,
  onAccept,
  photoUri,
  isVideo,
  imageRotation = 0,
}: CameraPreviewModalProps) => {
  return (
    <Modal
      visible={previewVisible}
      animationType="slide"
      transparent={false}
      navigationBarTranslucent
      statusBarTranslucent
      supportedOrientations={MODAL_SUPPORTED_ORIENTATIONS}
      onRequestClose={onRetake}>
      <SafeAreaProvider>
        <CameraPreviewModalContent
          onRetake={onRetake}
          onAccept={onAccept}
          photoUri={photoUri}
          isVideo={isVideo}
          imageRotation={imageRotation}
        />
      </SafeAreaProvider>
      <SnackbarStack hostName="camera-preview-modal" />
      <PortalHost name="camera-preview-modal" />
    </Modal>
  );
};
