import React, {useCallback, useEffect, memo, useRef} from 'react';
import {LayoutChangeEvent, View} from 'react-native';
import Animated, {
  Easing,
  runOnJS,
  useAnimatedStyle,
  useSharedValue,
  withTiming,
} from 'react-native-reanimated';
import {Typography} from '@components/_core/Typography';
import {Avatar} from '@components/_HuGo/Avatar';
import {CloseButton} from '@components/_HuGo/Button';
import {Link} from '@components/_HuGo/Link';
import {SnackbarVariant} from '@redux/events/interfaces';
import {SPACING, useTheme} from '@shared/theme';
import {windowDimensions} from '@shared/constants';
import {wait} from '@shared/utils';

import {getVariantValues, styles} from './styles';

const IN_ANIMATION_DURATION = 250;
const OUT_ANIMATION_DURATION = 250;
export const SNACKBAR_DURATION_WITH_ACTION = 6000;
export const SNACKBAR_DURATION = 4000;

export interface SnackbarRef {
  show: () => void;
  hide: () => void;
}

interface Props {
  title: string;
  description?: string;
  actionLabel?: string;
  onActionPress?: () => void;
  variant?: SnackbarVariant;
  onClose: () => void;
}

export function SnackbarComponent({
  title,
  description,
  actionLabel,
  onActionPress,
  onClose,
  variant = 'info',
}: Props) {
  const {theme} = useTheme();
  const {primaryColor, secondaryColor, Icon} = getVariantValues(variant, theme);
  const hidden = useRef(false);
  const animatedWidth = useSharedValue(1);
  const animatedTranslateX = useSharedValue(windowDimensions.width);
  const animatedTranslateY = useSharedValue(0);
  const animatedHeight = useSharedValue<Nullable<number>>(null);

  const hide = useCallback(() => {
    if (!hidden.current) {
      hidden.current = true;

      animatedTranslateY.value = withTiming(
        windowDimensions.height,
        {
          duration: OUT_ANIMATION_DURATION,
          easing: Easing.cubic,
        },
        () => {
          animatedHeight.value = 0;
          runOnJS(onClose)();
        },
      );
    }
  }, [animatedHeight, animatedTranslateY, onClose]);

  const onContentLayout = useCallback(
    (event: LayoutChangeEvent) => {
      // The extra px is to handle the spacing between snackbars
      const nextValue = event.nativeEvent.layout.height + SPACING.x2;
      if (animatedHeight.value === null) {
        animatedHeight.value = nextValue;
      }
    },
    [animatedHeight],
  );

  const onProgressFinish = useCallback(async () => {
    // Wait 2 second to hide the Snackbar. This is a definition of the Design team
    await wait(2000);
    hide();
  }, [hide]);

  const containerAnimatedStyle = useAnimatedStyle(() => {
    return {
      height: animatedHeight.value !== null ? animatedHeight.value : undefined,
      transform: [
        {translateX: animatedTranslateX.value},
        {translateY: animatedTranslateY.value},
      ],
    };
  });

  const progressAnimatedStyle = useAnimatedStyle(() => ({
    width: `${animatedWidth.value}%`,
  }));

  useEffect(() => {
    animatedTranslateX.value = withTiming(
      0,
      {
        duration: IN_ANIMATION_DURATION,
        easing: Easing.cubic,
      },
      () => {
        // Start the animation for the progress bar
        animatedWidth.value = withTiming(
          100,
          {
            duration: actionLabel
              ? SNACKBAR_DURATION_WITH_ACTION
              : SNACKBAR_DURATION,
            easing: Easing.linear,
          },
          () => {
            // Schedule the "out" animation
            runOnJS(onProgressFinish)();
          },
        );
      },
    );
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return (
    <Animated.View style={[styles.container, containerAnimatedStyle]}>
      <View
        style={[
          styles.content,
          {backgroundColor: theme.background.elements.inverted},
        ]}
        onLayout={onContentLayout}>
        <View style={styles.infoContainer}>
          <Avatar
            Icon={Icon}
            size="xs"
            iconColor={theme.text.neutral.inverted}
            style={{backgroundColor: primaryColor}}
          />
          <View style={styles.middleContent}>
            <View>
              <Typography weight="semiBold" color={theme.text.neutral.inverted}>
                {title}
              </Typography>
              {!!description && (
                <Typography variant="xs" color={theme.text.neutral.inverted}>
                  {description}
                </Typography>
              )}
            </View>
            {!!actionLabel && (
              <Link numberOfLines={1} onPress={onActionPress} white>
                {actionLabel}
              </Link>
            )}
          </View>
          <CloseButton
            size="sm"
            onPress={hide}
            iconColor={theme.text.neutral.inverted}
          />
        </View>
        <View
          style={[
            styles.progressBackground,
            {backgroundColor: secondaryColor},
          ]}>
          <Animated.View
            style={[
              styles.progress,
              {backgroundColor: primaryColor},
              progressAnimatedStyle,
            ]}
          />
        </View>
      </View>
    </Animated.View>
  );
}

const MemorizedSnackbar = memo(SnackbarComponent);
export {MemorizedSnackbar as Snackbar};
