import { useCallback, useEffect, useRef } from "react";
import { BackHandler, ViewStyle } from "react-native";
import {
  Extrapolate,
  interpolate,
  runOnJS,
  useAnimatedGestureHandler,
  useAnimatedScrollHandler,
  useAnimatedStyle,
  withTiming,
} from "react-native-reanimated";
import { useDimensions } from "../../commons";
import { DISTANCE, RATIO } from "./configs";
import {
  AnimatedValue,
  AnimationType,
  GestureCtxType,
  LayoutType,
  ModalType,
} from "./types";

export const useStyle = (
  layout: LayoutType,
  modalType: ModalType,
  animationType: AnimationType,
  visibleValue: AnimatedValue,
  gestureX: AnimatedValue,
  gestureY: AnimatedValue
) => {
  const { width, height } = useDimensions();

  const overlayStyle = useAnimatedStyle(
    () => ({
      opacity:
        interpolate(visibleValue.value, [0, 1], [0, 1], Extrapolate.CLAMP) -
        interpolate(
          Math.abs(gestureX.value),
          [0, layout.width],
          [0, 1],
          Extrapolate.CLAMP
        ) -
        interpolate(
          Math.abs(gestureY.value),
          [0, layout.height],
          [0, 1],
          Extrapolate.CLAMP
        ),
    }),
    [layout]
  );

  const containerStyle = useAnimatedStyle(() => {
    const slide = animationType === "slide";
    const style: ViewStyle = {};
    switch (modalType) {
      case "top":
        if (slide)
          style.transform = [
            {
              translateY: interpolate(
                visibleValue.value,
                [0, 1],
                [-height, 0],
                Extrapolate.CLAMP
              ),
            },
          ];
        break;
      case "left":
        style.flexDirection = "row";
        if (slide)
          style.transform = [
            {
              translateX: interpolate(
                visibleValue.value,
                [0, 1],
                [-width, 0],
                Extrapolate.CLAMP
              ),
            },
          ];
        break;
      case "right":
        style.flexDirection = "row";
        if (slide)
          style.transform = [
            {
              translateX: interpolate(
                visibleValue.value,
                [0, 1],
                [width, 0],
                Extrapolate.CLAMP
              ),
            },
          ];
        break;
      case "bottom":
        if (slide)
          style.transform = [
            {
              translateY: interpolate(
                visibleValue.value,
                [0, 1],
                [height, 0],
                Extrapolate.CLAMP
              ),
            },
          ];
        break;
      default:
        style.alignItems = "center";
        style.justifyContent = "center";
        break;
    }
    if (animationType === "scale")
      style.transform = [
        {
          scale: interpolate(
            visibleValue.value,
            [0, 1],
            [0, 1],
            Extrapolate.CLAMP
          ),
        },
      ];
    if (animationType === "fade")
      style.opacity = interpolate(
        visibleValue.value,
        [0, 1],
        [0, 1],
        Extrapolate.CLAMP
      );
    return style;
  }, [modalType, animationType]);

  const contentStyle = useAnimatedStyle(() => {
    return {
      transform: [
        {
          translateX: gestureX.value,
        },
        {
          translateY: gestureY.value,
        },
      ],
    };
  }, []);

  return { overlayStyle, contentStyle, containerStyle };
};

export const useGesture = (
  layout: LayoutType,
  modalType: ModalType,
  gestureX: AnimatedValue,
  gestureY: AnimatedValue,
  visibleHandler?: (v: boolean) => void
) => {
  return useAnimatedGestureHandler(
    {
      onStart: (_, ctx: GestureCtxType) => {
        ctx.startX = gestureX.value;
        ctx.startY = gestureY.value;
      },
      onActive: (ev, ctx) => {
        if (modalType === "top") {
          gestureY.value = Math.max(
            -layout.height,
            Math.min(ctx.startY + ev.translationY, 0)
          );
          return;
        }
        if (modalType === "left") {
          gestureX.value = Math.max(
            -layout.width,
            Math.min(ctx.startX + ev.translationX, 0)
          );
          return;
        }
        if (modalType === "right") {
          gestureX.value = Math.max(
            0,
            Math.min(ctx.startX + ev.translationX, layout.width)
          );
          return;
        }
        if (modalType === "bottom") {
          gestureY.value = Math.max(
            0,
            Math.min(ctx.startY + ev.translationY, layout.height)
          );

          return;
        }
      },
      onEnd: () => {
        if (visibleHandler) {
          if (
            modalType === "top" &&
            gestureY.value < -Math.min(DISTANCE, layout.height * RATIO)
          ) {
            runOnJS(visibleHandler)(false);
            return;
          }
          if (
            modalType === "left" &&
            gestureX.value < -Math.min(DISTANCE, layout.width * RATIO)
          ) {
            runOnJS(visibleHandler)(false);
            return;
          }
          if (
            modalType === "right" &&
            gestureX.value > Math.min(DISTANCE, layout.width * RATIO)
          ) {
            runOnJS(visibleHandler)(false);
            return;
          }
          if (
            modalType === "bottom" &&
            gestureY.value > Math.min(DISTANCE, layout.height * RATIO)
          ) {
            runOnJS(visibleHandler)(false);
            return;
          }
        }
        gestureX.value = withTiming(0);
        gestureY.value = withTiming(0);
      },
    },
    [layout, modalType]
  );
};

export const useScroll = (
  layout: LayoutType,
  modalType: ModalType,
  gestureX: AnimatedValue,
  gestureY: AnimatedValue,
  visibleHandler?: (v: boolean) => void
) => {
  return useAnimatedScrollHandler(
    (ev) => {
      if (visibleHandler) {
        const offset = ev.contentOffset;

        if (offset.x > 0 || offset.y > 0) return;

        if (modalType === "top") {
          return;
        }
        if (modalType === "left") {
          return;
        }
        if (modalType === "right") {
          gestureX.value = Math.max(0, Math.min(-offset.x, layout.width));
          return;
        }
        if (modalType === "bottom") {
          gestureY.value = Math.max(0, Math.min(-offset.y, layout.height));
          return;
        }
      }
    },
    [layout, modalType]
  );
};

export const useTimeout = (
  duration: number
): [(cb: () => void) => void, () => void] => {
  const timeout = useRef<any>();

  const stop = useCallback(() => {
    timeout.current && clearTimeout(timeout.current);
  }, []);

  const start = useCallback((cb: () => void) => {
    stop();
    timeout.current = setTimeout(cb, duration);
  }, []);

  useEffect(() => {
    return () => {
      stop();
    };
  }, []);

  return [start, stop];
};

export const useBack = (cb: () => void) => {
  const onBack = () => {
    cb();
    return true;
  };

  useEffect(() => {
    const backHandler = BackHandler.addEventListener(
      "hardwareBackPress",
      onBack
    );
    return () => {
      backHandler.remove();
    };
  }, []);
};
