import React, { MouseEvent, FocusEvent, ReactElement, ReactChild } from 'react';
import css from '../../utils/css';
import {
  StyledButton,
  IconWrapper,
  Intent,
  ThemeVariant,
} from './StyledButton';
import Icon, { IconName } from '../Icon';

import { CommonProps } from '../common';
import {
  fromUndefinedable,
  getOrElse,
  map,
  none,
  some,
  Option,
} from '../../fp/Option';
import { pipe } from '../../fp/function';

export interface ButtonProps extends CommonProps {
  /**
   * Disable state of button.
   */
  disabled?: boolean;
  /**
   * Icon name to render before the text.
   */
  icon?: IconName;
  /**
   * Visual intent color to apply to button. It is required for `filled`, `outlined` and `text` variants.
   */
  intent?: 'primary' | 'danger' | 'success' | 'warning' | 'error';
  /**
   * Loading state of button.
   */
  loading?: boolean;
  /**
   * Set handler to handler `blur` event.
   */
  onBlur?: (e: FocusEvent) => void;
  /**
   * Set the handler to handle `click` event.
   */
  onClick?: (e: MouseEvent) => void;
  /**
   * Icon name to render after the text.
   */
  rightIcon?: IconName;
  /**
   * Size of button.
   */
  size?: 'small' | 'medium' | 'large';
  /**
   * Button label.
   */
  text: ReactChild;
  /**
   * Specifies the HTML attribute type of button.
   */
  type?: 'submit' | 'reset' | 'button';
  /**
   * Button type.
   */
  variant?:
    | 'basic'
    | 'basic-transparent'
    | 'filled'
    | 'outlined'
    | 'text'
    | 'filled-reversed';
}

const FILLED_VARIANTS = {
  primary: 'filled-primary',
  success: 'filled-success',
  danger: 'filled-danger',
  warning: 'filled-warning',
  error: 'filled-error',
  black: 'filled-black',
} as const;

const OUTLINED_VARIANTS = {
  primary: 'outlined-primary',
  success: 'outlined-success',
  danger: 'outlined-danger',
  warning: 'outlined-warning',
  error: 'outlined-error',
  black: 'outlined-black',
} as const;

const TEXT_VARIANTS = {
  primary: 'text-primary',
  success: 'text-success',
  danger: 'text-danger',
  warning: 'text-warning',
  error: 'text-error',
  black: 'text-black',
} as const;

const FILLED_REVERSED_VARIANTS = {
  primary: 'filled-reversed-primary',
  success: 'filled-reversed-success',
  danger: 'filled-reversed-danger',
  warning: 'filled-reversed-warning',
  error: 'filled-reversed-error',
  black: 'filled-reversed-black',
} as const;

export const getThemeVariant = (
  variant:
    | 'basic'
    | 'basic-transparent'
    | 'filled'
    | 'outlined'
    | 'text'
    | 'filled-reversed',
  intent: Intent
): ThemeVariant => {
  switch (variant) {
    case 'basic':
    case 'basic-transparent':
      return variant;
    case 'filled':
      return FILLED_VARIANTS[intent];
    case 'outlined':
      return OUTLINED_VARIANTS[intent];
    case 'text':
      return TEXT_VARIANTS[intent];
    case 'filled-reversed':
      return FILLED_REVERSED_VARIANTS[intent];
  }
};

const Button = ({
  text,
  icon,
  rightIcon,
  onClick,
  onBlur,
  variant = 'filled',
  intent = 'primary',
  loading,
  disabled,
  size = 'medium',
  type = 'button',
  id,
  className,
  style,
  sx = {},
  'data-test-id': dataTestId,
}: ButtonProps): ReactElement => {
  const maybeIcon = fromUndefinedable(icon);
  const maybeLoading: Option<IconName> =
    loading === true ? some('loading') : none;

  const mapToLeftIcon = map(
    (ic: IconName): ReactElement => (
      <IconWrapper themePosition="left">
        <Icon icon={loading === true ? 'loading' : ic} spin={loading} />
      </IconWrapper>
    )
  );

  return (
    <StyledButton
      onClick={onClick}
      onBlur={onBlur}
      loading={loading}
      disabled={disabled}
      type={type}
      id={id}
      className={className}
      style={{ ...style, ...css(sx) }}
      data-test-id={dataTestId}
      themeSize={size}
      themeVariant={getThemeVariant(variant, intent)}
    >
      {pipe(
        maybeIcon,
        mapToLeftIcon,
        getOrElse(() =>
          pipe(
            maybeLoading,
            mapToLeftIcon,
            getOrElse(() => null)
          )
        )
      )}
      {text}
      {rightIcon !== undefined && (
        <IconWrapper themePosition="right">
          <Icon icon={rightIcon} />
        </IconWrapper>
      )}
    </StyledButton>
  );
};

export default Button;
