import {RefObject} from 'react';
import {
  LayoutChangeEvent,
  Pressable,
  TextInput,
  TouchableOpacity,
  View,
} from 'react-native';
import {IconPhoto, IconRefresh} from '@tabler/icons-react-native';
import Animated, {
  cancelAnimation,
  clamp,
  Extrapolation,
  interpolate,
  runOnJS,
  SharedValue,
  useAnimatedProps,
  useAnimatedStyle,
  useSharedValue,
  withTiming,
} from 'react-native-reanimated';
import {Gesture, GestureDetector} from 'react-native-gesture-handler';
import {useSafeAreaBottomPadding} from '@hooks/useSafeAreaBottomPadding';
import {useTheme} from '@shared/theme';

import {styles} from './styles';
import {CameraEngine, LensInfo} from '../CameraEngine';
import {iconRotationSV} from '../../utils';

const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
const AnimatedTextInput = Animated.createAnimatedComponent(TextInput);

const OUTER_CIRCLE_SCALE = 1.4;
const INNER_CIRCLE_SCALE = 0.9;

const ZOOM_SENSITIVITY = 0.01;

const CAPTURE_BUTTON_WIDTH = styles.captureButtonInner.width;

const LensButton: React.FC<{
  lenses: LensInfo[];
  selectedLens: string;
  zoom: SharedValue<number>;
  genOnLensPress: (
    lensInfo: LensInfo,
    zoom: SharedValue<number>,
    lenses: LensInfo[],
  ) => () => void;
}> = ({lenses, selectedLens, zoom, genOnLensPress}) => {
  const handleLensPress = () => {
    const activeLens = CameraEngine.activeLens({
      lenses: lenses,
      zoom: zoom.value,
      selectedLens,
    });
    if (!activeLens) return;
    genOnLensPress(activeLens, zoom, lenses)();
  };

  const animatedProps = useAnimatedProps(() => {
    const currentLens =
      lenses.findLast(
        lens => lens.rangeStart !== undefined && zoom.value >= lens.rangeStart,
      ) ?? lenses[0];

    const ratio = currentLens.opticalZoom / currentLens.zoomFactor;
    const userFacingZoom = zoom.value * ratio;
    const zoomString = parseFloat(userFacingZoom.toFixed(1)).toString();
    const formattedZoom = `${zoomString}x`;
    return {
      text: formattedZoom,
      defaultValue: formattedZoom,
    };
  });

  const rotationStyle = useAnimatedStyle(() => ({
    transform: [{rotate: `${iconRotationSV.value}deg`}],
  }));

  return (
    <TouchableOpacity style={styles.button} onPress={handleLensPress}>
      <Animated.View style={rotationStyle}>
        <AnimatedTextInput
          animatedProps={animatedProps}
          editable={false}
          pointerEvents="none"
          style={styles.lensText}
        />
      </Animated.View>
    </TouchableOpacity>
  );
};

