/**
 * imui.Validator
 * @author moxhe
 * @date 2016-09-24
 */
import React from 'react';
import classnames from 'classnames';
import PropTypes from 'prop-types';

import enhancer from './enhancer';
import { deepSearch } from './utils';
// @require '../style/index.scss'

/**
 * 1. 为了对无状态组件的支持，统一在表单组件上做一层封装HOC
 *   - 存在componentWillReceiveProps方法的组件需要做好优化
 *   - 会导致表单组件的ref属性不可用(fixed)
 *   - 每次render的时候都会新建enhancer class，bug不可预计(fixed)
 */

function hasValidationProps(element) {
  return (
    React.isValidElement(element) &&
    element.props.hasOwnProperty('data-patterns')
  );
}

function getFieldName(element) {
  const name = element.props.name || element.props['data-name'];

  if (!name) {
    throw new Error('[Validator]validated field should has name prop');
  }
  return name;
}

function getFieldTrigger(element) {
  let trigger = element.props['data-trigger'] || ['onChange', 'onBlur'];

  if (typeof trigger === 'string') {
    trigger = [trigger];
  }

  if (Object.prototype.toString.apply(trigger) !== '[object Array]') {
    throw new Error('[Validator]invalid data-trigger prop');
  }

  return trigger;
}

export default class Validator extends React.Component {
  static propTypes = {
    /**
     * 校验模式函数集
     */
    checkers: PropTypes.object,
    /**
     * valid回调接口
     */
    onValid: PropTypes.func,
    /**
     * invalid回调接口
     */
    onInValid: PropTypes.func,
    /**
     * 提示信息位置
     */
    msgAlign: PropTypes.oneOf(['right', 'bottom'])
  };

  static defaultProps = {
    prefixCls: 'im-validator',
    checkers: {},
    onValid() {},
    onInValid() {},
    msgAlign: 'right'
  };

  constructor(props) {
    super(props);

    this.fields = {};

    this.refy = this.refy.bind(this);
    this.checkify = this.checkify.bind(this);
    this.checkField = this.checkField.bind(this);
    this.scrollToErrorField = this.scrollToErrorField.bind(this);
  }

  refy(ref, name) {
    return (instance) => {
      // react存在detach ref的操作，故兼容之
      if (instance && instance.restoreRef) {
        this.fields[name] = instance;
        instance.restoreRef(ref, name);
      }
    };
  }

  checkify(handler, name) {
    return (data) => {
      this.checkField(data, name);

      if (handler) {
        handler(data);
      }
    };
  }

  isAllValid() {
    return Object.keys(this.fields).every((key) => this.fields[key].isValid());
  }

  isFieldValid(name) {
    let result = null;

    if (name && this.fields[name]) {
      result = this.fields[name].isValid();
    }

    return result;
  }

  resetField(name) {
    if (name && this.fields[name]) {
      const instance = this.fields[name];

      instance.reset();
    }
  }

  checkField(data, name) {
    const instance = this.fields[name];
    // SyntheticEvent or value(不同组件的value格式不一样)
    let val = data && data.nativeEvent ? data.target.value : data;

    // 如果没有传递有效的data，则尝试取实例的值
    if (data === undefined) {
      // 此处的instance是enhancer实例
      val = instance.getValue();
    }

    instance.checkSelf(val, () => {
      let allIsValid = Object.keys(this.fields).every((key) => {
        return this.fields[key].isValid();
      });

      if (allIsValid) {
        this.props.onValid();
      } else {
        this.props.onInValid();
      }
    });
  }

  scrollToErrorField(idx, offset) {
    const errKeys = Object.keys(this.fields).filter((key) => {
      return !this.fields[key].isValid();
    });

    if (idx >= errKeys.length) {
      return false;
    }

    this.fields[errKeys[idx]].focusSelf(offset);
  }

  render() {
    const { prefixCls, className, msgAlign } = this.props;

    const children = deepSearch(
      this.props.children,
      hasValidationProps,
      (item) => {
        // 使用HOC封装表单组件, 重置onChange, ref属性
        // 见https://facebook.github.io/react/blog/2015/03/03/react-v0.13-rc2.html
        const name = getFieldName(item);
        const trigger = getFieldTrigger(item);
        const enhancedEl = this.fields[name];
        const klass = item.type;

        const eventProps = trigger.reduce((props, event) => {
          props[event] = this.checkify(item.props[event], name);

          return props;
        }, {});

        const props = Object.assign({}, item.props, eventProps, {
          name,
          ref: this.refy(item.ref, name),
          // onChange: this.checkify(item.props.onChange, name),
          // onBlur: this.checkify(item.props.onBlur, name),
          'data-checkers': this.props.checkers
        });

        if (enhancedEl) {
          return React.createElement(enhancedEl.constructor, props);
        }

        return React.createElement(enhancer(klass), props);
      }
    );

    const allCls = {
      [className]: !!className,
      [prefixCls]: true,
      [`${prefixCls}--${msgAlign}`]: true
    };

    // 清理fields以防止隐藏，失效的表单影响校验逻辑
    this.fields = {};

    return (
      <div id={this.selector} className={classnames(allCls)}>
        {children}
      </div>
    );
  }
}
