'use strict';

/**
 * 台账相关控制器
 *
 * @author CaiAoLin
 * @date 2017/11/30
 * @version
 */

const stdDataAddType = {
    withParent: 1,
    child: 2,
    next: 3,
};
const audit = require('../const/audit');
const auditConst = audit.ledger;
const tenderMenu = require('../../config/menu').tenderMenu;
const measureType = require('../const/tender').measureType;
const spreadConst = require('../const/spread');
const fs = require('fs');
const LzString = require('lz-string');
const accountGroup = require('../const/account_group').group;

module.exports = app => {

    class LedgerController extends app.BaseController {

        /**
         * 构造函数
         *
         * @param {Object} ctx - egg全局变量
         * @return {void}
         */
        constructor(ctx) {
            super(ctx);
            ctx.showProject = true;
            ctx.showTitle = true;
            ctx.showTender = true;
        }

        /**
         * 检查标段是否只读(审核中，审核完成)
         * @param {Object} tenderData
         * @return {boolean}
         * @private
         */
        _ledgerReadOnly() {
            const tender = this.ctx.tender.data;
            return tender.ledger_status === auditConst.status.checking || tender.ledger_status === auditConst.status.checked;
        }

        /**
         * 获取SpreadSetting
         * @private
         */
        _getSpreadSetting() {
            const _ = this.app._;
            function removeFieldCols(setting, cols) {
                _.remove(setting.cols, function(c) {
                    return cols.indexOf(c.field) > -1;
                });
            }
            function hideFieldCols(setting, cols) {
                for (const c of setting.cols) {
                    if (cols.indexOf(c.field) > -1) {
                        c.visible = false;
                    }
                }
            }
            function setColFormat(cols, field, formatter) {
                const col = _.find(cols, function (c) {
                    return c.field === field;
                });
                col.formatter = formatter;
            }
            // const tpFormatter = this.ctx.helper.getNumberFormatter(this.ctx.tender.info.decimal.tp);
            // const upFormatter = this.ctx.helper.getNumberFormatter(this.ctx.tender.info.decimal.up);
            const tender = this.ctx.tender;
            const setting = tender.info.display.ledger.clQty ? spreadConst.withCl : spreadConst.withoutCl;
            const ledger = JSON.parse(JSON.stringify(setting.ledger));
            // setColFormat(ledger.cols, 'unit_price', upFormatter);
            // setColFormat(ledger.cols, 'dgn_price', upFormatter);
            // setColFormat(ledger.cols, 'total_price', tpFormatter);
            // setColFormat(ledger.cols, 'deal_tp', tpFormatter);
            const pos = JSON.parse(JSON.stringify(setting.pos));

            if (this._ledgerReadOnly(tender.data)) {
                ledger.readOnly = true;
                pos.readOnly = true;
            }
            if (tender.data.measure_type === measureType.tz.value) {
                removeFieldCols(ledger, spreadConst.filterCols.tzWithoutCols);
            }
            if (!tender.info.display.ledger.dgnQty) {
                removeFieldCols(ledger, spreadConst.filterCols.dgnCols);
            }
            return [ledger, pos];
        }

        /**
         * 台账分解页面 (Get)
         *
         * @param {Object} ctx - egg全局变量
         * @return {void}
         */
        async explode(ctx) {
            try {
                const tender = ctx.tender;
                const [ledgerSpread, posSpread] = this._getSpreadSetting();

                const curAuditor = await ctx.service.ledgerAudit.getCurAuditor(tender.id, tender.data.ledger_times);
                const times = tender.data.ledger_status === auditConst.status.checkNo ? tender.data.ledger_times - 1 : tender.data.ledger_times;
                const auditors = await ctx.service.ledgerAudit.getAuditors(tender.id, times);
                const content = auditors.length > 0 ? await ctx.service.ledgerAuditContent.getAllDataByCondition({
                    where: { tender_id: tender.id, times, audit_id: auditors[0].audit_id },
                }) : null;
                const ledgerData = await ctx.service.ledger.getDataByTenderId(tender.id, -1);
                const user = await ctx.service.projectAccount.getAccountInfoById(ctx.tender.data.user_id);
                const auditHistory = [];
                if (ctx.tender.data.ledger_times > 1) {
                    for (let i = 1; i < ctx.tender.data.ledger_times; i++) {
                        auditHistory.push(await ctx.service.ledgerAudit.getAuditors(ctx.tender.id, i));
                    }
                }
                const renderData = {
                    tender: tender.data,
                    tenderInfo: tender.info,
                    auditConst,
                    auditors,
                    curAuditor,
                    user,
                    auditHistory,
                    content,
                    ledger: JSON.stringify(ledgerData),
                    ledgerSpreadSetting: JSON.stringify(ledgerSpread),
                    posSpreadSetting: JSON.stringify(posSpread),
                    tenderMenu,
                    preUrl: '/tender/' + tender.id,
                    measureType,
                    jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.ledger.explode),
                };
                if ((tender.data.ledger_status === auditConst.status.uncheck || tender.data.ledger_status === auditConst.status.checkNo) && tender.data.user_id === ctx.session.sessionUser.accountId) {
                    renderData.accountGroup = accountGroup;
                    // 获取所有项目参与者
                    const accountList = await ctx.service.projectAccount.getAllDataByCondition({
                        where: { project_id: ctx.session.sessionProject.id, enable: 1 },
                        columns: ['id', 'name', 'company', 'role', 'enable', 'is_admin', 'account_group'],
                    });
                    renderData.accountList = accountList;
                    renderData.auditorList = await ctx.service.ledgerAudit.getAuditors(tender.id, tender.data.ledger_times);
                }

                await this.layout('ledger/explode.ejs', renderData, 'ledger/explode_modal.ejs');
            } catch (err) {
                this.log(err);
                await this.layout('/dashboard');
            }
        }

        /**
         * 获取子节点 (Ajax)
         * @param ctx
         * @return {Promise<void>}
         */
        async getChildren(ctx) {
            const responseData = {
                err: 0,
                msg: '',
                data: [],
            };
            try {
                const data = JSON.parse(ctx.request.body.data);
                const id = data.ledger_id;
                if (isNaN(id) || id <= 0) {
                    throw '参数错误';
                }

                responseData.data = await ctx.service.ledger.getChildrenByParentId(ctx.tender.id, id);
            } catch (err) {
                responseData.err = 1;
                responseData.msg = err;
            }

            ctx.body = responseData;
        }

        /**
         * 树结构基本操作（增、删、上下移、升降级）(Ajax)
         * @param {Object} ctx - egg全局变量
         * @return {Promise<void>}
         */
        async baseOperation(ctx) {
            const responseData = {
                err: 0,
                msg: '',
                data: [],
            };
            try {
                if (!ctx.tender.data || ctx.tender.data.user_id !== ctx.session.sessionUser.accountId || this._ledgerReadOnly()) {
                    throw '标段数据错误';
                }
                const data = JSON.parse(ctx.request.body.data);

                switch (data.postType) {
                    case 'add':
                        responseData.data = await ctx.service.ledger.addNode(ctx.tender.id, data.id);
                        break;
                    case 'delete':
                        responseData.data = await ctx.service.ledger.deleteNode(ctx.tender.id, data.id);
                        break;
                    case 'up-move':
                        responseData.data = await ctx.service.ledger.upMoveNode(ctx.tender.id, data.id);
                        break;
                    case 'down-move':
                        responseData.data = await ctx.service.ledger.downMoveNode(ctx.tender.id, data.id);
                        break;
                    case 'up-level':
                        responseData.data = await ctx.service.ledger.upLevelNode(ctx.tender.id, data.id);
                        break;
                    case 'down-level':
                        responseData.data = await ctx.service.ledger.downLevelNode(ctx.tender.id, data.id);
                        break;
                    default:
                        throw '未知操作';
                }
            } catch (err) {
                responseData.err = 1;
                responseData.msg = err;
                this.log(err);
            }

            ctx.body = responseData;
        }

        /**
         * 提交更新数据 (Ajax)
         * @param ctx
         * @return {Promise<void>}
         */
        async updateInfo(ctx) {
            const responseData = {
                err: 0,
                msg: '',
                data: [],
            };
            try {
                if (!ctx.tender.data || ctx.tender.data.user_id !== ctx.session.sessionUser.accountId || this._ledgerReadOnly()) {
                    throw '标段数据错误';
                }
                const data = JSON.parse(ctx.request.body.data);
                if (data instanceof Array) {
                    responseData.data = await ctx.service.ledger.updateInfos(ctx.tender.id, data);
                } else {
                    responseData.data = await ctx.service.ledger.updateInfo(ctx.tender.id, data);
                }
            } catch (err) {
                responseData.err = 1;
                responseData.msg = err;
            }

            ctx.body = responseData;
        }

        async update(ctx) {
            const responseData = {
                err: 0,
                msg: '',
                data: [],
            };
            try {
                const tenderId = parseInt(ctx.params.id);
                if (!tenderId) {
                    throw '当前未打开标段';
                }
                const tenderData = await ctx.service.tender.getDataById(tenderId);
                if (!tenderData || tenderData.user_id !== ctx.session.sessionUser.accountId || this._ledgerReadOnly()) {
                    throw '标段数据错误';
                }
                const data = JSON.parse(ctx.request.body.data);
                responseData.data = await ctx.service.ledger.updateCalc(ctx.tender.id, data);
            } catch (err) {
                this.log(err);
                responseData.err = 1;
                responseData.msg = err;
            }

            ctx.body = responseData;
        }

        /**
         * 复制粘贴整块 (Ajax)
         *
         * @param ctx
         * @return {Promise<void>}
         */
        async pasteBlock(ctx) {
            const responseData = {
                err: 0,
                msg: '',
                data: [],
            };
            try {
                if (!ctx.tender.data || ctx.tender.data.user_id !== ctx.session.sessionUser.accountId || this._ledgerReadOnly()) {
                    throw '标段数据错误';
                }

                const data = JSON.parse(ctx.request.body.data);
                if ((isNaN(data.id) || data.id <= 0) || (!data.tid && data.tid <= 0) || (!data.block || data.block.length <= 0)) {
                    throw '参数错误';
                }

                responseData.data = await ctx.service.ledger.pasteBlock(data.tid, data.id, data.block);
            } catch (err) {
                responseData.err = 1;
                responseData.msg = err;
            }

            ctx.body = responseData;
        }

        /**
         * 从标准项目表添加数据 (Ajax)
         * @param ctx
         * @return {Promise<void>}
         */
        async addFromStandardLib(ctx) {
            const responseData = {
                err: 0,
                msg: '',
                data: [],
            };
            try {
                if (!ctx.tender.data || ctx.tender.data.user_id !== ctx.session.sessionUser.accountId || this._ledgerReadOnly()) {
                    throw '标段数据错误';
                }
                const data = JSON.parse(ctx.request.body.data);
                if ((isNaN(data.id) || data.id <= 0) || !data.stdType || !data.stdNode) {
                    throw '参数错误';
                }
                // todo 校验项目是否使用该库的权限

                let stdLib, addType;
                switch (data.stdType) {
                    case 'chapter':
                        stdLib = ctx.service.stdChapter;
                        addType = stdDataAddType.withParent;
                        break;
                    case 'bills':
                        stdLib = ctx.service.stdBills;
                        const selectNode = await ctx.service.ledger.getDataByNodeId(ctx.tender.id, data.id);
                        if (selectNode.b_code) {
                            addType = stdDataAddType.next;
                        } else {
                            addType = stdDataAddType.child;
                        }
                        break;
                    default:
                        throw '未知标准库';
                }
                const stdData = await stdLib.getDataByDataId(data.stdLibId, data.stdNode);
                switch (addType) {
                    case stdDataAddType.child:
                        responseData.data = await ctx.service.ledger.addStdNodeAsChild(ctx.tender.id, data.id, stdData);
                        break;
                    case stdDataAddType.next:
                        responseData.data = await ctx.service.ledger.addStdNode(ctx.tender.id, data.id, stdData);
                        break;
                    case stdDataAddType.withParent:
                        responseData.data = await ctx.service.ledger.addStdNodeWithParent(ctx.tender.id, stdData, stdLib);
                        break;
                    default:
                        throw '未知添加方式';
                }
            } catch (err) {
                responseData.err = 1;
                responseData.msg = err;
            }

            ctx.body = responseData;
        }

        /**
         * 从签约清单添加节点
         * @param ctx
         * @returns {Promise<void>}
         */
        async addFromDealBills(ctx) {
            try {
                if (ctx.tender.ledgerReadOnly) {
                    throw '标段数据错误';
                }
                const data = JSON.parse(ctx.request.body.data);
                if ((isNaN(data.id) || data.id <= 0) || !data.type || !data.dealBills) {
                    throw '参数错误';
                }
                let result;
                if (data.type === 'child') {
                    result = await ctx.service.ledger.addChild(ctx.tender.id, data.id, data.dealBills);
                } else if (data.type === 'next') {
                    result = await ctx.service.ledger.addNode(ctx.tender.id, data.id, data.dealBills);
                } else {
                    throw '参数错误';
                }
                ctx.body = {err: 0, msg: '', data: result};
            } catch (err) {
                this.log(err);
                if (err.stack) {
                    ctx.body = {err: 1, msg: '未知错误', data: null};
                } else {
                    ctx.body = {err: 1, msg: err, data: null};
                }
            }
        }

        /**
         * 批量插入数据 (Ajax)
         *
         * data = {id, batchData, batchType}
         * data.batchType = 'batchInsertChild'/'batchInsertNext'
         * data.batchData = [{name, children}] -- 项目节列表
         * data.batchData.children = [{code, name, unit, unit_price, quantity}] -- 工程量清单列表
         *
         * @param ctx
         * @return {Promise<void>}
         */
        async batchInsert(ctx) {
            const responseData = {
                err: 0,
                msg: '',
                data: [],
            };
            try {
                if (!ctx.tender.data || ctx.tender.data.user_id !== ctx.session.sessionUser.accountId || this._ledgerReadOnly()) {
                    throw '标段数据错误';
                }
                const data = JSON.parse(ctx.request.body.data);
                if ((isNaN(data.id) || data.id <= 0) || !data.batchType) {
                    throw '参数错误';
                }

                switch (data.batchType) {
                    case 'child':
                        responseData.data = await ctx.service.ledger.batchInsertChild(ctx.tender.id, data.id, data.batchData);
                        break;
                    case 'next':
                        responseData.data = await ctx.service.ledger.batchInsertNext(ctx.tender.id, data.id, data.batchData);
                        break;
                    default:
                        throw '参数错误';
                }
            } catch (err) {
                this.log(err);
                responseData.err = 1;
                responseData.msg = err;
            }

            ctx.body = responseData;
        }

        /**
         * 查询
         *
         * @param ctx
         * @return {Promise<void>}
         */
        async search(ctx) {
            const responseData = {
                err: 0,
                msg: '',
                data: [],
            };
            try {
                const tenderId = ctx.params.id;
                if (!tenderId) {
                    throw '当前未打开标段';
                }
                const data = JSON.parse(ctx.request.body.data);
                if (!data.keyword || data.keyword === '') {
                    throw '参数错误';
                }
                responseData.data = await ctx.service.ledger.search(tenderId, {
                    value: ctx.app.mysql.escape('%' + data.keyword + '%'),
                    operate: 'Like',
                    fields: ['code', 'b_code', 'name'],
                });
            } catch (err) {
                this.log(err);
                responseData.err = 1;
                responseData.msg = err;
            }

            ctx.body = responseData;
        }

        /**
         * 定位
         * @param ctx
         * @return {Promise<void>}
         */
        async locate(ctx) {
            const responseData = {
                err: 0,
                msg: '',
                data: [],
            };
            try {
                const tenderId = ctx.params.id;
                if (!tenderId) {
                    throw '当前未打开标段';
                }
                const data = JSON.parse(ctx.request.body.data);
                if ((isNaN(data.id) || data.id <= 0)) {
                    throw '参数错误';
                }
                responseData.data = await ctx.service.ledger.locateNode(tenderId, data.id);
            } catch (err) {
                this.log(err);
                responseData.err = 1;
                responseData.msg = err;
            }

            ctx.body = responseData;
        }

        /**
         * 获取全部子节点
         *
         * @param ctx
         * @return {Promise<void>}
         */
        async posterity(ctx) {
            const responseData = {
                err: 0,
                msg: '',
                data: [],
            };
            try {
                const tenderId = ctx.params.id;
                if (!tenderId) {
                    throw '当前未打开标段';
                }
                const data = JSON.parse(ctx.request.body.data);
                if ((isNaN(data.id) || data.id <= 0)) {
                    throw '参数错误';
                }
                const expandData = await ctx.service.ledger.getPosterityByParentId(tenderId, data.id);
                responseData.data = { expand: expandData };
            } catch (err) {
                this.log(err);
                responseData.err = 1;
                responseData.msg = err;
            }

            ctx.body = responseData;
        }

        /**
         * 获取部位明细数据(Ajax)
         *
         * @param ctx
         * @return {Promise<void>}
         */
        async pos(ctx) {
            try {
                await this.checkMeasureType(measureType.tz.value);
                const condition = JSON.parse(ctx.request.body.data) || {};
                condition.tid = ctx.tender.id;

                const posData = await ctx.service.pos.getPosData(condition);
                ctx.body = { err: 0, msg: '', data: posData };
            } catch (err) {
                this.log(err);
                ctx.body = { err: 1, msg: err.toString(), data: [] };
            }
        }

        /**
         * 更新 部位明细数据
         *
         * @param ctx
         * @return {Promise<void>}
         */
        async posUpdate(ctx) {
            try {
                await this.checkMeasureType(measureType.tz.value);
                const data = JSON.parse(ctx.request.body.data);
                const responseData = await ctx.service.pos.savePosData(data, ctx.tender.id);
                ctx.body = { err: 0, msg: '', data: responseData };
            } catch (err) {
                this.log(err);
                ctx.body = { err: 1, msg: err.toString(), data: null };
            }
        }

        /**
         * 复制粘贴 部位明细
         * @param ctx
         * @returns {Promise<void>}
         */
        async posPaste(ctx) {
            try {
                await this.checkMeasureType(measureType.tz.value);
                const data = JSON.parse(ctx.request.body.data);
                const result = await ctx.service.pos.pastePosData(data, ctx.tender.id);
                ctx.body = { err: 0, msg: '', data: result };
            } catch(err) {
                this.log(err);
                ctx.body = { err: 1, msg: err.toString(), data: null };
            }
        }

        /**
         * 上传 清单Excel 并导入
         * @param ctx
         * @return {Promise<void>}
         */
        async uploadExcel(ctx) {
            try {
                const compressData = ctx.request.body.data;
                const data = JSON.parse(LzString.decompressFromUTF16(compressData));
                const responseData = { err: 0, msg: '', data: {}, };
                await ctx.service.ledger.importExcel(data);
                responseData.data.bills = await ctx.service.ledger.getDataByTenderId(ctx.tender.id, -1);
                responseData.data.pos = await ctx.service.pos.getPosData({tid: this.ctx.tender.id});
                ctx.body = responseData;
            } catch (err) {
                this.log(err);
                ctx.body = { err: 1, msg: err.toString(), data: null };
            }

        }

        /**
         * 下载（清单Excel模板 or 导出项目台账Excel）
         * @param ctx
         * @return {Promise<void>}
         */
        async download(ctx) {
            const file = ctx.params.file;
            if (file) {
                try {
                    let fileName;
                    if (file === 'template') {
                        fileName = this.app.baseDir + '/app/public/files/template/ledger/导入分项清单Excel格式.xls';
                    } else if (file === 'ledger') {
                        const create_time = Date.parse(new Date()) / 1000;
                        fileName = this.app.baseDir + '/app/public/files/downloads/ledger/' + ctx.tender.id + '-' + create_time + '.xlsx';
                        // todo 导出台账清单Excel
                    }
                    ctx.body = await fs.readFileSync(fileName);
                } catch (err) {
                    this.log(err);
                    this.setMessage(err.toString(), this.messageType.ERROR);
                }
            }
        }

        /**
         * 计量台账页面 (Get)
         *
         * @param {object} ctx - egg全局变量
         * @return {void}
         */
        async index(ctx) {
            const renderData = {};
            await this.layout('ledger/index.ejs', renderData);
        }
    }

    return LedgerController;
};
