import {useCallback, useMemo} from 'react';
import {useCameraDevice} from 'react-native-vision-camera';
import Animated, {withSpring} from 'react-native-reanimated';

import type {SharedValue, WithSpringConfig} from 'react-native-reanimated';

Animated.addWhitelistedUIProps({
  zoom: true,
});

const LENS_SPRING: WithSpringConfig = {
  stiffness: 600,
  damping: 60,
  mass: 1,
  overshootClamping: true,
};

export type LensInfo = {
  opticalZoom: number;
  name: string;
  rangeStart: number;
  rangeEnd: number;
  zoomFactor: number;
};

export type ActiveLensOptions = {
  lenses: LensInfo[];
  zoom: number;
  selectedLens?: string;
};

export const CameraEngine = {
  activeLens: (options: ActiveLensOptions): LensInfo | undefined => {
    const {lenses, zoom} = options;
    return (
      lenses.findLast(
        lens => lens.rangeStart !== undefined && zoom >= lens.rangeStart,
      ) ?? lenses[0]
    );
  },
  useOnLensPress: (): {
    genOnLensPress: (
      lensInfo: LensInfo,
      zoom: SharedValue<number>,
      lenses: LensInfo[],
    ) => () => void;
    selectedLens: string;
  } => {
    return {
      genOnLensPress: useCallback(
        (lensInfo: LensInfo, zoom: SharedValue<number>, lenses: LensInfo[]) => {
          const currentLensIndex = lenses.findIndex(
            l => l.zoomFactor === lensInfo.zoomFactor,
          );
          const nextIndex = (currentLensIndex + 1) % lenses.length;
          const nextLens = lenses[nextIndex];

          return () => {
            zoom.value = withSpring(nextLens.zoomFactor, LENS_SPRING);
          };
        },
        [],
      ),
      selectedLens: 'wide-angle-camera',
    };
  },
  useLenses: (minZoom: number, maxZoom: number): LensInfo[] => {
    return useMemo(() => {
      const presetZoomFactors = [
        ...new Set([minZoom, 1, 2, ...(maxZoom >= 5 ? [5] : [])]),
      ].filter(z => z <= maxZoom);

      return presetZoomFactors.map((zoomFactor, index) => {
        const rangeStart =
          index === 0
            ? minZoom
            : (presetZoomFactors[index - 1] + zoomFactor) / 2;
        const rangeEnd =
          index === presetZoomFactors.length - 1
            ? maxZoom
            : (zoomFactor + presetZoomFactors[index + 1]) / 2;

        let name = 'wide-angle-camera';
        if (zoomFactor < 1) {
          name = 'ultra-wide-angle-camera';
        } else if (zoomFactor > 2) {
          name = 'telephoto-camera';
        }

        return {
          opticalZoom: zoomFactor,
          name,
          rangeStart,
          rangeEnd,
          zoomFactor,
        };
      });
    }, [minZoom, maxZoom]);
  },
  useBackDevice: () => {
    return useCameraDevice('back');
  },
};
