import type { ReactElement } from 'react';
import { Modal, Pressable, StyleSheet, View } from 'react-native';
import { Image } from 'expo-image';
import { useVideoPlayer, VideoView } from 'expo-video';
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import Animated, {
  useAnimatedStyle,
  useSharedValue,
  withTiming,
} from 'react-native-reanimated';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { Icon } from '@/components';

export interface ViewerSource {
  url: string;
  isVideo: boolean;
}

interface Props {
  source: ViewerSource | null;
  onClose: () => void;
}

const ZoomableImage = ({ url }: { url: string }): ReactElement => {
  const scale = useSharedValue(1);
  const savedScale = useSharedValue(1);
  const translateX = useSharedValue(0);
  const translateY = useSharedValue(0);

  const pinch = Gesture.Pinch()
    .onUpdate((event) => {
      scale.value = Math.max(1, savedScale.value * event.scale);
    })
    .onEnd(() => {
      savedScale.value = scale.value;
      if (scale.value <= 1) {
        translateX.value = withTiming(0);
        translateY.value = withTiming(0);
      }
    });
  const pan = Gesture.Pan()
    .onUpdate((event) => {
      if (scale.value > 1) {
        translateX.value = event.translationX;
        translateY.value = event.translationY;
      }
    });
  const composed = Gesture.Simultaneous(pinch, pan);

  const animatedStyle = useAnimatedStyle(() => ({
    transform: [
      { translateX: translateX.value },
      { translateY: translateY.value },
      { scale: scale.value },
    ],
  }));

  return (
    <GestureDetector gesture={composed}>
      <Animated.View style={[styles.fill, animatedStyle]}>
        <Image source={{ uri: url }} style={styles.fill} contentFit="contain" />
      </Animated.View>
    </GestureDetector>
  );
};

const ViewerVideo = ({ url }: { url: string }): ReactElement => {
  const player = useVideoPlayer({ uri: url }, (instance) => {
    instance.play();
  });
  return <VideoView player={player} style={styles.fill} contentFit="contain" />;
};

// Full-screen image (pinch-zoom) / video overlay opened from a media bubble.
export const MediaViewer = ({ source, onClose }: Props): ReactElement => {
  const insets = useSafeAreaInsets();
  return (
    <Modal visible={source !== null} transparent animationType="fade" onRequestClose={onClose}>
      <View style={styles.backdrop}>
        {source !== null ? (
          source.isVideo ? (
            <ViewerVideo url={source.url} />
          ) : (
            <ZoomableImage url={source.url} />
          )
        ) : null}
        <Pressable style={[styles.close, { top: insets.top + 8 }]} onPress={onClose} hitSlop={8}>
          <Icon name="close" size={26} color="#fff" />
        </Pressable>
      </View>
    </Modal>
  );
};

const styles = StyleSheet.create({
  backdrop: { flex: 1, backgroundColor: '#000' },
  fill: { flex: 1, width: '100%', height: '100%' },
  close: {
    position: 'absolute',
    right: 16,
    width: 40,
    height: 40,
    borderRadius: 20,
    backgroundColor: 'rgba(0,0,0,0.5)',
    alignItems: 'center',
    justifyContent: 'center',
  },
});
