'use strict';

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

const keyPre = 'revise_bills_maxLid:';
const tidField = 'tender_id';
const lidField = 'ledger_id';
const pidField = 'ledger_pid';
const pathField = 'full_path';

module.exports = app => {
    class ReviseBills extends app.BaseService {

        /**
         * 构造函数
         *
         * @param {Object} ctx - egg全局变量
         * @return {void}
         */
        constructor(ctx) {
            super(ctx);
            this.tableName = 'revise_bills';
        }

        /**
         * 获取 修订 清单数据
         * @param {Number}tid - 标段id
         * @returns {Promise<void>}
         */
        async getData(tid) {
            return await this.db.select(this.tableName, {
                where: {tender_id: tid}
            });
        }

        /**
         * 获取节点数据
         * @param {Number} tid - 标段id
         * @param {Number} lid - 标段内，台账id
         * @returns {Promise<void>}
         */
        async getDataByLid (tid, lid) {
            return await this.db.get(this.tableName, {tender_id: tid, ledger_id: lid});
        }

        /**
         * 获取节点数据
         * @param id
         * @returns {Promise<Array>}
         */
        async getDataById(id) {
            if (id instanceof Array) {
                return await this.db.select(this.tableName, { where: {id: id} });
            } else {
                return await this.db.get(this.tableName, { id: id });
            }
        }

        /**
         * 获取最末的子节点
         * @param {Number} tenderId - 标段id
         * @param {Number} pid - 父节点id
         * @return {Object}
         */
        async getLastChildData(tid, pid) {
            this.initSqlBuilder();
            this.sqlBuilder.setAndWhere('tender_id', {
                value: tid,
                operate: '=',
            });
            this.sqlBuilder.setAndWhere('ledger_pid', {
                value: pid,
                operate: '=',
            });
            this.sqlBuilder.orderBy = [['order', 'DESC']];
            const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
            const resultData = await this.db.queryOne(sql, sqlParam);

            return resultData;
        }
        /**
         * 根据 父节点id 和 节点排序order 获取数据
         *
         * @param {Number} tid - 标段id
         * @param {Number} pid - 父节点id
         * @param {Number|Array} order - 排序
         * @return {Object|Array} - 查询结果
         */
        async getDataByParentAndOrder(tid, pid, order) {
            const result = await this.db.select(this.tableName, {where: {
                tender_id: tid, ledger_pid: pid, order: order
            }});
            return order instanceof Array ? result : (result.length > 0 ? result[0] : null);
        }
        /**
         * 根据 父节点ID 和 节点排序order 获取全部后节点数据
         * @param {Number} tid - 标段id
         * @param {Number} pid - 父节点id(ledger_pid)
         * @param {Number} order - 排序
         * @return {Array}
         */
        async getNextsData(tid, pid, order) {
            this.initSqlBuilder();
            this.sqlBuilder.setAndWhere('tender_id', {
                value: tid,
                operate: '=',
            });
            this.sqlBuilder.setAndWhere('ledger_pid', {
                value: pid,
                operate: '=',
            });
            this.sqlBuilder.setAndWhere('order', {
                value: order,
                operate: '>',
            });

            const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
            const data = await this.db.query(sql, sqlParam);

            return data;
        }

        /**
         * 获取最大节点id
         *
         * @param {Number} tid - 台账id
         * @return {Number}
         * @private
         */
        async _getMaxLid(tid) {
            const cacheKey = keyPre + tid;
            let maxId = parseInt(await this.cache.get(cacheKey));
            if (!maxId) {
                const sql = 'SELECT Max(??) As max_id FROM ?? Where tender_id = ?';
                const sqlParam = ['ledger_id', this.tableName, tid];
                const queryResult = await this.db.queryOne(sql, sqlParam);
                maxId = queryResult.max_id || 0;
                this.cache.set(cacheKey, maxId, 'EX', this.ctx.app.config.cacheTime);
            }
            return maxId;
        }

        /**
         * 缓存最大节点id
         *
         * @param {Number} tid - 台账id
         * @param {Number} maxId - 当前最大节点id
         * @returns {Promise<void>}
         * @private
         */
        async _cacheMaxLid(tid, maxId) {
            this.cache.set(keyPre + tid , maxId, 'EX', this.ctx.app.config.cacheTime);
        }

        /**
         * 更新order
         * @param {Number} tid - 台账id
         * @param {Number} pid - 父节点id（ledger_id）
         * @param {Number} order - 开始更新的order
         * @param {Number} incre - 更新的增量
         * @returns {Promise<*>}
         * @private
         */
        async _updateChildrenOrder(tid, pid, order, incre = 1) {
            this.initSqlBuilder();
            this.sqlBuilder.setAndWhere('tender_id', {
                value: tid,
                operate: '=',
            });
            this.sqlBuilder.setAndWhere('order', {
                value: order,
                operate: '>=',
            });
            this.sqlBuilder.setAndWhere('ledger_pid', {
                value: pid,
                operate: '=',
            });
            this.sqlBuilder.setUpdateData('order', {
                value: Math.abs(incre),
                selfOperate: incre > 0 ? '+' : '-',
            });
            const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'update');
            const data = await this.transaction.query(sql, sqlParam);

            return data;
        }

        /**
         * 新增数据(新增为selectData的后项，该方法不可单独使用)
         *
         * @param {Number} tid - 台账id
         * @param {uuid} rid - 修订id
         * @param {Object} select - 选中节点的数据
         * @param {Object} data - 新增节点的初始数据
         * @return {Object} - 新增结果
         * @private
         */
        async _addNodeData(tid, rid, select, data) {
            if (!data) {
                data = {};
            }
            const maxId = await this._getMaxLid(tid);

            data.id = this.uuid.v4();
            data.crid = rid;
            data.ledger_id = maxId + 1;
            data.ledger_pid = select.ledger_pid;
            data.tender_id = tid;
            data.level = select.level;
            data.order = select.order + 1;
            data.full_path = select.full_path.replace('.' + select.ledger_id, '.' + data.ledger_id);
            data.is_leaf = true;
            const result = await this.transaction.insert(this.tableName, data);

            this._cacheMaxLid(tid, maxId + 1);

            return result;
        }

        /**
         * 新增节点
         * @param {Number} tid - 台账id
         * @param {uuid} rid - 修订id
         * @param {Number} lid - 清单节点id
         * @returns {Promise<void>}
         */
        async addNode(tid, rid, lid) {
            if (!tid || !rid || !lid) return null;
            const select = await this.getDataByLid(tid, lid);
            if (!select) {
                throw '新增节点数据错误';
            }

            this.transaction = await this.db.beginTransaction();
            try {
                await this._updateChildrenOrder(tid, select.ledger_pid, select.order+1);
                const newNode = await this._addNodeData(tid, rid, select);
                if (newNode.affectedRows !== 1) {
                    throw '新增节点数据额错误';
                }
                await this.transaction.commit();
                this.transaction = null;
            } catch (err) {
                await this.transaction.rollback();
                this.transaction = null;
                throw err;
            }

            const createData = await this.getDataByParentAndOrder(tid, select.ledger_pid, [select.order + 1]);
            const updateData = await this.getNextsData(tid, select.ledger_pid, select.order + 1);
            return {create: createData, update: updateData};
        }

        /**
         * 上移节点
         *
         * @param {Number} tid - 台账id
         * @param {Number} lid - 选中节点id
         * @return {Array} - 发生改变的数据
         */
        async upMoveNode(tid, lid) {
            if (!tid || !lid) return null;
            const select = await this.getDataByLid(tid, lid);
            if (!select) {
                throw '上移节点数据错误';
            }
            const pre = await this.getDataByParentAndOrder(tid, select.ledger_pid, select.order - 1);
            if (!pre) {
                throw '节点不可上移';
            }

            this.transaction = await this.db.beginTransaction();
            try {
                const sData = await this.transaction.update(this.tableName, { id: select.id, order: select.order - 1 });
                const pData = await this.transaction.update(this.tableName, { id: pre.id, order: pre.order + 1 });
                await this.transaction.commit();
            } catch (err) {
                await this.transaction.rollback();
                throw err;
            }

            const resultData = await this.getDataByParentAndOrder(tid, select.ledger_pid, [select.order, pre.order]);
            return { update: resultData };
        }

        /**
         * 下移节点
         *
         * @param {Number} tid - 台账id
         * @param {Number} lid - 选中节点id
         * @return {Array} - 发生改变的数据
         */
        async downMoveNode(tid, lid) {
            if (!tid || !lid) return null;
            const select = await this.getDataByLid(tid, lid);
            if (!select) {
                throw '下移节点数据错误';
            }
            const next = await this.getDataByParentAndOrder(tid, select.ledger_pid, select.order + 1);
            if (!next) {
                throw '节点不可下移';
            }

            this.transaction = await this.db.beginTransaction();
            try {
                const sData = await this.transaction.update(this.tableName, { id: select.id, order: select.order + 1 });
                const pData = await this.transaction.update(this.tableName, { id: next.id, order: next.order - 1 });
                await this.transaction.commit();
            } catch (err) {
                await this.transaction.rollback();
                throw err;
            }

            const resultData = await this.getDataByParentAndOrder(tid, select.ledger_pid, [select.order, next.order]);
            return { update: resultData };
        }

        /**
         * 批量插入子项
         * @param {uuid} rid - 修订id
         * @param {Number} lid - 节点id
         * @param {Object} data - 批量插入数据
         * @return {Promise<void>}
         */
        async batchInsertChild(tid, rid, lid, data) {
            const result = { ledger: {}, pos: null };
            if (!tid || !rid || !lid) return result;

            const select = await this.getDataByLid(tid, lid);
            if (!select) {
                throw '位置数据错误';
            }

            // 计算id和order
            const maxId = await this._getMaxLid(tid);
            const lastChild = await this.getLastChildData(tid, lid);
            const order = lastChild ? lastChild.order : 0;
            // 整理数据
            const bills = [], pos = [], newIds = [];
            for (let i = 0, iLen = data.length; i < iLen; i++) {
                // 合并新增数据
                const qd = {
                    crid: rid,
                    id: this.uuid.v4(),
                    tender_id: tid,
                    ledger_id: maxId + i + 1,
                    ledger_pid: select.ledger_id,
                    is_leaf: true,
                    order: order + i + 1,
                    level: select.level + 1,
                    b_code: data[i].b_code,
                    name: data[i].name,
                    unit: data[i].unit,
                    unit_price: data[i].price,
                };

                const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, bills.unit);
                qd.sgfh_qty = 0;
                for (const p of data[i].pos) {
                    const inP = {
                        id: this.uuid.v4(), tid: tid, lid: qd.id, crid: rid,
                        add_stage: 0, add_times: 0, add_user: this.ctx.session.sessionUser.accountId,
                        name: p.name, drawing_code: p.drawing_code,
                    };
                    if (p.quantity) {
                        inP.sgfh_qty = this.round(p.quantity, precision.value);
                        inP.quantity = inP.sgfh_qty;
                    }
                    qd.sgfh_qty = this.ctx.helper.add(qd.sgfh_qty, inP.sgfh_qty);
                    pos.push(inP);
                }
                qd.full_path = select.full_path + '.' + qd.ledger_id;
                qd.sgfh_tp = this.ctx.helper.mul(qd.sgfh_qty, qd.unit_price, this.ctx.tender.info.decimal.tp);
                qd.quantity = qd.sgfh_qty;
                qd.total_price = qd.sgfh_tp;
                bills.push(qd);
                newIds.push(qd.id);
            }

            this.transaction = await this.db.beginTransaction();
            try {
                // 更新父项isLeaf
                if (!lastChild) {
                    await this.transaction.update(this.tableName, {
                        is_leaf: false,
                        unit_price: null,
                        quantity: null,
                        total_price: null,
                        deal_qty: null,
                        deal_tp: null,
                    }, {tender_id: rid, id: select.id});
                }
                // 数据库创建新增节点数据
                await this.transaction.insert(this.tableName, bills);
                await this.transaction.insert(this.ctx.service.revisePos.tableName, pos);
                this._cacheMaxLid(tid, maxId + data.length);
                await this.transaction.commit();
            } catch (err) {
                await this.transaction.rollback();
                throw err;
            }

            // 查询应返回的结果
            result.ledger.create = await this.getDataById(newIds);
            if (!lastChild) {
                result.ledger.update = await this.getDataByLid(select.id);
            }
            result.pos = await this.ctx.service.revisePos.getDataByLid(tid, newIds);
            return result;
        }

        /**
         * 批量插入后项
         * @param {Number} tid - 台账id
         * @param {uuid} rid - 修订id
         * @param {Number} lid - 节点id
         * @param {Object} data - 批量插入数据
         * @return {Promise<void>}
         */
        async batchInsertNext(tid, rid, lid, data) {
            const result = { ledger: {}, pos: null };
            if (!tid || !rid || !lid) return result;

            const select = await this.getDataByLid(tid, lid);
            if (!select) {
                throw '位置数据错误';
            }
            const parentData = await this.getDataByLid(tid, select.ledger_pid);
            if (!parentData) {
                throw '位置数据错误';
            }

            // 计算id和order
            const maxId = await this._getMaxLid(tid);
            const order = select.order;
            // 整理数据
            const bills = [], pos = [], newIds = [];
            for (let i = 0, iLen = data.length; i < iLen; i++) {
                // 合并新增数据
                const qd = {
                    crid: rid,
                    id: this.uuid.v4(),
                    tender_id: tid,
                    ledger_id: maxId + i + 1,
                    ledger_pid: parentData.ledger_id,
                    is_leaf: true,
                    order: order + i + 1,
                    level: parentData.level + 1,
                    b_code: data[i].b_code,
                    name: data[i].name,
                    unit: data[i].unit,
                    unit_price: data[i].price,
                };

                const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, bills.unit);
                qd.sgfh_qty = 0;
                for (const p of data[i].pos) {
                    const inP = {
                        id: this.uuid.v4(), tid: tid, lid: qd.id, crid: rid,
                        add_stage: 0, add_times: 0, add_user: this.ctx.session.sessionUser.accountId,
                        name: p.name, drawing_code: p.drawing_code,
                    };
                    if (p.quantity) {
                        inP.sgfh_qty = this.round(p.quantity, precision.value);
                        inP.quantity = inP.sgfh_qty;
                    }
                    qd.sgfh_qty = this.ctx.helper.add(qd.sgfh_qty, inP.sgfh_qty);
                    pos.push(inP);
                }
                qd.full_path = parentData.full_path + '.' + qd.ledger_id;
                qd.sgfh_tp = this.ctx.helper.mul(qd.sgfh_qty, qd.unit_price, this.ctx.tender.info.decimal.tp);
                qd.quantity = qd.sgfh_qty;
                qd.total_price = qd.sgfh_tp;
                bills.push(qd);
                newIds.push(qd.id);
            }

            this.transaction = await this.db.beginTransaction();
            try {
                // 选中节点的所有后兄弟节点，order+粘贴节点个数
                await this._updateChildrenOrder(tid, select.ledger_pid, select.order+1);
                // 数据库创建新增节点数据
                await this.transaction.insert(this.tableName, bills);
                await this.transaction.insert(this.ctx.service.revisePos.tableName, pos);
                this._cacheMaxLid(tid, maxId + data.length);
                await this.transaction.commit();
            } catch (err) {
                await this.transaction.rollback();
                throw err;
            }

            // 查询应返回的结果
            result.ledger.create = await this.getDataById(newIds);
            result.ledger.update = await this.getNextsData(select.tender_id, select.ledger_pid, select.order + data.length);
            result.pos = await this.ctx.service.revisePos.getDataByLid(tid, newIds);
            return result;
        }
    }

    return ReviseBills;
};