import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import Msg from './Validator.Msg';
import * as Checkers from './checker';

/**
 * 为Validator指定的表单组件提供封装
 * 1. 提供校验API
 * 2. 提供默认的提示信息定位API
 * 3. 对包裹的表单组件实例做管理(传递原有属性)
 *
 * 表单组件应至少存在以下API
 * 1. getValue/setValue
 */

const prefixCls = 'im-validator-enhancer';

function enhancer(klass) {
  return class extends React.Component {
    static propTypes = {
      'data-checking': PropTypes.bool,
      'data-required': PropTypes.bool,
      'data-required-msg': PropTypes.string,
      'data-patterns': PropTypes.array,
      'data-placer': PropTypes.func,
      'data-checkers': PropTypes.object,
      name: PropTypes.string,
    };

    constructor(props) {
      super(props);

      this.state = {
        initialIsValid: true,
        // 为null时，说明还未触发过校验
        isValid: null,
        msgs: [],
      };

      this.isValid = this.isValid.bind(this);
      this.restoreRef = this.restoreRef.bind(this);
      this.reset = this.reset.bind(this);
      this.focusSelf = this.focusSelf.bind(this);
      this.checkSelf = this.checkSelf.bind(this);
      this.checkPatterns = this.checkPatterns.bind(this);
    }

    // 设置初始有效值
    componentDidMount() {
      const $field = this.$field;

      let val = this.props.value;
      if ($field && $field.getValue) {
        val = $field.getValue();
      }

      this.checkPatterns(val, (result) => {
        this.state.initialIsValid = result.isValid;
      });
    }

    isValid() {
      const { isValid, initialIsValid } = this.state;

      return isValid !== null ? isValid : initialIsValid;
    }

    restoreRef(ref, name) {
      if (typeof ref === 'string') {
        return console.warn(`[Validator]${name} field's ref prop has been reset`);
      }

      if (typeof ref === 'function') {
        ref(this.$field);
      }
    }

    reset() {
      this.setState({
        isValid: null,
        msgs: [],
      });
    }

    focusSelf(offset) {
      const $dom = ReactDOM.findDOMNode(this);

      // window.scrollTo(window.scrollX, $dom.scrollTop + offset);
      $dom.scrollIntoView(offset);
      if (this.$field && this.$field.focus) {
        this.$field.focus();
      }
    }

    checkSelf(val, callback) {
      const isChecking = this.props['data-checking'];
      const placer = this.props['data-placer'];

      if (isChecking === false) {
        return false;
      }

      this.checkPatterns(val, (result) => {
        // 保证已渲染完成
        this.setState(result, () => {
          if (placer) {
            placer(result);
          }

          if (callback) {
            callback(result);
          }
        });
      });
    }

    checkPatterns(val, callback) {
      const result = {
        isValid: true,
        msgEl: null,
        msgs: [],
      };

      const isRequired = this.props['data-required'];

      // 0, '', undefined, null
      if (!val && !isRequired) {
        return callback(result);
      } else if (isRequired && !val) {
        result.isValid = false;
        result.msgs.push(this.props['data-required-msg'] || '不能为空');
      }

      const patterns = this.props['data-patterns'];
      const checkers = this.props['data-checkers'];

      const promises = patterns.map((pattern) => {
        const key = pattern.key;
        const checker = checkers[key] || Checkers[key];
        const isBuiltIn = !!Checkers[key];

        if (!checker && typeof checker !== 'function') {
          throw new Error(`[Validator]${pattern.key} checker not valid`);
        }

        return new Promise((resolve) => {
          if (isBuiltIn) {
            const isValid = checker(val, pattern[pattern.key]);
            return resolve({
              isValid,
              msg: !isValid ? pattern.msg : '',
            });
          }

          checker(val, resolve);
        });
      });

      // 规定checker返回的数据结构
      Promise.all(promises).then((infos) => {
        infos.forEach((info) => {
          if (!info.isValid) {
            result.isValid = false;
            result.msgs.push(info.msg);
          }
        });

        // 带上错误信息element
        // 带上表单组件DOM
        result.$field = ReactDOM.findDOMNode(this.$field);
        result.msgEl = <Msg status="error" msgs={result.msgs} />;
        callback(result);
      });
    }

    getValue() {
      const $field = this.$field || {};

      if (typeof $field.getValue === 'function') {
        return $field.getValue();
      }

      return $field.state && $field.state.value; // 尝试取state中的value值，后续增加data-valueProp来指定key
    }

    render() {
      // 加入data-前缀，后缀元素以满足简单的布局插入
      // 对于复杂的表单，应该抽象成组件以配合validator
      const $prefix = this.props['data-prefix'];
      const $suffix = this.props['data-suffix'];

      const isChecking = this.props['data-checking'];
      const placer = this.props['data-placer'];
      const tips = this.props['data-tips'];
      const { isValid, msgs } = this.state;

      const props = Object.keys(this.props).reduce((result, key) => {
        if (key.indexOf('data-') === -1) {
          result[key] = this.props[key];
        }
        return result;
      }, {});

      const $field = React.createElement(
        klass,
        Object.assign(props, {
          ref: (el) => {
            this.$field = el;
          },
        })
      );

      let $msg = null;

      if (tips) {
        $msg = <Msg status="info" msgs={[tips]} />;
      }

      if (isChecking !== false && !placer && isValid !== null) {
        $msg = <Msg status={isValid ? 'success' : 'error'} msgs={msgs} />;
      }

      const allCls = classnames({
        [prefixCls]: true,
        [`${prefixCls}--error`]: isValid === false && !!$msg,
      });

      return (
        <div className={allCls}>
          {$prefix}
          {$field}
          {$suffix}
          {$msg}
        </div>
      );
    }
  };
}

export default enhancer;
