import { Component, forwardRef, isValidElement } from 'react';
import styles from '@patternfly/react-styles/css/components/MenuToggle/menu-toggle';
import { css } from '@patternfly/react-styles';
import CaretDownIcon from '@patternfly/react-icons/dist/esm/icons/caret-down-icon';
import { BadgeProps } from '../Badge';
import RhUiSettingsFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-settings-fill-icon';
import RhUiCheckCircleFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-check-circle-fill-icon';
import RhUiErrorFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-error-fill-icon';
import RhUiWarningFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-warning-fill-icon';
import { OUIAProps, getOUIAProps } from '../../helpers';
import { SSRSafeIds } from '../../helpers/SSRSafeIds/SSRSafeIds';

export enum MenuToggleStatus {
  success = 'success',
  danger = 'danger',
  warning = 'warning'
}

export enum MenuToggleSize {
  default = 'default',
  sm = 'sm'
}

export type MenuToggleElement = HTMLDivElement | HTMLButtonElement;

export interface MenuToggleProps
  extends
    Omit<
      React.DetailedHTMLProps<
        React.ButtonHTMLAttributes<HTMLButtonElement> & React.HTMLAttributes<HTMLDivElement>,
        MenuToggleElement
      >,
      'ref'
    >,
    OUIAProps {
  /** Content rendered inside the toggle */
  children?: React.ReactNode;
  /** Additional classes added to the toggle */
  className?: string;
  /** Flag indicating the toggle has expanded styling */
  isExpanded?: boolean;
  /** Flag indicating the toggle is disabled */
  isDisabled?: boolean;
  /** Flag indicating the toggle is full height */
  isFullHeight?: boolean;
  /** Flag indicating the toggle takes up the full width of its parent */
  isFullWidth?: boolean;
  /** @beta Flag indicating the toggle is placed inside a form */
  isInForm?: boolean;
  /** Flag indicating the toggle contains placeholder text */
  isPlaceholder?: boolean;
  /** @beta Flag indicating the toggle has circular styling. Can only be applied to plain toggles. */
  isCircle?: boolean;
  /** Flag indicating whether the toggle is a settings toggle. This will override the icon property */
  isSettings?: boolean;
  /** @beta Flag indicating the menu toggle is a docked variant. For use in docked navigation. */
  isDocked?: boolean;
  /** @beta Flag indicating the docked toggle should display text. Only applies when isDocked is true. */
  isTextExpanded?: boolean;
  /** Elements to display before the toggle button. When included, renders the menu toggle as a split button. */
  splitButtonItems?: React.ReactNode[];
  /** Variant styles of the menu toggle */
  variant?: 'default' | 'plain' | 'primary' | 'plainText' | 'secondary' | 'typeahead';
  /** Status styles of the menu toggle */
  status?: 'success' | 'warning' | 'danger';
  /** Overrides the status icon */
  statusIcon?: React.ReactNode;
  /** Optional icon or image rendered inside the toggle, before the children content. It is
   * recommended to wrap most basic icons in our icon component.
   */
  icon?: React.ReactNode;
  /** Optional badge rendered inside the toggle, after the children content */
  badge?: BadgeProps | React.ReactNode;
  /** Adds styling which affects the size of the menu toggle */
  size?: 'default' | 'sm';
  /** @hide Forwarded ref */
  innerRef?: React.Ref<MenuToggleElement>;
  /** Value to overwrite the randomly generated data-ouia-component-id. It will always target the toggle button. */
  ouiaId?: number | string;
  /** Set the value of data-ouia-safe. Only set to true when the component is in a static state, i.e. no animations are occurring. At all other times, this value must be false. */
  ouiaSafe?: boolean;
}

class MenuToggleBase extends Component<MenuToggleProps> {
  displayName = 'MenuToggleBase';
  static defaultProps: MenuToggleProps = {
    className: '',
    isExpanded: false,
    isDisabled: false,
    isFullWidth: false,
    isFullHeight: false,
    isInForm: false,
    isPlaceholder: false,
    isCircle: false,
    isDocked: false,
    isTextExpanded: false,
    size: 'default',
    ouiaSafe: true
  };

