import React, { memo, useEffect, useRef, useState } from "react";
import {
  StyleSheet,
  TextInput,
  View,
  Text,
  ViewStyle,
  TextStyle,
  TextInputProps,
} from "react-native";
import Animated, {
  Easing,
  useAnimatedStyle,
  useSharedValue,
  withRepeat,
  withTiming,
} from "react-native-reanimated";

export type OTPInputProps = {
  onChange?: (otp: string) => void;
  otpLength?: number;
  inputSize?: number;
  containerStyle?: ViewStyle;
  boxStyle?: ViewStyle;
  textStyle?: TextStyle;
  cursorStyle?: ViewStyle;
} & TextInputProps;

export type BoxDigitProps = {
  index: number;
  isFocused: boolean;
} & Pick<
  OTPInputProps,
  "value" | "otpLength" | "inputSize" | "boxStyle" | "textStyle" | "cursorStyle"
>;

const BoxDigit = memo(
  ({
    index,
    value,
    isFocused,
    otpLength = 0,
    inputSize,
    boxStyle,
    textStyle,
    cursorStyle,
  }: BoxDigitProps) => {
    const digit = value?.[index] || "";
    const isCurrentValue = index === value?.length;
    const isLastValue = index === otpLength - 1;
    const isCodeComplete = value?.length === otpLength;
    const isValueFocused = isCurrentValue || (isLastValue && isCodeComplete);
    const blink = useSharedValue(1);

    const animatedStyle = useAnimatedStyle(() => {
      const translateX = digit ? 10 : 0;

      return {
        opacity: blink.value,
        transform: [
          { translateY: -12 },
          {
            translateX: withTiming(translateX, {
              duration: 300,
              easing: Easing.inOut(Easing.ease),
            }),
          },
        ],
      };
    });

    useEffect(() => {
      if (isValueFocused && isFocused) {
        blink.value = withRepeat(withTiming(0, { duration: 500 }), -1, true);
      } else {
        blink.value = 1;
      }
    }, [isValueFocused, isFocused]);

    return (
      <View style={[styles.boxContainer, boxStyle]}>
        <Text
          style={[
            styles.otpInput,
            {
              width: inputSize,
              height: inputSize,
              lineHeight: inputSize,
            },
            textStyle,
          ]}
        >
          {digit}
        </Text>
        {isValueFocused && isFocused && (
          <Animated.View style={[styles.cursor, animatedStyle, cursorStyle]} />
        )}
      </View>
    );
  }
);

const OTPInput: React.FC<OTPInputProps> = ({
  value = "",
  onChange,
  otpLength = 4,
  inputSize = 60,
  containerStyle,
  boxStyle,
  textStyle,
  cursorStyle,
  ...rest
}) => {
  const inputRef = useRef<TextInput>(null);
  const [isFocused, setIsFocused] = useState(false);
  const [otp, setOtp] = useState(value);

  const handleOnPress = () => {
    setIsFocused(true);
    inputRef.current?.focus();
  };

  const handleOnBlur = () => {
    setIsFocused(false);
  };

  useEffect(() => {
    setOtp(value);
  }, [value]);

  return (
    <View
      style={[styles.otpContainer, containerStyle]}
      onTouchEndCapture={handleOnPress}
    >
      {Array.from({ length: otpLength }).map((_, index) => (
        <BoxDigit
          key={index}
          index={index}
          value={otp}
          isFocused={isFocused}
          inputSize={inputSize}
          boxStyle={boxStyle}
          textStyle={textStyle}
          cursorStyle={cursorStyle}
        />
      ))}
      <TextInput
        keyboardType={rest.keyboardType || "phone-pad"}
        value={otp}
        onChangeText={(text) => {
          const newOtp = text.slice(0, otpLength);
          setOtp(newOtp);
          onChange?.(newOtp);
        }}
        onBlur={handleOnBlur}
        ref={inputRef}
        style={[
          styles.input,
          {
            height: inputSize,
          },
        ]}
        accessibilityLabel="OTP Input"
        accessibilityHint="Enter the OTP"
        caretHidden
        autoFocus
      />
    </View>
  );
};

const styles = StyleSheet.create({
  otpContainer: {
    flexDirection: "row",
    justifyContent: "space-between",
    flexWrap: "wrap",
    gap: 6,
    paddingHorizontal: 10,
  },
  boxContainer: {
    position: "relative",
  },
  otpInput: {
    borderWidth: 1,
    borderRadius: 12,
    fontSize: 24,
    textAlign: "center",
  },
  input: {
    backgroundColor: "red",
    opacity: 0,
    width: "100%",
    position: "absolute",
    left: 0,
    top: 0,
    color: "transparent",
  },
  cursor: {
    position: "absolute",
    width: 2,
    height: 24,
    left: "50%",
    top: "50%",
    backgroundColor: "black",
  },
});

export default OTPInput;
