import React, { memo, useEffect, useRef, useState } from "react";
import { Pressable, View } from "react-native";
import Animated, {
  Easing,
  interpolate,
  useAnimatedProps,
  useSharedValue,
  withSpring,
  withTiming,
} from "react-native-reanimated";
import {
  G,
  Path,
  Svg,
  // @ts-check
  type PathProps,
  type GProps,
} from "react-native-svg";
import type { ICheckbox, IStrokePath } from "./types";
import { BOX_PATH, PADDING, TICK_PATH, VIEWPORT_SIZE } from "./conf";

const AnimatedSvgPath = Animated.createAnimatedComponent<PathProps>(Path);
const AnimatedG = Animated.createAnimatedComponent<GProps>(G);

const StrokePath: React.FC<IStrokePath> = ({
  animValue,
  ...pathProps
}: IStrokePath): React.ReactNode & React.JSX.Element => {
  const [pathLength, setPathLength] = useState<number>(0);
  const pathRef = useRef<typeof AnimatedSvgPath>(null);

  const animatedStrokeProps = useAnimatedProps<
    Pick<PathProps, "strokeDashoffset" | "opacity">
  >(() => {
    if (pathLength === 0 || animValue.value === 0) {
      return {
        strokeDashoffset: 1,
        opacity: 0,
      };
    }
    const easedProgress = Easing.bezierFn(0.37, 0, 0.63, 1)(animValue.value);
    const offset = pathLength - pathLength * easedProgress;

    return {
      strokeDashoffset: Math.max(0, offset),
      opacity: 1,
    };
  });

  const handleLayout = () => {
    if (pathRef.current) {
      // @ts-ignore
      const totalLength = pathRef.current?.getTotalLength();
      setPathLength(totalLength);
    }
  };

  return (
    <AnimatedSvgPath
      ref={pathRef}
      onLayout={handleLayout}
      strokeDasharray={pathLength}
      animatedProps={animatedStrokeProps}
      {...pathProps}
    />
  );
};

export const Checkbox: React.FC<ICheckbox> = memo<ICheckbox>(
  ({
    checked = false,
    checkmarkColor = "#000",
    stroke = 2.5,
    size = 28,
    showBorder = false,
    onPress,
    containerStyle,
  }: ICheckbox) => {
    const animValue = useSharedValue<number>(checked ? 1 : 0);
    const borderAnimValue = useSharedValue<number>(showBorder ? 1 : 0);
    const scaleValue = useSharedValue<number>(1);
    const isFirstRender = useRef<boolean>(true);

    useEffect(() => {
      if (isFirstRender.current) {
        isFirstRender.current = false;
        animValue.value = checked ? 1 : 0;
        borderAnimValue.value = showBorder ? 1 : 0;
        scaleValue.value = 1;
        return;
      }

      animValue.value = withTiming<number>(checked ? 1 : 0, {
        duration: checked ? 300 : 250,
        easing: checked
          ? Easing.bezier(0.4, 0, 0.2, 1)
          : Easing.bezier(0.4, 0, 0.6, 1),
      });

      if (checked) {
        scaleValue.value = withSpring<number>(1, {
          damping: 10,
          stiffness: 150,
          mass: 0.5,
        });
      } else {
        scaleValue.value = withTiming<number>(1, { duration: 100 });
      }
    }, [checked, animValue, scaleValue]);

    useEffect(() => {
      if (isFirstRender.current) return;

      borderAnimValue.value = withTiming<number>(showBorder ? 1 : 0, {
        duration: 250,
        easing: showBorder
          ? Easing.bezier(0.4, 0, 0.2, 1)
          : Easing.bezier(0.4, 0, 0.6, 1),
      });
    }, [showBorder, borderAnimValue]);

    const animatedCheckmarkProps = useAnimatedProps<Pick<GProps, "transform">>(
      () => {
        const scale = interpolate(scaleValue.value, [0, 1], [0.8, 1]);
        return {
          transform: [
            { translateX: size / 2 },
            { translateY: size / 2 },
            { scale },
            { translateX: -size / 2 },
            { translateY: -size / 2 },
          ],
        };
      },
    );

    const viewBox = [
      -PADDING,
      -PADDING,
      VIEWPORT_SIZE + PADDING,
      VIEWPORT_SIZE + PADDING,
    ].join(" ");

    return (
      <Pressable onPress={onPress}>
        <View
          style={[
            {
              width: size,
              height: size,
              backgroundColor: 'rgba(255,255,255,0.12)',
              borderRadius: 8,
              justifyContent: 'center',
              alignItems: 'center',
            },
            containerStyle,
          ]}
        >
          <Svg width={size} height={size} viewBox={viewBox}>
            <StrokePath
              d={BOX_PATH}
              stroke={checkmarkColor}
              strokeWidth={stroke}
              fill="none"
              strokeLinecap="round"
              strokeLinejoin="round"
              animValue={borderAnimValue}
            />
            <AnimatedG animatedProps={animatedCheckmarkProps}>
              <StrokePath
                d={TICK_PATH}
                stroke={checkmarkColor}
                strokeWidth={stroke}
                fill="none"
                strokeLinecap="round"
                strokeLinejoin="round"
                animValue={animValue}
              />
            </AnimatedG>
          </Svg>
        </View>
      </Pressable>
    );
  },
);

export default memo<React.FC<ICheckbox>>(Checkbox);