import type { ReactElement, ReactNode } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import type { StyleProp, ViewStyle } from 'react-native';
import type { Theme } from '../../theme';
import { useTheme } from '../../theme';
import { useDeprecation } from '../../utils/hooks';
import type { IconName } from '../Icon';
import LoadingIndicator from './LoadingIndicator';
import type { Intent, ThemeVariant, CamelCase } from './StyledButton';
import {
  StyledButtonContainer,
  StyledButtonIcon,
  StyledButtonIconWrapper,
  StyledButtonText,
  StyledButtonTitleOfVariantText,
  StyledSmallButtonText,
} from './StyledButton';

/**
 * @deprecated Use 'primary' | 'secondary' | 'danger' | 'inverted' instead.
 */
type DeprecatedIntent = 'white';

type ValidIntent = 'primary' | 'secondary' | 'danger' | 'inverted';

export interface ButtonProps {
  /**
   * Helps users understand what will happen when they perform an action on the accessibility element when that result is not clear from the accessibility label.
   */
  accessibilityHint?: string;
  /**
   * A succinct label in a localized string that identifies the accessibility element
   */
  accessibilityLabel?: string;
  /**
   * Disable state of button.
   */
  disabled?: boolean;
  /**
   * Places an icon within the button, before the button's text
   */
  icon?: IconName | ReactNode;
  /**
   * Visual intent color to apply to button. It is required for `filled`, `outlined` and `text` variants.
   *
   * ⚠️ 'white' intent is deprecated and will be removed in the next major release. Please use 'primary' | 'secondary' | 'danger' | 'inverted' instead.
   */
  intent?: ValidIntent | DeprecatedIntent;
  /**
   * Loading state of button.
   */
  loading?: boolean;
  /**
   * Set the handler to handle press event.
   */
  onPress: () => void;
  /**
   * Places an icon within the button, after the button's text
   */
  rightIcon?: IconName | ReactNode;
  /**
   * Additional style.
   */
  style?: StyleProp<ViewStyle>;
  /**
   * Testing id of the component.
   */
  testID?: string;
  /**
   * Button label.
   */
  text?: ReactNode;
  /**
   * Button type.
   */
  variant?:
    | 'filled'
    | 'outlined'
    | 'text'
    | 'inline-text'
    | 'filled-medium'
    | 'outlined-medium'
    | 'text-medium'
    | 'filled-compact'
    | 'outlined-compact'
    | 'text-compact'
    | 'inline-text-compact';
}

const isIconName = (icon: IconName | ReactNode): icon is IconName => {
  return typeof icon === 'string';
};

const FILLED_VARIANTS = {
  primary: 'filled-primary',
  secondary: 'filled-secondary',
  danger: 'filled-danger',
  white: 'filled-white',
  inverted: 'filled-inverted',
} as const;

const OUTLINED_VARIANTS = {
  primary: 'outlined-primary',
  secondary: 'outlined-secondary',
  danger: 'outlined-danger',
  white: 'outlined-white',
  inverted: 'outlined-inverted',
} as const;

const TEXT_VARIANTS = {
  primary: 'text-primary',
  secondary: 'text-secondary',
  danger: 'text-danger',
  white: 'text-white',
  inverted: 'text-inverted',
} as const;

export const getThemeVariant = (
  variant:
    | 'filled'
    | 'outlined'
    | 'text'
    | 'filled-medium'
    | 'outlined-medium'
    | 'text-medium'
    | 'filled-compact'
    | 'outlined-compact'
    | 'text-compact',
  intent: Intent
): ThemeVariant => {
  switch (variant) {
    case 'filled':
    case 'filled-medium':
    case 'filled-compact':
      return FILLED_VARIANTS[intent];
    case 'outlined':
    case 'outlined-medium':
    case 'outlined-compact':
      return OUTLINED_VARIANTS[intent];
    case 'text':
    case 'text-medium':
    case 'text-compact':
      return TEXT_VARIANTS[intent];
  }
};
const getUnderlayColor = (theme: Theme, themeVariant: ThemeVariant) => {
  const colorKey = themeVariant.replace(/-([a-z])/g, (_, letter) => {
    return letter.toUpperCase();
  }) as CamelCase<ThemeVariant>;

  return theme.__hd__.button.colors.pressedBackground[colorKey];
};

const deprecatedVariants: ThemeVariant[] = [
  'filled-secondary',
  'filled-danger',
  'outlined-secondary',
  'outlined-danger',
];

function isTextVariant(
  themeVariant: ThemeVariant
): themeVariant is
  | 'text-primary'
  | 'text-secondary'
  | 'text-danger'
  | 'text-white'
  | 'text-inverted' {
  return [
    'text-primary',
    'text-secondary',
    'text-danger',
    'text-white',
    'text-inverted',
  ].includes(themeVariant);
}

