import React, {FC, useMemo, useState} from 'react';
import {LayoutChangeEvent, StyleProp, View, ViewStyle} from 'react-native';
import {IconProps} from '@tabler/icons-react-native';
import {Spinner} from '@components/_HuGo/Spinner';
import {Pressable} from '@components/_core/Pressable';
import {Typography} from '@components/_core/Typography';
import {ThemeAppearance, useTheme} from '@shared/theme';

import {getColorsByVariant} from '../../utils';
import {ButtonSize, ButtonVariant} from '../../interfaces';
import {styles} from './styles';

export interface ButtonProps {
  text: string;
  hitSlop?: number;
  onPress?: () => void;
  variant?: ButtonVariant;
  size?: ButtonSize;
  isLoading?: boolean;
  disabled?: boolean;
  testID?: string;
  IconRight?: FC<IconProps>;
  IconLeft?: FC<IconProps>;
  style?: StyleProp<ViewStyle & {underlayColor?: string | undefined}>;
  containerStyle?: StyleProp<ViewStyle>;
  appearance?: ThemeAppearance;
}

export function Button({
  text,
  hitSlop,
  isLoading,
  disabled,
  testID,
  IconLeft,
  IconRight,
  style,
  containerStyle,
  onPress,
  appearance = 'auto',
  variant = 'primary',
  size = 'lg',
}: ButtonProps) {
  const {iconSizes, theme} = useTheme();
  const [buttonWidth, setButtonWidth] = useState(0);
  const {backgroundColor, textColor, borderColor, underlayColor} =
    getColorsByVariant({
      variant,
      disabled,
      isLoading,
      appearance,
      theme,
    });
  const isSm = size === 'sm';
  const isFlat = variant === 'flat';
  const textVariant = isSm ? 'xs' : 's';
  const hasIcon = !isSm && (!!IconLeft || !!IconRight);

  const onContentLayout = (layout: LayoutChangeEvent) => {
    if (variant === 'flat') {
      setButtonWidth(layout.nativeEvent.layout.width);
    }
  };

  // This is a workaround so buttons with border have same height as buttons without border adding the extra 1 px to the padding
  const borderStyle = useMemo(() => {
    if (borderColor) {
      return styles.border;
    }

    return {
      paddingHorizontal: (styles[size].paddingHorizontal || 0) + 1,
      paddingVertical: (styles[size].paddingVertical || 0) + 1,
    };
  }, [borderColor, size]);

  const bgColor = useMemo(() => {
    if (!!style && 'backgroundColor' in style && style.backgroundColor) {
      return style.backgroundColor as string;
    }
    return backgroundColor;
  }, [backgroundColor, style]);

  const unColor = useMemo(() => {
    if (!!style && 'underlayColor' in style && style.underlayColor) {
      return style.underlayColor as string;
    }
    return underlayColor;
  }, [underlayColor, style]);

  const pressableStyle = useMemo(
    () => [
      styles.container,
      !isFlat && styles[size],
      {borderColor},
      borderStyle,
      style,
    ],
    [isFlat, size, borderColor, borderStyle, style],
  );

  return (
    <Pressable
      accessibilityRole="button"
      accessibilityState={{disabled: !!(disabled || isLoading)}}
      hitSlop={hitSlop}
      onPress={onPress}
      testID={testID}
      backgroundColor={bgColor}
      underlayColor={unColor}
      disabled={disabled || isLoading}
      containerStyle={containerStyle}
      style={pressableStyle}>
      {isLoading ? (
        <View style={[styles.spinnerContainer, {minWidth: buttonWidth}]}>
          <Spinner
            dark={variant === 'primary'}
            size={isSm ? iconSizes.x5 : iconSizes.x6}
          />
        </View>
      ) : (
        <View style={styles.container} onLayout={onContentLayout}>
          <View style={hasIcon && styles.iconContainer}>
            {!!IconLeft && <IconLeft size={iconSizes.x4} color={textColor} />}
          </View>
          <Typography
            numberOfLines={1}
            variant={textVariant}
            weight="semiBold"
            color={textColor}
            style={styles.text}>
            {text}
          </Typography>
          <View style={hasIcon && styles.iconContainer}>
            {!!IconRight && <IconRight size={iconSizes.x4} color={textColor} />}
          </View>
        </View>
      )}
    </Pressable>
  );
}
