import React, { useState, useRef } from "react";
import classNames from "classnames";
import { Button } from "../button";
import { mergeRefs } from "../_util/merge-refs";
import { useConfig } from "../_util/config-context";
import { useDefaultValue, ChangeContext } from "../form/controlled";
import { forwardRefWithStatics } from "../_util/forward-ref-with-statics";
import { List } from "../list";
import { Icon } from "../icon";
import { Input, InputProps } from "./Input";
import { Bubble, BubbleProps } from "../bubble";

export interface InputPasswordRule {
  /**
   * 规则描述
   */
  text: string;

  /**
   * 规则校验器
   */
  validator: (inputValue: string, subRuleResults: boolean[]) => boolean;

  /**
   * 子规则
   */
  subRules?: {
    /**
     * 规则描述
     */
    text: InputPasswordRule["text"];

    /**
     * 规则校验器
     */
    validator: (inputValue: string) => boolean;
  }[];
}

export interface InputPasswordChangeContext extends ChangeContext {
  /**
   * 是否符合校验规则
   */
  valid: boolean;
}

export interface InputPasswordProps
  extends Omit<
    InputProps,
    "multiline" | "type" | "onChange" | "rows" | "showCount" | "baseClassName"
  > {
  /**
   * 密码校验规则
   *
   * **建议统一使用组件默认规则**
   *
   * @default 见示例
   */
  rules?: InputPasswordRule[] | false;

  /**
   * 规则气泡放置的位置
   * @default "right-start"
   */
  bubblePlacement?: BubbleProps["placement"];

  /**
   * 值发生变更时进行回调
   */
  onChange?: (value: string, context: InputPasswordChangeContext) => void;
}

const defaultRules: InputPasswordRule[] = [
  {
    text: "在8 ～ 30 位字符数以内（推荐12位以上）",
    validator: value => value.length >= 8 && value.length <= 30,
  },
  {
    text: '不能以" / "开头',
    validator: value => !value.startsWith("/"),
  },
  {
    text: "至少包含其中三项",
    validator: (_, results) =>
      results.reduce((pre, cur) => (cur ? pre + 1 : pre), 0) >= 3,
    subRules: [
      {
        text: "小写字母 a ~ z",
        validator: value => /[a-z]/.test(value),
      },
      {
        text: "大写字母 A ～ Z",
        validator: value => /[A-Z]/.test(value),
      },
      {
        text: "数字 0 ～ 9",
        validator: value => /[0-9]/.test(value),
      },
      {
        text: "()!@#$%^&*|?><",
        validator: value => /[()!@#$%^&*|?><]/.test(value),
      },
    ],
  },
];

function isValid(value: string, rules: InputPasswordProps["rules"]) {
  return (rules || []).reduce((pre, cur) => {
    const subResults = Array.isArray(cur.subRules)
      ? cur.subRules.map(({ validator }) => validator(value))
      : [];
    return pre && cur.validator(value, subResults);
  }, true);
}

export const InputPassword = forwardRefWithStatics(
  (props: InputPasswordProps, ref: React.Ref<HTMLInputElement>) => {
    const { classPrefix } = useConfig();
    const {
      value,
      onChange,
      className,
      style,
      rules = defaultRules,
      bubblePlacement = "right-start",
      size,
      readonly,
      disabled,
      ...inputProps
    } = useDefaultValue(props, "");
    const inputRef = useRef<HTMLInputElement>(null);
    const [show, setShow] = useState<boolean>(false);

    return (
      <div
        className={classNames(`${classPrefix}-input-password`, className, {
          "size-full-width": size === "full",
        })}
        style={style}
      >
        <Bubble
          trigger="focus"
          arrowPointAtCenter
          placement={bubblePlacement}
          className={`${classPrefix}-input-password-bubble`}
          content={
            Array.isArray(rules) &&
            rules.length && (
              <List>
                {rules.map(({ text, validator, subRules }, index) => {
                  return (
                    <List.Item key={index}>
                      <Icon
                        type={
                          validator(
                            value,
                            Array.isArray(subRules)
                              ? subRules.map(({ validator }) =>
                                  validator(value)
                                )
                              : []
                          )
                            ? "success"
                            : "error--ring"
                        }
                      />
                      {text}
                      {Array.isArray(subRules) && (
                        <List type="bullet">
                          {subRules.map(({ text, validator }, index) => {
                            return (
                              <List.Item
                                key={index}
                                className={classNames({
                                  "is-success": validator(value),
                                })}
                              >
                                {text}
                              </List.Item>
                            );
                          })}
                        </List>
                      )}
                    </List.Item>
                  );
                })}
              </List>
            )
          }
        >
          <Input
            {...inputProps}
            size={size}
            value={value}
            onChange={(value, { event }) =>
              onChange(value, { event, valid: isValid(value, rules) })
            }
            ref={mergeRefs(ref, inputRef)}
            type={show ? "text" : "password"}
            readonly={readonly}
            disabled={disabled}
          />
        </Bubble>
        <div className={`${classPrefix}-input-password__op`}>
          {!readonly && !disabled && value && (
            <Button
              htmlType="button"
              type="icon"
              icon="dismiss"
              onClick={event =>
                onChange("", { event, valid: isValid("", rules) })
              }
              onMouseUp={e => e.preventDefault()}
              onMouseDown={e => e.preventDefault()}
            />
          )}
          <Button
            htmlType="button"
            type="icon"
            icon={show ? "show" : "hide"}
            onClick={() => setShow(show => !show)}
            onMouseUp={e => e.preventDefault()}
            onMouseDown={e => e.preventDefault()}
          />
        </div>
      </div>
    );
  },
  {
    defaultLabelAlign: "middle",
  }
);

InputPassword.displayName = "InputPassword";
