import LinearGradient from 'react-native-linear-gradient';
import React, {
  useEffect,
  useMemo,
  useState,
  useCallback,
  useRef,
} from 'react';
import type { LayoutChangeEvent, ViewProps } from 'react-native';
import { Animated, Easing, Platform, StyleSheet } from 'react-native';
import type { Theme } from '../../theme';
import { useTheme } from '../../theme';
import { StyledContainer, StyledGradientContainer } from './StyledSkeleton';

interface SkeletonProps extends ViewProps {
  /**
   * Intent of the component.
   */
  intent?: 'light' | 'dark';
  /**
   * Variant of the component.
   */
  variant?: 'circular' | 'rectangular' | 'rounded';
}

const AnimatedLinearGradient = Animated.createAnimatedComponent(LinearGradient);

const gradientPositions = {
  start: { x: 0, y: 0 },
  end: { x: 1, y: 0 },
};

const getGradientColors = (theme: Theme, intent: 'light' | 'dark') => {
  switch (intent) {
    case 'light': {
      return [
        theme.__hd__.skeleton.colors.lightGradientStart,
        theme.__hd__.skeleton.colors.lightGradientEnd,
        theme.__hd__.skeleton.colors.lightGradientStart,
      ];
    }
    case 'dark': {
      return [
        theme.__hd__.skeleton.colors.darkGradientStart,
        theme.__hd__.skeleton.colors.darkGradientEnd,
        theme.__hd__.skeleton.colors.darkGradientStart,
      ];
    }
  }
};

const Skeleton = ({
  intent = 'light',
  variant = 'rounded',
  style,
  onLayout,
  ...props
}: SkeletonProps) => {
  const theme = useTheme();
  const colors = useMemo(
    () => getGradientColors(theme, intent),
    [theme, intent]
  );
  const [containerWidth, setContainerWidth] = useState(
    Number(StyleSheet.flatten(style)?.width) || 0
  );

  const [shouldStartAnimation, setShouldStartAnimation] = useState(false);

  const animatedValue = useRef(new Animated.Value(0)).current;

  useEffect(() => {
    if (shouldStartAnimation) {
      Animated.loop(
        Animated.timing(animatedValue, {
          toValue: 1,
          duration: 1000,
          easing: Easing.linear,
          useNativeDriver: Platform.OS === 'ios' || Platform.OS === 'android',
        })
      ).start();
    }
  }, [shouldStartAnimation]);

  const translateX = animatedValue.interpolate({
    inputRange: [0, 1],
    outputRange: [-containerWidth, containerWidth],
  });

  const onContainerLayout = useCallback((e: LayoutChangeEvent) => {
    const { width } = e.nativeEvent.layout;
    setContainerWidth(width);

    if (!shouldStartAnimation) {
      setShouldStartAnimation(true);
    }

    onLayout?.(e);
  }, []);

  return (
    <StyledContainer
      style={style}
      themeVariant={variant}
      themeIntent={intent}
      onLayout={onContainerLayout}
      {...props}
    >
      <StyledGradientContainer themeVariant={variant}>
        <AnimatedLinearGradient
          start={gradientPositions.start}
          end={gradientPositions.end}
          style={{
            width: '100%',
            height: '100%',
            transform: [{ translateX }],
          }}
          colors={colors}
        />
      </StyledGradientContainer>
    </StyledContainer>
  );
};

export default Skeleton;
