import React from 'react';
import { clsx } from 'clsx';

import isNonNullable from '../_util/isNonNullable';
import { cloneElement, isFragment } from '../_util/reactNode';
import { PresetColors } from '../theme/interface';
import type { BaseButtonProps, LegacyButtonType } from './Button';

const rxTwoCNChar = /^[\u4E00-\u9FA5]{2}$/;

export const isTwoCNChar = rxTwoCNChar.test.bind(rxTwoCNChar);

export function convertLegacyProps(
  type?: LegacyButtonType,
): Pick<BaseButtonProps, 'danger' | 'type'> {
  if (type === 'danger') {
    return { danger: true };
  }
  return { type };
}

export function isString(str: unknown): str is string {
  return typeof str === 'string';
}

export function isUnBorderedButtonVariant(type?: ButtonVariantType) {
  return type === 'text' || type === 'link';
}

function splitCNCharsBySpace(
  child: React.ReactElement<any> | string | number,
  needInserted: boolean,
  style?: React.CSSProperties,
  className?: string,
) {
  if (!isNonNullable(child) || child === '') {
    return;
  }

  const SPACE = needInserted ? ' ' : '';

  if (
    typeof child !== 'string' &&
    typeof child !== 'number' &&
    isString(child.type) &&
    isTwoCNChar((child as React.ReactElement<{ children: string }>).props.children)
  ) {
    return cloneElement(child, (oriProps) => {
      const mergedCls = clsx(oriProps.className, className) || undefined;
      const mergedStyle: React.CSSProperties = { ...style, ...oriProps.style };
      return {
        ...oriProps,
        children: oriProps.children.split('').join(SPACE),
        className: mergedCls,
        style: mergedStyle,
      };
    });
  }

  if (isString(child)) {
    return (
      <span className={className} style={style}>
        {isTwoCNChar(child) ? child.split('').join(SPACE) : child}
      </span>
    );
  }

  if (isFragment(child)) {
    return (
      <span className={className} style={style}>
        {child}
      </span>
    );
  }

  return cloneElement(child, (oriProps) => ({
    ...oriProps,
    className: clsx(oriProps.className, className) || undefined,
    style: { ...oriProps.style, ...style },
  }));
}

export function spaceChildren(
  children: React.ReactNode,
  needInserted: boolean,
  style?: React.CSSProperties,
  className?: string,
) {
  let isPrevChildPure = false;
  const childList: React.ReactNode[] = [];

  React.Children.forEach(children, (child) => {
    const type = typeof child;
    const isCurrentChildPure = type === 'string' || type === 'number';
    if (isPrevChildPure && isCurrentChildPure) {
      const lastIndex = childList.length - 1;
      const lastChild = childList[lastIndex];
      childList[lastIndex] = `${lastChild}${child}`;
    } else {
      childList.push(child);
    }

    isPrevChildPure = isCurrentChildPure;
  });

  return React.Children.map(childList, (child) =>
    splitCNCharsBySpace(
      child as React.ReactElement | string | number,
      needInserted,
      style,
      className,
    ),
  );
}

const _ButtonTypes = ['default', 'primary', 'dashed', 'link', 'text'] as const;
export type ButtonType = (typeof _ButtonTypes)[number];

const _ButtonShapes = ['default', 'circle', 'round', 'square'] as const;
export type ButtonShape = (typeof _ButtonShapes)[number];

const _ButtonHTMLTypes = ['submit', 'button', 'reset'] as const;
export type ButtonHTMLType = (typeof _ButtonHTMLTypes)[number];

export const _ButtonVariantTypes = [
  'outlined',
  'dashed',
  'solid',
  'filled',
  'text',
  'link',
] as const;
export type ButtonVariantType = (typeof _ButtonVariantTypes)[number];

export const _ButtonColorTypes = ['default', 'primary', 'danger', ...PresetColors] as const;

export type ButtonColorType = (typeof _ButtonColorTypes)[number];
