'use strict';

/**
 * 期计量 - 部位明细计量
 *
 * @author Mai
 * @date 2018/12/8
 * @version
 */
const calcFields = ['contract_qty', 'qc_qty'];
const auditConst = require('../const/audit');
const timesLen = auditConst.stage.timesLen;

module.exports = app => {
    class StageBills extends app.BaseService {
        /**
         * 构造函数
         *
         * @param {Object} ctx - egg全局变量
         * @return {void}
         */
        constructor(ctx) {
            super(ctx);
            this.tableName = 'stage_bills';
        }

        /**
         * 查询期计量最后审核人数据
         * @param {Number} tid - 标段id
         * @param {Number} sid - 期id
         * @param {Number|Array} lid - 台账节点id（可以为空）
         * @returns {Promise<*>}
         */
        async getLastestStageData(tid, sid, lid) {
            let lidSql = '', result;
            if (lid) {
                if (lid instanceof Array) {
                    lidSql = lid.length > 0 ? ' And lid in (' + this.ctx.helper.getInArrStrSqlFilter(lid) + ')' : '';
                } else {
                    lidSql = ' And lid in (' + this.db.escape(lid) + ')';
                }
            }
            const sql = 'SELECT Bills.* FROM ' + this.tableName + ' As Bills ' +
                        '  INNER JOIN ( ' +
                        '    SELECT MAX(`times` * ' + timesLen + ' + `order`) As `progress`, `lid`, `sid` From ' + this.tableName +
                        '      WHERE tid = ? And sid = ?' + lidSql +
                        '      GROUP BY `lid`' +
                        '  ) As MaxFilter ' +
                        '  ON (Bills.times * ' + timesLen + ' + `order`) = MaxFilter.progress And Bills.lid = MaxFilter.lid And Bills.`sid` = MaxFilter.`sid`';
            const sqlParam = [tid, sid];
            if (!lid) {
                return await this.db.query(sql, sqlParam);
            } else if (lid instanceof Array) {
                return await this.db.query(sql, sqlParam);
            } else {
                return await this.db.queryOne(sql, sqlParam);
            }
        }

        /**
         * 查询 某期 某轮审批 某人数据
         * @param {Number} tid - 标段id
         * @param {Number} sid - 期id
         * @param {Number} times - 第几轮
         * @param {Number} order - 流程
         * @param {Number|Array} lid - 台账节点id（可以为空）
         * @returns {Promise<*>}
         */
        async getAuditorStageData(tid, sid, times, order, lid) {
            const lidSql = lid ? ' And Bills.lid in (?)' : '';
            const sql = 'SELECT Bills.* FROM ' + this.tableName + ' As Bills ' +
                '  INNER JOIN ( ' +
                '    SELECT MAX(`times` * ' + timesLen + ' + `order`) As `progress`, `lid`, `tid`, `sid` From ' + this.tableName +
                '      WHERE `times` < ? OR (`times` = ? AND `order` <= ?) And tid = ? And sid = ?' + lidSql +
                '      GROUP BY `lid`' +
                '  ) As MaxFilter ' +
                '  ON (Bills.times * ' + timesLen + ' + `order`) = MaxFilter.progress And Bills.lid = MaxFilter.lid' +
                '    AND Bills.sid = MaxFilter.sid';
            const sqlParam = [times, times, order, tid, sid];
            if (!lid) {
                return await this.db.query(sql, sqlParam);
            } else if (lid instanceof Array) {
                sqlParam.push(lid.join(', '));
                return await this.db.query(sql, sqlParam);
            } else {
                sqlParam.push(lid);
                return await this.db.queryOne(sql, sqlParam);
            }
        }

        /**
         * 获取截止本期数据
         * @param {Number} tid - 标段id
         * @param {Number} sorder - 截止期序号
         * @param {String|Array[String]} lid - 台账id
         * @returns {Promise<*>}
         */
        async getEndStageData(tid, sorder, lid) {
            let lidSql = '';
            if (lid) {
                if (lid instanceof Array) {
                    lidSql = lid.length > 0 ? ' And lid in (' + this.ctx.helper.getInArrStrSqlFilter(lid) + ')' : '';
                } else {
                    lidSql = ' And lid in (' + this.db.escape(lid) + ')';
                }
            }

            const sql = 'SELECT Bills.tid, Bills.lid,' +
                '  Sum(Bills.contract_qty) As contract_qty, Sum(Bills.contract_tp) As contract_tp,' +
                '  Sum(Bills.qc_qty) As qc_qty, Sum(Bills.qc_tp) As qc_tp FROM ' + this.tableName + ' As Bills ' +
                '  INNER JOIN ( ' +
                '    SELECT MAX(`times` * ' + timesLen + ' + `order`) As `progress`, `lid`, `sid` From ' + this.tableName +
                '      WHERE tid = ? ' + lidSql +
                '      GROUP BY `lid`, `sid`' +
                '  ) As MaxFilter ' +
                '  ON (Bills.times * ' + timesLen + ' + `order`) = MaxFilter.progress And Bills.lid = MaxFilter.lid And Bills.`sid` = MaxFilter.`sid`' +
                '  INNER JOIN ' + this.ctx.service.stage.tableName + ' As Stage' +
                '  ON Bills.sid = Stage.id' +
                '  WHERE Stage.order <= ?' +
                '  GROUP BY `lid`';
            const sqlParam = [tid, sorder];
            if (!lid) {
                return await this.db.query(sql, sqlParam);
            } else if (lid instanceof Array) {
                return await this.db.query(sql, sqlParam);
            } else {
                return await this.db.queryOne(sql, sqlParam);
            }
        }

        async getStageBills(tid, sid, lid) {
            const sql = 'SELECT Stage.*, Ledger.unit_price FROM ?? As Stage, ?? As Ledger ' +
                        '  Where Stage.tid = ?, Stage.sid = ?, Stage.lid = ?, Stage.lid = Ledger.id ' +
                        '  Order Stage.time DESC, Stage.order DESC ';
            const sqlParam = [this.tableName, this.ctx.service.ledger.tableName];
            sqlParam.push(this.db.escape(tid));
            sqlParam.push(this.db.escape(sid));
            sqlParam.push(this.db.escape(lid));
            return await this.db.queryOne(sql, sqlParam);
        }

        async _insertStageBillsData(transaction, insertData, ledgerData) {
            const info = this.ctx.tender.info;
            const d = {
                tid: this.ctx.tender.id,
                lid: ledgerData.id,
                sid: this.ctx.stage.id,
                times: this.ctx.stage.curTimes,
                order: this.ctx.stage.curOrder,
                said: this.ctx.session.sessionUser.accountId,
            };

            const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, ledgerData.unit);
            if (insertData.contract_qty) {
                d.contract_qty = this.round(insertData.contract_qty, precision.value);
                d.contract_tp = this.ctx.helper.mul(d.contract_qty, ledgerData.unit_price, info.decimal.tp);
            }
            if (insertData.qc_qty) {
                d.qc_qty = this.round(insertData.qc_qty, precision.value);
                d.qc_tp = this.ctx.helper.mul(d.qc_qty, ledgerData.unit_price, info.decimal.tp);
            }
            if (insertData.postil) {
                d.postil = insertData.postil;
            }
            await transaction.insert(this.tableName, d);
        }

        /**
         * 前端提交数据
         * @param {Object|Array} data - 提交的数据
         * @returns {Promise<void>}
         */
        async updateStageData(data) {
            const info = this.ctx.tender.info;
            const datas = data instanceof Array ? data : [data];
            const transaction = await this.db.beginTransaction();
            try {
                for (const d of datas) {
                    const stageBills = await this.getLastestStageData(this.ctx.tender.id, this.ctx.stage.id, d.lid);
                    const ledgerBills = await this.ctx.service.ledger.getDataById(d.lid);
                    const precision = this.ctx.helper.findPrecision(info.precision, ledgerBills.unit);
                    if (d.contract_qty !== undefined) {
                        d.contract_qty = this.round(d.contract_qty, precision.value);
                        d.contract_tp = this.ctx.helper.mul(d.contract_qty, ledgerBills.unit_price, info.decimal.tp);
                    }
                    if (d.qc_qty !== undefined) {
                        d.qc_qty = this.round(d.qc_qty, precision.value);
                        d.qc_tp = this.ctx.helper.mul(d.qc_qty, ledgerBills.unit_price, info.decimal.tp);
                    }
                    if (!stageBills) {
                        d.tid = this.ctx.tender.id;
                        d.sid = this.ctx.stage.id;
                        d.said = this.ctx.session.sessionUser.accountId;
                        d.times = this.ctx.stage.curTimes;
                        d.order = this.ctx.stage.curOrder;
                        await transaction.insert(this.tableName, d);
                    } else {
                        d.id = stageBills.id;
                        await transaction.update(this.tableName, d);
                    }
                }
                await transaction.commit();
            } catch (err) {
                await transaction.rollback();
                throw err;
            }
            return await this.getLastestStageData(this.ctx.tender.id, this.ctx.stage.id, this._.map(datas, 'lid'));
        }

        /**
         * 根据
         * @param transaction
         * @param ledgerBills
         * @param stageBills
         * @param data
         * @returns {Promise<void>}
         * @private
         */
        async updateStageBillsQty(transaction, ledgerBills, stageBills, data) {
            const info = this.ctx.tender.info;
            const precision = this.ctx.helper.findPrecision(info.precision, ledgerBills.unit);
            const updateData = {};
            if (data.contract_qty !== undefined) {
                updateData.contract_qty = this.round(data.contract_qty, precision.value);
                updateData.contract_tp = this.ctx.helper.mul(updateData.contract_qty, ledgerBills.unit_price, info.decimal.tp);
            }
            if (data.qc_qty !== undefined) {
                updateData.qc_qty = this.round(data.qc_qty, precision.value);
                updateData.qc_tp = this.ctx.helper.mul(updateData.qc_qty, ledgerBills.unit_price, info.decimal.tp);
            }
            if (stageBills) {
                if ((updateData.contract_qty === undefined || stageBills.contract_qty !== updateData.contract_qty) ||
                    (updateData.qc_qty === undefined || stageBills.qc_qty !== updateData.qc_qty)) {
                    if (stageBills.times === this.ctx.stage.curTimes && stageBills.order === this.ctx.stage.curOrder) {
                        updateData.id = stageBills.id;
                        await transaction.update(this.tableName, updateData);
                    } else {
                        await this._insertStageBillsData(transaction, updateData, ledgerBills);
                    }
                }
            } else {
                await this._insertStageBillsData(transaction, updateData, ledgerBills);
            }
        };
        /**
         * 重算 本期计量 数量 (根据部位明细)
         * @param {Number} tid - 标段id
         * @param {Number} id - 需要计算的节点的id
         * @param {Number} lid - 台账id
         * @param {Object} transaction - 操作所属事务
         * @returns {Promise<void>}
         */
        async calc(tid, sid, lid, transaction) {
            const info = this.ctx.tender.info;
            const stageBills = await this.getLastestStageData(tid, sid, lid);
            const ledgerBills = await this.ctx.service.ledger.getDataById(lid);
            if (!ledgerBills) {
                throw '提交数据错误';
            }
            const posGather = await this.ctx.service.stagePos.getPosGather(tid, sid, lid, transaction);
            if (!posGather) { return; }

            const precision = this.ctx.helper.findPrecision(info.precision, ledgerBills.unit);
            // 计算
            if (posGather.contract_qty !== undefined) {
                posGather.contract_qty = this.round(posGather.contract_qty, precision.value);
                posGather.contract_tp = this.ctx.helper.mul(posGather.contract_qty, ledgerBills.unit_price, info.decimal.tp);
            }
            if (posGather.qc_qty !== undefined) {
                posGather.qc_qty = this.round(posGather.qc_qty, precision.value);
                posGather.qc_tp = this.ctx.helper.mul(posGather.qc_qty, ledgerBills.unit_price, info.decimal.tp);
            }
            if (stageBills) {
                if (stageBills.contract_qty === posGather.contract_qty && stageBills.qc_qty === posGather.qc_qty) {
                    return;
                } else {
                    const curOrder = this.ctx.stage.curAuditor ? this.ctx.stage.curAuditor.order : 0;
                    if (stageBills.times === this.ctx.stage.curTimes && stageBills.order === this.ctx.stage.curOrder) {
                        posGather.id = stageBills.id;
                        await transaction.update(this.tableName, posGather);
                    } else {
                        await this._insertStageBillsData(transaction, posGather, ledgerBills);
                    }
                }
            } else {
                await this._insertStageBillsData(transaction, posGather, ledgerBills);
            }
        }

        async getSumTotalPrice(stage) {
            const sql = 'SELECT Sum(`contract_tp`) As `contract_tp`, Sum(`qc_tp`) As `qc_tp` FROM ' + this.tableName + ' As Bills ' +
                '  INNER JOIN ( ' +
                '    SELECT MAX(`times` * ' + timesLen + ' + `order`) As `flow`, `lid` From ' + this.tableName +
                '      WHERE `times` <= ? AND `order` <= ?' +
                '      GROUP BY `lid`' +
                '  ) As MaxFilter ' +
                '  ON (Bills.times * ' + timesLen + ' + `order`) = MaxFilter.flow And Bills.lid = MaxFilter.lid' +
                '  WHERE Bills.sid = ?';
            const sqlParam = [stage.curTimes, stage.curOrder, stage.id];
            const result = await this.db.queryOne(sql, sqlParam);
            return result;
        }

        async getSumTotalPriceFilter(stage, operate, filter) {
            const sql = 'SELECT Sum(`contract_tp`) As `contract_tp`, Sum(`qc_tp`) As `qc_tp`' +
                '  FROM ' + this.tableName + ' As Bills ' +
                '  INNER JOIN ( ' +
                '    SELECT MAX(`times` * ' + timesLen + ' + `order`) As `flow`, `lid` From ' + this.tableName +
                '      WHERE `times` <= ? AND `order` <= ?' +
                '      GROUP BY `lid`' +
                '  ) As MaxFilter ' +
                '  ON (Bills.times * ' + timesLen + ' + `order`) = MaxFilter.flow And Bills.lid = MaxFilter.lid ' +
                '  INNER JOIN ' + this.ctx.service.ledger.tableName + ' As Ledger ON Bills.lid = Ledger.id' +
                '  WHERE Bills.sid = ? And Ledger.b_code ' + operate + ' ?';
            const sqlParam = [stage.times, stage.curAuditor ? stage.curAuditor.order : 0, stage.id, filter];
            const result = await this.db.queryOne(sql, sqlParam);
            return result;
        }

        async getSumTotalPriceGcl(stage, regText) {
            if (regText) {
                return await this.getSumTotalPriceFilter(stage, 'REGEXP', regText);
            } else {
                return await this.getSumTotalPriceFilter(stage, '<>', this.db.escape(''));
            }
        }

        async getSumTotalPriceNotGcl(stage) {
            return await this.getSumTotalPriceFilter(stage, '=', this.db.escape(''));
        }
    }

    return StageBills;
};