  render() {
    const {
      children,
      className,
      icon,
      badge,
      isExpanded,
      isDisabled,
      isFullHeight,
      isFullWidth,
      isInForm,
      isPlaceholder,
      isCircle,
      isSettings,
      isDocked,
      isTextExpanded,
      splitButtonItems,
      variant,
      status,
      statusIcon,
      innerRef,
      onClick,
      'aria-label': ariaLabel,
      ouiaId,
      ouiaSafe,
      size,
      ...otherProps
    } = this.props;
    const isPlain = variant === 'plain';
    const isPlainText = variant === 'plainText';
    const isTypeahead = variant === 'typeahead';

    return (
      <SSRSafeIds prefix="pf-" ouiaComponentType={`MenuToggle${variant ? `-${variant}` : ''}`}>
        {(_, generatedOuiaId) => {
          const ouiaProps = getOUIAProps(MenuToggle.displayName, ouiaId ?? generatedOuiaId, ouiaSafe);

          let _statusIcon = statusIcon;
          if (!statusIcon) {
            switch (status) {
              case MenuToggleStatus.success:
                _statusIcon = <RhUiCheckCircleFillIcon />;
                break;
              case MenuToggleStatus.warning:
                _statusIcon = <RhUiWarningFillIcon />;
                break;
              case MenuToggleStatus.danger:
                _statusIcon = <RhUiErrorFillIcon />;
                break;
            }
          }

          const toggleControls = (
            <span className={css(styles.menuToggleControls)}>
              {status !== undefined && <span className={css(styles.menuToggleStatusIcon)}>{_statusIcon}</span>}
              <span className={css(styles.menuToggleToggleIcon)}>
                <CaretDownIcon />
              </span>
            </span>
          );

          const content = (
            <>
              {(icon || isSettings) && (
                <span className={css(styles.menuToggleIcon)}>{isSettings ? <RhUiSettingsFillIcon /> : icon}</span>
              )}
              {isTypeahead ? children : children && <span className={css(styles.menuToggleText)}>{children}</span>}
              {isValidElement(badge) && <span className={css(styles.menuToggleCount)}>{badge}</span>}
              {isTypeahead ? (
                <button
                  type="button"
                  className={css(styles.menuToggleButton)}
                  aria-expanded={isExpanded}
                  onClick={onClick}
                  aria-label={ariaLabel || 'Menu toggle'}
                  tabIndex={-1}
                  {...ouiaProps}
                >
                  {toggleControls}
                </button>
              ) : (
                !isPlain && toggleControls
              )}
            </>
          );

          const commonStyles = css(
            styles.menuToggle,
            isExpanded && styles.modifiers.expanded,
            variant === 'primary' && styles.modifiers.primary,
            variant === 'secondary' && styles.modifiers.secondary,
            status && styles.modifiers[status],
            (isPlain || isPlainText) && styles.modifiers.plain,
            isPlainText && styles.modifiers.text,
            isFullHeight && styles.modifiers.fullHeight,
            isFullWidth && styles.modifiers.fullWidth,
            isInForm && styles.modifiers.form,
            isDisabled && styles.modifiers.disabled,
            isPlaceholder && styles.modifiers.placeholder,
            isSettings && styles.modifiers.settings,
            isDocked && styles.modifiers.docked,
            isDocked && isTextExpanded && styles.modifiers.textExpanded,
            size === MenuToggleSize.sm && styles.modifiers.small,
            className
          );

          const componentProps = {
            children: content,
            ...(isDisabled && { disabled: true }),
            ...otherProps
          };

          if (isTypeahead) {
            return (
              <div
                ref={innerRef as React.Ref<HTMLDivElement>}
                className={css(commonStyles, styles.modifiers.typeahead)}
                {...componentProps}
              />
            ) as React.ReactElement;
          }

          if (splitButtonItems) {
            return (
              <div
                ref={innerRef as React.Ref<HTMLDivElement>}
                className={css(commonStyles, styles.modifiers.splitButton)}
              >
                {splitButtonItems}
                <button
                  className={css(styles.menuToggleButton, children && styles.modifiers.text)}
                  type="button"
                  aria-expanded={isExpanded}
                  aria-label={ariaLabel}
                  disabled={isDisabled}
                  onClick={onClick}
                  {...otherProps}
                  {...ouiaProps}
                >
                  {children && <span className={css(styles.menuToggleText)}>{children}</span>}
                  {toggleControls}
                </button>
              </div>
            ) as React.ReactElement;
          }

          return (
            <button
              className={css(commonStyles, isCircle && isPlain && styles.modifiers.circle)}
              type="button"
              aria-label={ariaLabel}
              aria-expanded={isExpanded}
              ref={innerRef as React.Ref<HTMLButtonElement>}
              disabled={isDisabled}
              onClick={onClick}
              {...componentProps}
              {...ouiaProps}
            />
          ) as React.ReactElement;
        }}
      </SSRSafeIds>
    );
  }
}

export const MenuToggle = forwardRef((props: MenuToggleProps, ref: React.Ref<MenuToggleElement>) => (
  <MenuToggleBase innerRef={ref} {...props} />
));

MenuToggle.displayName = 'MenuToggle';
