/**
 * H5表单验证 适用所有，当同时存在多个逻辑时 有先后顺序
 * 注意：凡是设置了正则表达式验证的 需在后面指出具体规则以告知用户
 * @author lautin
 * @created 2019-11-22 14:14:20
 */

class Validate {

    // 初始化验证元素
    constructor(root) {
        // 提取表单中的输入控件
        if (!root.nodeType) root = document.querySelector(root);

        // 将nodeList以及标识符 写入全局对象中
        Object.assign(this, {
            flag: true,
        }, {
            fields: this.extractInputs(root),
        });
    }

    /**
     * 遍历节点 提取输入控件
     * @param {dom}} node
     * @param {nodeList} result 返回DOM集合对象
     */
    extractInputs(node, result = null) {
        let fields = result || [];
        const types = ["text", "password", "file", "number", "range", "search", "email", "url"];

        for (let x = node.firstChild; x != null; x = x.nextSibling) {
            // 如果是元素节点 则检测节点名称并递归
            if (x.nodeType == node.ELEMENT_NODE) {
                switch (x.nodeName.toLowerCase()) {
                    case "input":
                        if (types.includes(x.type)) {
                            if (getComputedStyle(x, false).display != "none") {
                                fields.push(x); //放入该节点
                            }

                        }
                        break;
                    case "select":
                    case "textarea":
                        fields.push(x); //放入该节点
                        break;
                }
                // 继续递归
                this.extractInputs(x, fields);
            }
        }
        return fields;
    }

    /**
     * text验证maxLength和minLength
     * @param {object} state
     * @param {dom} item
     */
    textTask(state, item) {
        let result = '';
        switch (true) {
            case state.tooLong:
                result = `${item.name} 不能超过${item.maxLength}位`;
                break;

            case state.tooShort:
                result = `${item.name} 不能少于${item.minLength}位`;
                break;

            case state.patternMismatch:
                result = item.dataset.message || item.placeholder || `输入不符合要求`;
                break;
        }
        return result;
    }

    /**
     * password验证pattern
     * @param {object} state
     */
    pwdTask(state, item) {
        let result = '';
        if (state.patternMismatch) {
            result = item.dataset.message || `输入不符合规则要求`
        }
        return result;
    }

    /**
     * number和range类型 先验证min和max，再验证step
     * @param {object} state
     * @param {dom} item
     */
    numberTask(state, item) {
        let result = '';
        switch (true) {
            case state.rangeOverflow:
                result = `不能超出${item.max}的上限`;
                break;

            case state.rangeUnderflow:
                result = `不能低于${item.min}的下限`;
                break;

            case state.stepMismatch:
                result = item.dataset.message || `${item.value} 不是合法值，需要以${item.step}递增`;
                break;
        }
        return result;
    }

    /**
     * email和url类型 先验证type 再验证pattern
     * @param {object} state
     * @param {dom} item
     */
    emailAndUrl(state, item) {
        let result = '';
        switch (true) {
            case state.typeMismatch: // 适合number、email、url
                result = item.dataset.message || item.placeholder || `${item.name} 格式不正确`;
                break;

            case state.patternMismatch:
                result = item.dataset.message || item.placeholder || `输入不符合规则要求`;
                break;
        }
        return result;
    }

    /**
     * 执行验证的方法
     */
    validate() {
        [...this.fields].reverse().forEach((item) => {
            // 验证通过 则跳过本次循环
            if (item.checkValidity()) return;
            // 返回validityState对象
            let state = item.validity,
                message = '';

            // 首先检测值是否为空
            if (state.valueMissing) {
                message = item.dataset.has || item.placeholder || `${item.name} 不能为空`;
            } else {
                // 根据类型执行不同验证
                switch (item.type) {
                    case "text":
                    case "search":
                        message = this.textTask(state, item);
                        break;
                    case "password":
                        message = this.pwdTask(state, item);
                        break;
                    case "number":
                    case "range":
                        message = this.numberTask(state, item);
                        break;
                    case "email":
                    case "url":
                        message = this.emailAndUrl(state, item);
                        break;
                }
            }

            // 未知的类型验证
            if (state.badInput) {
                message = `输入了的值无效`;
            }

            //设置验证提示信息
            item.setCustomValidity(message);

            if (message) {

                // 手动报告验证的结果 触发元素的focus事件 等待用户输入
                item.reportValidity();
                // 如果用户有输入 则清除提示信息，最好监听change 避免破坏原有的v-model
                item.addEventListener("change", function () {

                    this.blur();
                })

                this.flag = false;
            }

        });

        return this.flag;
    }

    /**
     * 调用执行的静态方法
     * @param {dom} root
     */
    static start(root) {
        const obj = new Validate(root);
        return obj.validate();
    }
}

export default Validate.start;