export const CameraBottomControls = ({
  onOpenImagePicker,
  onControlsLayout,
  onFlip,
  onTakePhoto,
  startRecording,
  stopRecording,
  isRecordingRef,
  isRecordingSV,
  recordType,
  captureScale,
  minZoom,
  maxZoom,
  lenses,
  selectedLens,
  zoom,
  genOnLensPress,
  isFront,
}: {
  onOpenImagePicker: () => void;
  onControlsLayout: (e: LayoutChangeEvent) => void;
  onFlip: () => void;
  onTakePhoto: () => void;
  startRecording: () => void;
  stopRecording: () => void;
  isRecordingRef: RefObject<boolean>;
  isRecordingSV: SharedValue<number>;
  recordType: SharedValue<'photo' | 'video'>;
  captureScale: SharedValue<number>;
  lenses: LensInfo[];
  selectedLens: string;
  zoom: SharedValue<number>;
  minZoom: number;
  maxZoom: number;
  genOnLensPress: (
    lens: LensInfo,
    zoom: SharedValue<number>,
    lenses: LensInfo[],
  ) => () => void;
  isFront: boolean;
}) => {
  const bottom = useSafeAreaBottomPadding();
  const baseZoom = useSharedValue(1);
  const initialY = useSharedValue(0);
  const {colors} = useTheme();

  const COLOR_RED = colors.red[400];
  const COLOR_WHITE = colors.neutral;

  const isLongPressActive = useSharedValue(false);

  const cancelCaptureAnimations = () => {
    'worklet';
    cancelAnimation(captureScale);
  };

  const resetScale = () => {
    'worklet';
    captureScale.value = withTiming(1, {
      duration: 100,
    });
  };

  const iconRotationStyle = useAnimatedStyle(() => ({
    transform: [{rotate: `${iconRotationSV.value}deg`}],
  }));

  const hideButtonAnimatedStyle = useAnimatedStyle(() => ({
    opacity: withTiming(isRecordingSV.value === 1 ? 0 : 1, {
      duration: 100,
    }),
  }));

  // Hide only when recording video by holding the button
  const hideZoomButtonAnimatedStyle = useAnimatedStyle(() => ({
    opacity: interpolate(
      captureScale.value,
      [1, OUTER_CIRCLE_SCALE],
      [1, 0],
      Extrapolation.CLAMP,
    ),
  }));

  const longPressGesture = Gesture.LongPress()
    .minDuration(400)
    .shouldCancelWhenOutside(false)
    .maxDistance(1000)
    .onStart(e => {
      // Stop recording if already recording (for video mode)
      if (isRecordingSV.value === 1) {
        runOnJS(stopRecording)();
        resetScale();
        return;
      }

      isLongPressActive.value = true;
      baseZoom.value = zoom.value;
      initialY.value = e.y;

      cancelCaptureAnimations();

      captureScale.value = withTiming(OUTER_CIRCLE_SCALE, {
        duration: 300,
      });

      runOnJS(startRecording)();
    })
    .onTouchesMove(e => {
      if (!isLongPressActive.value) {
        return;
      }

      const touches = e.allTouches;
      if (!touches.length) {
        return;
      }
      const touch = e.allTouches[0];

      const deltaY = initialY.value - touch.y;

      const zoomFactor = 1 + deltaY * ZOOM_SENSITIVITY;
      const newZoom = baseZoom.value * zoomFactor;

      zoom.value = clamp(newZoom, minZoom, maxZoom);
    })
    .onEnd(() => {
      if (!isLongPressActive.value) {
        return;
      }
      isLongPressActive.value = false;
      cancelCaptureAnimations();

      resetScale();

      runOnJS(stopRecording)();
    });

  const innerButtonStyle = useAnimatedStyle(() => {
    const isVideo = recordType.value === 'video';

    const isRecording = isRecordingSV.value === 1;

    if (isVideo) {
      return {
        backgroundColor: COLOR_RED,
        transform: [{scale: clamp(captureScale.value, INNER_CIRCLE_SCALE, 1)}],
        width: interpolate(
          captureScale.value,
          [INNER_CIRCLE_SCALE, 1],
          [32, CAPTURE_BUTTON_WIDTH],
          Extrapolation.CLAMP,
        ),
        height: interpolate(
          captureScale.value,
          [INNER_CIRCLE_SCALE, 1],
          [32, CAPTURE_BUTTON_WIDTH],
          Extrapolation.CLAMP,
        ),
        borderRadius: interpolate(
          captureScale.value,
          [INNER_CIRCLE_SCALE, 1],
          [6, 100],
          Extrapolation.CLAMP,
        ),
      };
    }

    return {
      backgroundColor: isRecording ? COLOR_RED : COLOR_WHITE,
      transform: [{scale: clamp(captureScale.value, INNER_CIRCLE_SCALE, 1)}],
    };
  });

  const outerButtonStyle = useAnimatedStyle(() => {
    return {
      transform: [
        {
          scale: clamp(captureScale.value, 1, OUTER_CIRCLE_SCALE),
        },
      ],
    };
  });

  return (
    <View
      style={[styles.controlsBottom, {paddingBottom: bottom}]}
      onLayout={onControlsLayout}>
      <Animated.View
        style={[styles.controlOpenPicker, hideButtonAnimatedStyle]}>
        <TouchableOpacity
          onPress={onOpenImagePicker}
          style={styles.buttonLarge}>
          <Animated.View style={iconRotationStyle}>
            <IconPhoto size={24} color="white" />
          </Animated.View>
        </TouchableOpacity>
      </Animated.View>
      <GestureDetector gesture={longPressGesture}>
        <View style={styles.controlRecordButton}>
          <Animated.View
            style={[styles.recordButtonContainer, outerButtonStyle]}>
            <AnimatedPressable
              onPress={() => {
                if (recordType.value === 'video') {
                  if (isRecordingRef.current) {
                    stopRecording();
                    // Reset scale after stopping recording
                    resetScale();
                  } else {
                    startRecording();
                  }
                } else {
                  onTakePhoto();
                  // Reset scale after photo capture
                  resetScale();
                }
              }}
              onPressIn={() => {
                const isVideo = recordType.value === 'video';

                cancelCaptureAnimations();
                // Scale down inner button circle on press in
                captureScale.value = withTiming(INNER_CIRCLE_SCALE, {
                  duration: isVideo ? 150 : 100,
                });
              }}
              style={[styles.captureButtonOuter]}>
              <Animated.View
                style={[styles.captureButtonInner, innerButtonStyle]}
              />
            </AnimatedPressable>
          </Animated.View>
        </View>
      </GestureDetector>

      <View style={styles.controlRightBottom}>
        {lenses && lenses.length > 1 && !isFront ? (
          <Animated.View style={hideZoomButtonAnimatedStyle}>
            <LensButton
              lenses={lenses}
              selectedLens={selectedLens}
              zoom={zoom}
              genOnLensPress={genOnLensPress}
            />
          </Animated.View>
        ) : (
          <View style={styles.placeholderView} />
        )}
        <TouchableOpacity onPress={onFlip} style={styles.buttonLarge}>
          <Animated.View style={iconRotationStyle}>
            <IconRefresh size={24} color="white" />
          </Animated.View>
        </TouchableOpacity>
      </View>
    </View>
  );
};
