'use strict';

/**
 *
 *
 * @author Mai
 * @date 2018/4/25
 * @version
 */

const Service = require('egg').Service;
module.exports = app => {

    const paramConst = app.paramConst;
    const nodeConst = app.nodeConst;

    class Match extends Service {

        /**
         * 初始化匹配所需变量
         * @param {Array|Object} bills - 导入的清单数据
         * @private
         */
        _init(bills) {
            this.bills = bills instanceof Array ? bills : [bills];
            this.bills[0].match_node = null;
            this.fixedBills = this.bills.filter(function (b) {
                return b.n_id < 100;
            });
            this.nodes = [];
            this.indexes = [];
            this.params = [];
        }
        /**
         * 获取指标模板数据
         * @param {Number} templateId - 指标模板Id
         * @returns {Promise<void>}
         * @private
         */
        async _getTemplateData (templateId) {
            this.templateNodes = await this.ctx.service.templateNode.getAllDataByCondition({ where: { template_id: templateId } });
            this.templateIndexes = await this.ctx.service.templateIndex.getAllDataByCondition({ where: { template_id: templateId } });
            this.templateParams = await this.ctx.service.templateParam.getAllDataByCondition({ where: { template_id: templateId } });
        }

        /**
         * 检查code是否满足匹配规则rule
         * @param {String} code - 匹配编号
         * @param {String} rule - 匹配规则
         * @returns {boolean}
         * @private
         */
        _matchCode(code, rule) {
            const codeParts = code.split('-');
            const ruleParts = rule.split('-');
            const numReg = /(^[0-9]+$)/;
            if (codeParts.length !== ruleParts.length) { return false; }
            for (let i = 0, iLen = codeParts.length; i < iLen; i++) {
                if (numReg.test(ruleParts[i]) && ruleParts[i] !== codeParts[i]) {
                    return false;
                }
            }
            return true;
        }

        /**
         * 过滤符合指标节点node匹配规则的清单
         * 已经匹配过的清单不再匹配
         *
         * @param {Object} node - 指标节点
         * @private
         */
        _filterBills (node) {
            const self = this;
            return this.bills.filter(function (b) {
                if (!b.match_node) {
                    if (node.match_type === nodeConst.matchType.code) {
                        return self._matchCode(b.code, node.match_key);
                    } else {
                        return node.match_key === b.name;
                    }
                } else {
                    return false;
                }
            })
        }

        /**
         * 根据指标参数取值类别，从清单取值
         *
         * @param {Object} param - 指标参数
         * @param {Object} bills - 清单节点
         * @returns {number}
         * @private
         */
        _getNodeBillsValue(param, bills) {
            if (bills) {
                switch (param.match_num) {
                    case paramConst.matchNum.quantity: return bills.quantity;
                    case paramConst.matchNum.total_price: return bills.total_price;
                    case paramConst.matchNum.dgn_quantity1: return bills.dgn_quantity1;
                    case paramConst.matchNum.dgn_quantity2: return bills.dgn_quantity2;
                    default: return undefined;
                }
            } else {
                return undefined;
            }
        }

        /**
         * 获取指标参数取值(固定Id匹配)
         * @param {Object} param - 指标参数
         * @returns {*}
         * @private
         */
        _getFixedIdParamValue(param) {
            for (const b of this.fixedBills) {
                if (b.n_id == param.match_key) {
                    return this._getNodeBillsValue(param, b);
                }
            }
            return undefined;
        }

        /**
         * 获取指标参数取值(编号匹配)
         * @param param
         * @param nodeBills
         * @returns {*}
         * @private
         */
        _getCodeParamValue(param) {
            for (const b of this.bills) {
                if (this._matchCode(b.code, param.match_key)) {
                    return this._getNodeBillsValue(param, b);
                }
            }
            return undefined;
        }

        /**
         * 获取父项
         * @param {Object} node - 分项清单节点
         * @private
         */
        _getParentBills(node) {
            return node ? this.ctx.helper.findObj(this.bills, 'n_id', node.n_pid) : null;
        }

        /**
         * 获取指标参数取值
         * @param {Object} param - 指标参数
         * @param {Object} nodeBills - 指标参数 所属 指标节点，绑定的 清单
         * @returns {*}
         * @private
         */
        _getParamValue(param, nodeBills) {
            switch (param.match_type) {
                case paramConst.matchType.fixed_id: return this._getFixedIdParamValue(param);
                case paramConst.matchType.node_default: return this._getNodeBillsValue(param, nodeBills);
                case paramConst.matchType.parent_default: return this._getNodeBillsValue(param, this._getParentBills(nodeBills));
                case paramConst.matchType.code: return this._getCodeParamValue(param);
                // to do 匹配属性
                default: return undefined;
            }
        }

        /**
         * 同步指标节点下的全部指标参数
         * @param {Number} nodeId
         * @private
         */
        _syncNodeParam(sourceId, nodeId, nodeBills) {
            const nodeParams = this.templateParams.filter(function (p) {
                return p.node_id === sourceId;
            });
            for (const np of nodeParams) {
                const newParam = {
                    node_id: nodeId,
                    lib_id: nodeBills.lib_id,
                    param_id: this.params.length + 1,
                    source_id: np.param_id,
                    code: np.code,
                    name: np.name,
                    match_type: np.match_type,
                    match_key: np.match_key,
                    match_num: np.match_num,
                }
                newParam.match_value = this._getParamValue(newParam, nodeBills);
                newParam.calc_value = newParam.match_value;
                this.params.push(newParam);
            }
        }

        /**
         * 同步指标节点下的全部指标
         * @param {Number} nodeId
         * @private
         */
        _syncNodeIndex(node) {
            const nodeIndexes = this.templateIndexes.filter(function (i) {
                return i.node_id === node.source_id;
            });
            for (const ni of nodeIndexes) {
                const newIndex = {
                    node_id: node.node_id,
                    lib_id: node.lib_id,
                    index_id: this.indexes.length + 1,
                    source_id: ni.index_id,
                    code: ni.code,
                    name: ni.name,
                    unit1: ni.unit1,
                    unit2: ni.unit2,
                    rule: ni.rule,
                    calc_rule: ni.calc_rule,
                    parse_rule: ni.parse_rule,
                    index_type: ni.index_type,
                }
                this.indexes.push(newIndex);
            }
        }

        /**
         * 匹配指标节点
         * @param node
         * @private
         */
        _matchNode(node) {
            if (!node) { return; }

            const matchedBills = this._filterBills(node);
            for (const mb of matchedBills) {
                const newNode = {
                    node_id: this.nodes.length + 1,
                    lib_id: mb.lib_id,
                    source_id: node.node_id,
                    code: node.code,
                    name: node.name,
                    match_type: node.match_type,
                    match_key: node.match_key,
                    bills_id: mb.n_id,
                }
                this.nodes.push(newNode);
                this._syncNodeParam(newNode.source_id, newNode.node_id, mb);
                this._syncNodeIndex(newNode);
                mb.match_node = newNode.node_id;
            }
        }

        /**
         * 按模板匹配规则，给清单匹配指标节点
         *
         * @param {Array} bills - 匹配的清单
         * @param {Number} templateId - 用户匹配的模板Id
         * @returns {Promise<void>}
         */
        async matchBills (bills, templateId = 1) {
            this._init(bills);
            // 获取指标模板全部数据
            await this._getTemplateData(templateId);
            // 同步全局指标参数
            this._syncNodeParam(paramConst.globalParamNodeId, paramConst.globalParamNodeId, this.bills[0]);
            // 遍历模板中所有指标节点，匹配清单
            for (const node of this.templateNodes) {
                this._matchNode(node);
            }
            // 计算全部指标节点
            const globalParams = this.params.filter(function (p) {
                return p.node_id === paramConst.globalParamNodeId;
            });
            for (const node of this.nodes) {
                const nodeParams = this.params.filter(function (n) {
                    return n.node_id === node.node_id;
                });
                const nodeIndexes = this.indexes.filter(function (i) {
                    return i.node_id === node.node_id;
                });
                this.ctx.service.indexCalc.calculate(nodeIndexes, globalParams, nodeParams);
            }
        }
    };

    return Match;
}