'use strict';

/**
 *
 *
 * @author Mai
 * @date
 * @version
 */

const timesLen = require('../const/audit').stage.timesLen;

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

        /**
         * 查询 某期 某轮审批 某审核人数据
         *
         * @param {Number} pid - 合同支付id
         * @param {Number} sid - 期id
         * @param {Number} times - 期第几轮审批
         * @param {Number} order - 审核人顺序
         * @param {Number|Array|Null} pid - 部位明细id - 为空则查询全部
         * @returns {Promise<*>}
         */
        async getAuditorStagePay(pid, sid, times, order) {
            const pidSql = pid instanceof Array ? ' And SP.`pid` in (' + pid.join(',') + ')' : 'And SP.`pid` = ' + pid;
            const sql = 'SELECT SP.*, P.`csorder`, P.`cstimes`, P.`csaorder`, P.`order`, P.uid, P.minus, P.ptype, P.sprice, P.sexpr, P.rprice, P.rexpr, P.is_yf, P.dl_type, P.dl_count, P.dl_tp_type, P.dl_tp ' +
                '  FROM ?? As SP, ?? As P ' +
                '  WHERE SP.`sid` = ? AND SP.`stimes` = ? AND SP.`sorder` = ? AND SP.`pid` = P.`id` AND P.`valid`' + pidSql +
                '  ORDER BY P.`order`';
            const sqlParam = [this.tableName, this.ctx.service.pay.tableName, sid, times, order, pid];
            if (pid instanceof Array) {
                return await this.db.query(sql, sqlParam);
            } else {
                return await this.db.queryOne(sql, sqlParam);
            }
        }

        /**
         * 查询 某期 某轮审批 某审核人数据
         * @param {Number} sid - 期id
         * @param {Number} times - 期第几轮审批
         * @param {Number} order - 审核人顺序
         * @param {Number|Array|Null} pid - 部位明细id - 为空则查询全部
         * @returns {Promise<*>}
         */
        async getAuditorStageData(sid, times, order) {
            const sql = 'SELECT SP.*,' +
                '    P.`csorder`, P.`cstimes`, P.`csaorder`, P.`order`, P.uid, P.minus, P.ptype, P.sprice, P.sexpr, P.rprice, P.rexpr, P.is_yf, P.dl_type, P.dl_count, P.dl_tp_type, P.dl_tp ' +
                '  FROM ?? As SP, ?? As P ' +
                '  WHERE SP.`sid` = ? AND SP.`stimes` = ? AND SP.`sorder` = ? AND SP.`pid` = P.`id` AND P.`valid`' +
                '  ORDER BY P.`order`';
            const sqlParam = [this.tableName, this.ctx.service.pay.tableName, sid, times, order];
            return await this.db.query(sql, sqlParam);
        }

        /**
         * 新增期时，初始化 期--合同支付 数据
         * @param {Object} stage - 新增的期
         * @param transaction - 事务
         * @returns {Promise<void>}
         */
        async addInitialStageData(stage, transaction) {
            if (!stage) {
                throw '初始化期合同支付数据失败';
            }
            let pays = await this.ctx.service.pay.getAllDataByCondition({where: { tid: this.ctx.tender.id } });
            // 兼容旧项目，无初始化合同支付数据
            if (!pays || pays.length === 0) {
                const result = await this.ctx.service.pay.addDefaultPayData(this.ctx.tender.id, transaction);
                if (!result) {
                    throw '初始化合同支付数据失败';
                }
                pays = await transaction.select(this.ctx.service.pay.tableName, {where: { tid: this.ctx.tender.id } });
            }
            const stagePays = [];
            for (const p of pays) {
                stagePays.push({
                    tid: p.tid, sid: stage.id, pid: p.id,
                    stimes: stage.times, sorder: 0, expr: p.expr, name: p.name,
                });
            }
            // 获取截止上期数据
            if (stage.order > 1) {
                const preStage = await this.ctx.service.stage.getDataByCondition({tid: stage.tid, order: stage.order - 1});
                if (!preStage) {
                    throw '标段数据有误';
                }
                const prePays = await this.getStageLastestPays(preStage.id);
                for (const pp of prePays) {
                    const sp = this._.find(stagePays, {pid: pp.pid});
                    sp.name = pp.name;
                    sp.expr = pp.expr;
                    sp.pre_tp= pp.end_tp;
                    sp.pause = pp.pause;
                }
            }
            let result;
            if (transaction) {
                result = await transaction.insert(this.tableName, stagePays);
            } else {
                result = await this.db.insert(this.tableName, stagePays);
            }
            return result.affectedRows === pays.length;
        }

        async getStagePay(stage, pid) {
            return await this.getAuditorStagePay(pid, stage.id, stage.curTimes, stage.curOrder);
        }

        /**
         * 获取某期合同支付数据
         *
         * @param {Object} stage - 期数据(通过StageController._getStageData()获取)
         * @returns {Promise<*>}
         */
        async getStagePays(stage) {
            return await this.getAuditorStageData(stage.id, stage.curTimes, stage.curOrder);
        }

        /**
         * 获取某期最后数据(不限制该期是否已经审批通过)
         *
         * @param {Number} sid - 期id
         * @returns {Promise<*>}
         */
        async getStageLastestPays(sid) {
            const sql = 'SELECT SP.*, P.`order`, P.uid, P.minus, P.ptype, P.sprice, P.sexpr, P.rprice, P.rexpr, P.is_yf, P.dl_type, P.dl_count, P.dl_tp_type, P.dl_tp ' +
                '  FROM ' + this.tableName + ' As SP' +
                '  INNER JOIN ' + this.ctx.service.pay.tableName + ' As P ON SP.pid = P.id' +
                '  INNER JOIN ( ' +
                '    SELECT MAX(`stimes` * ' + timesLen + ' + `sorder`) As `sprogress` ' +
                '      FROM ' + this.tableName +
                '      WHERE `sid` = ? ' +
                '      GROUP BY `sid`) As M ON (SP.`stimes` * ' + timesLen + ' + SP.`sorder`) = M.`sprogress`' +
                '  WHERE SP.`sid` = ? AND P.`valid` = true' +
                '  ORDER BY P.`order`';
            const sqlParam = [sid, sid];
            return await this.db.query(sql, sqlParam);
        }

        /**
         * 同步新增 (仅供合同支付新增时调用)
         * @param {Object} pay - 合同支付项数据
         * @param transaction - 新增合同支付的事务
         * @returns {Promise<{tid, sid: number|*, pid, stimes: number|*, sorder: number|*}>}
         */
        async syncAdd(pay, transaction) {
            const stagePay = {
                tid: pay.tid,
                sid: pay.csid,
                pid: pay.id,
                stimes: pay.cstimes,
                sorder: pay.csaorder,
            };
            const result = await transaction.insert(this.tableName, stagePay);
            if (result.affectedRows !== 1) {
                throw '新增数据失败';
            }
            stagePay.id = result.insertId;
            return stagePay;
        }

        /**
         * 保存本期金额/表达式
         * @param data
         * @returns {Promise<void>}
         */
        async save(saveData) {
            const datas = saveData instanceof Array ? saveData : [saveData];
            const transaction = await this.db.beginTransaction();
            try {
                // 更新数据
                for (const data of datas) {
                    const stagePay = await this.getStagePay(this.ctx.stage, data.pid);
                    const updateData = { id: stagePay.id };
                    if (data.name !== undefined) { updateData.name = data.name }
                    if (data.expr !== undefined) { updateData.expr = data.expr }
                    if (data.tp !== undefined) { updateData.tp = data.tp }
                    if (data.pause !== undefined) { updateData.pause = data.pause }
                    const result = await transaction.update(this.tableName, updateData);
                    if (result.affectedRows !== 1) {
                        throw '保存数据失败';
                    }
                    // 缓存至pay
                    // if (data.expr !== undefined) {
                    //     const pr = await transaction.update(this.ctx.service.pay.tableName, {
                    //         id: data.pid, expr: data.expr
                    //     });
                    //     if (pr.affectedRows !== 1) {
                    //         throw '保存数据失败';
                    //     }
                    // }
                }
                await transaction.commit();
            } catch(err) {
                await transaction.rollback();
                throw err;
            }
        }

        /**
         * 计算
         * @param stage
         * @param transaction
         * @returns {Promise<boolean>}
         */
        async calcAllStagePays(stage, transaction) {
            if (!stage || !transaction) {
                throw '计算数据错误';
            }
            const stagePays = await this.getStagePays(stage);
            const PayCalculator = require('../lib/pay_calc');
            const payCalculator = new PayCalculator(this.ctx, this.ctx.tender.info.decimal);
            await payCalculator.calculateAll(stagePays);
            for (const sp of stagePays) {
                await transaction.update(this.tableName, {
                    id: sp.id,
                    tp: sp.tp,
                    end_tp: sp.end_tp
                });
            }
        }

        /**
         * 拷贝上一操作人数据 为 下一操作人数据
         * @param stage - 期数据
         * @param times - 下一操作人 该期第几次
         * @param order - 下一操作人顺序
         * @param transaction - 事务
         * @returns {Promise<*>}
         */
        async copyAuditStagePays(stage, times, order, transaction) {
            if (!stage || !transaction || !times || order === undefined) {
                throw '数据错误';
            }
            const sql = 'INSERT INTO ?? (`tid`, `sid`, `pid`, `stimes`, `sorder`, `name`, `tp`, `expr`, `pause`, `attachment`, `pre_tp`) ' +
                        '  SELECT SP.`tid`, SP.`sid`, SP.`pid`, ?, ?, SP.name, SP.`tp`, SP.`expr`, SP.`pause`, SP.`attachment`, SP.`pre_tp` ' +
                        '  FROM ?? As SP, ?? As P ' +
                        '  WHERE SP.`sid` = ? AND SP.`stimes` = ? AND SP.`sorder` = ? And SP.`pid` = P.`id` And P.`valid`';
            const sqlParam = [this.tableName, times, order, this.tableName, this.ctx.service.pay.tableName,
                stage.id, stage.curTimes, stage.curOrder];
            return await transaction.query(sql, sqlParam);
        }

        /**
         * 保存附件
         * @param data
         * @returns {Promise<void>}
         */
        async saveAtt(id, att) {
            // 获取attachment
            const info = await this.getDataById(id);
            let attachment = null;
            if (info.attachment !== null && JSON.parse(info.attachment).length !== 0) {
                attachment = JSON.parse(info.attachment);
                attachment.unshift(att);
            } else {
                attachment = new Array(att);
            }
            const result = await this.db.update(this.tableName, { id, attachment: JSON.stringify(attachment) });
            return result.affectedRows === 1;
        }

        /**
         * 删除附件
         * @param data
         * @returns {Promise<void>}
         */
        async deleteAtt(id, attachment) {
            const result = await this.db.update(this.tableName, { id, attachment: JSON.stringify(attachment) });
            return result.affectedRows === 1;
        }
    }

    return StagePay;
};
