import type { DSize } from '../../utils/types';

import React from 'react';

import { LoadingOutlined } from '@react-devui/icons';
import { checkNodeExist, getClassName } from '@react-devui/utils';

import { useWave, useGeneralContext } from '../../hooks';
import { registerComponentMate, TTANSITION_DURING_SLOW } from '../../utils';
import { DBaseDesign } from '../_base-design';
import { DCollapseTransition } from '../_transition';
import { useComponentConfig, usePrefixConfig } from '../root';

export interface DButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
  dType?: 'primary' | 'secondary' | 'outline' | 'dashed' | 'text' | 'link';
  dTheme?: 'primary' | 'success' | 'warning' | 'danger';
  // TODO: Should add `aria-busy` for loading state?
  dLoading?: boolean;
  dBlock?: boolean;
  dVariant?: 'circle' | 'round';
  dSize?: DSize;
  dIcon?: React.ReactNode;
  dIconRight?: boolean;
}

const { COMPONENT_NAME } = registerComponentMate({ COMPONENT_NAME: 'DButton' as const });
function Button(props: DButtonProps, ref: React.ForwardedRef<HTMLButtonElement>): JSX.Element | null {
  const {
    children,
    dType = 'primary',
    dTheme = 'primary',
    dLoading = false,
    dBlock = false,
    dVariant,
    dSize,
    dIcon,
    dIconRight = false,

    ...restProps
  } = useComponentConfig(COMPONENT_NAME, props);

  //#region Context
  const dPrefix = usePrefixConfig();
  const { gSize, gDisabled } = useGeneralContext();
  //#endregion

  const [waveNode, wave] = useWave();

  const size = dSize ?? gSize;
  const disabled = restProps.disabled || dLoading || gDisabled;

  const buttonIcon = (loading: boolean, iconRef?: React.RefObject<HTMLDivElement>, style?: React.CSSProperties) => (
    <div
      ref={iconRef}
      className={getClassName(`${dPrefix}button__icon`, {
        [`${dPrefix}button__icon--right`]: dIconRight,
      })}
      style={style}
    >
      {loading ? <LoadingOutlined dSpin /> : dIcon}
    </div>
  );

  const iconNode = checkNodeExist(dIcon) ? (
    buttonIcon(dLoading)
  ) : (
    <DCollapseTransition
      dOriginalSize={{
        width: '',
      }}
      dCollapsedStyle={{
        width: 0,
      }}
      dIn={dLoading}
      dDuring={TTANSITION_DURING_SLOW}
      dHorizontal
      dStyles={{
        entering: {
          transition: ['width', 'padding', 'margin'].map((attr) => `${attr} ${TTANSITION_DURING_SLOW}ms linear`).join(', '),
        },
        leaving: {
          transition: ['width', 'padding', 'margin'].map((attr) => `${attr} ${TTANSITION_DURING_SLOW}ms linear`).join(', '),
        },
        leaved: { display: 'none' },
      }}
      dDestroyWhenLeaved
    >
      {(collapseRef, collapseStyle) => buttonIcon(true, collapseRef, collapseStyle)}
    </DCollapseTransition>
  );

  const contentNode = <div>{children}</div>;

  return (
    <DBaseDesign
      dComposeDesign={{
        disabled: disabled,
      }}
      dFormDesign={false}
    >
      {({ render: renderBaseDesign }) =>
        renderBaseDesign(
          <button
            {...restProps}
            ref={ref}
            className={getClassName(restProps.className, `${dPrefix}button`, `${dPrefix}button--${dType}`, `t-${dTheme}`, {
              [`${dPrefix}button--${dVariant}`]: dVariant,
              [`${dPrefix}button--${size}`]: size,
              [`${dPrefix}button--block`]: dBlock,
              [`${dPrefix}button--icon`]: !children,
              'is-loading': dLoading,
            })}
            type={restProps.type ?? 'button'}
            disabled={disabled}
            onClick={(e) => {
              restProps.onClick?.(e);

              if (dType === 'primary' || dType === 'secondary' || dType === 'outline' || dType === 'dashed') {
                wave(`var(--${dPrefix}color-${dTheme})`);
              }
            }}
          >
            {dIconRight ? (
              <>
                {contentNode}
                {iconNode}
              </>
            ) : (
              <>
                {iconNode}
                {contentNode}
              </>
            )}
            {waveNode}
          </button>
        )
      }
    </DBaseDesign>
  );
}

export const DButton = React.forwardRef(Button);