const Button = ({
  accessibilityHint,
  accessibilityLabel,
  disabled = false,
  icon,
  intent = 'primary',
  loading = false,
  onPress,
  rightIcon,
  style,
  testID,
  text,
  variant = 'filled',
}: ButtonProps): ReactElement => {
  const isInlineText =
    variant === 'inline-text' || variant === 'inline-text-compact';
  const isIconButtonOnly = !text;

  const themeVariant = getThemeVariant(isInlineText ? 'text' : variant, intent);
  const theme = useTheme();
  const underlayColor = useMemo(() => {
    return isInlineText ? 'transparent' : getUnderlayColor(theme, themeVariant);
  }, [theme, themeVariant, isInlineText]);
  const [isPressed, setIsPressed] = useState(false);
  const handlePress = useCallback(() => onPress(), [onPress]);

  useDeprecation(
    `Button variant ${deprecatedVariants.join(', ')} are deprecated.`,
    deprecatedVariants.includes(themeVariant)
  );

  const isCompactVariant = [
    'filled-compact',
    'outlined-compact',
    'text-compact',
    'inline-text-compact',
  ].includes(variant);

  const isMediumVariant = [
    'filled-medium',
    'outlined-medium',
    'text-medium',
  ].includes(variant);

  const isRenderTextVariant = isTextVariant(themeVariant);

  const renderTextVariantTitle = () => {
    if (!isRenderTextVariant || isIconButtonOnly) return null;

    return (
      <StyledButtonTitleOfVariantText
        variant={isCompactVariant ? 'small-bold' : 'regular-bold'}
        ellipsizeMode="tail"
        numberOfLines={1}
        disabled={disabled}
        themeButtonVariant={themeVariant}
        themeIsPressed={isPressed}
        themeIsCompact={isCompactVariant}
      >
        {text}
      </StyledButtonTitleOfVariantText>
    );
  };

  const renderTitle = () => {
    if (isIconButtonOnly) return null;

    if (isCompactVariant) {
      return (
        <StyledSmallButtonText
          variant="small-bold"
          ellipsizeMode="tail"
          numberOfLines={1}
          disabled={disabled}
          themeButtonVariant={themeVariant}
        >
          {text}
        </StyledSmallButtonText>
      );
    }

    if (isMediumVariant) {
      return (
        <StyledSmallButtonText
          variant="regular-bold"
          ellipsizeMode="tail"
          numberOfLines={1}
          disabled={disabled}
          themeButtonVariant={themeVariant}
        >
          {text}
        </StyledSmallButtonText>
      );
    }

    return (
      <StyledButtonText
        level="h5"
        ellipsizeMode="tail"
        numberOfLines={1}
        disabled={disabled}
        themeButtonVariant={themeVariant}
      >
        {text}
      </StyledButtonText>
    );
  };

  return (
    <StyledButtonContainer
      accessibilityHint={accessibilityHint}
      accessibilityLabel={accessibilityLabel}
      disabled={disabled || loading}
      loading={loading}
      onPress={handlePress}
      testID={testID}
      themeButtonVariant={themeVariant}
      style={style}
      underlayColor={underlayColor}
      themeInlineText={isInlineText}
      onPressIn={() => isInlineText && setIsPressed(true)}
      onPressOut={() => isInlineText && setIsPressed(false)}
      themeIsCompact={isCompactVariant}
      themeIsMedium={isMediumVariant}
      themeIsIconOnly={isIconButtonOnly}
    >
      {loading === true ? (
        <LoadingIndicator
          testID={`${testID}-loading-indicator`}
          themeVariant={themeVariant}
        />
      ) : (
        <>
          {icon !== undefined && (
            <StyledButtonIconWrapper
              themePosition="left"
              themeIsCompact={isCompactVariant}
              themeIsIconOnly={isIconButtonOnly}
            >
              {isIconName(icon) ? (
                <StyledButtonIcon
                  disabled={disabled}
                  icon={icon}
                  testID={`${testID}-left-icon`}
                  themeButtonVariant={themeVariant}
                  themeIsPressed={isPressed}
                  themeIsCompact={isCompactVariant}
                />
              ) : (
                icon
              )}
            </StyledButtonIconWrapper>
          )}
          {isRenderTextVariant ? renderTextVariantTitle() : renderTitle()}
          {!isIconButtonOnly && rightIcon !== undefined && (
            <StyledButtonIconWrapper
              themePosition="right"
              themeIsCompact={isCompactVariant}
            >
              {isIconName(rightIcon) ? (
                <StyledButtonIcon
                  disabled={disabled}
                  icon={rightIcon}
                  testID={`${testID}-right-icon`}
                  themeButtonVariant={themeVariant}
                  themeIsPressed={isPressed}
                  themeIsCompact={isCompactVariant}
                />
              ) : (
                rightIcon
              )}
            </StyledButtonIconWrapper>
          )}
        </>
      )}
    </StyledButtonContainer>
  );
};

export default Button;
