/*
 * @Descripttion: 
 * @Author: vian
 * @Date: 2020-12-16 17:48:47
 */
/*
 * @Descripttion: 
 * @Author: vian
 * @Date: 2020-12-16 17:48:47
 */
/*
 * @Descripttion: 
 * @Author: vian
 * @Date: 2020-12-16 17:48:47
 */
/*
 * @Descripttion: 
 * @Author: vian
 * @Date: 2020-12-16 17:48:47
 */
/*
 * @Descripttion: 
 * @Author: vian
 * @Date: 2020-12-16 17:48:47
 */
/*
 * @Descripttion: 
 * @Author: vian
 * @Date: 2020-12-16 17:48:47
 */
/**
 * Created by Zhong on 2017/8/22.
 */
const mongoose = require('mongoose');
const complementaryGljModel = mongoose.model('complementary_glj_lib');
const stdGljModel = mongoose.model('std_glj_lib_gljList');
const gljClassModel = mongoose.model('std_glj_lib_gljClass');
const compleClassModel = mongoose.model('complementary_glj_section');
const _ = require('lodash');
const counter = require("../../../public/counter/counter");
const async = require("async");
const STDGLJLibGLJListModel = require("../../common/std/std_glj_lib_glj_list_model");
const libType = {
    stdGLJ: 1,
    complementaryGLJs: 2
};
const stdgljutil = require("../../../public/cache/std_glj_type_util");
const { ShareLibType } = require('../../../public/common_constants');

class GljDao {
    
    async prepareInitData(gljLibID, userID, sessionUserID, compilationID, projection) {
        const pmFacade = require('../../pm/facade/pm_facade');
        const receiveLibs = await pmFacade.getReceiveLibList(sessionUserID, compilationID, ShareLibType.GLJ_LIB);
        const shareLibs = sessionUserID === userID ? await pmFacade.getLibShareList(sessionUserID, compilationID, ShareLibType.GLJ_LIB) : [];
        const gljItems = await this.getGLJItemsSync(gljLibID, userID, compilationID, projection);
        const mixTree = await this.getMixedTree(gljLibID, userID, compilationID);
        const gljDistTypeCache = stdgljutil.getStdGljTypeCacheObj().toArray();
        return {
            receiveLibs,
            shareLibs,
            gljItems,
            mixTree,
            distTypeData: gljDistTypeCache,
        };
    }

    getGljTypes (gljLibId, callback){
        gljClassModel.find({"repositoryId": gljLibId},function(err,data){
            if(data.length) {
                callback(0,data);
            }
            else  if(err) callback("获取人材机类型错误！",false);
        })
    }

    _exist(data, attr){
        return data && data[attr] !== 'undefined' && data[attr];
    }

    sortToNumber(datas){
        for(let i = 0, len = datas.length; i < len; i++){
            let data = datas[i]._doc;
            if(this._exist(data, 'basePrice')){
                data['basePrice'] = parseFloat(data['basePrice']);
            }
            if(this._exist(data, 'component')){
                for(let j = 0, jLen = data['component'].length; j < jLen; j++){
                    let comGljObj = data['component'][j]._doc;
                    if(this._exist(comGljObj, 'consumeAmt')){
                        comGljObj['consumeAmt'] = parseFloat(comGljObj['consumeAmt']);
                    }
                }
            }
        }
    }

    async getQueryByType (condition) {
        const {
            userID, 
            compilationId, 
            gljLibId, code, 
            type,
            queryExtend,
            replace, 
            location, 
            classList, 
            search
        } = condition;
        let model = null,
            query = {},
            countQuery = {},
            matchLocation = false;
        if (type === libType.stdGLJ) {
            model = stdGljModel;
            query.repositoryId = gljLibId;
        } else {
            model = complementaryGljModel;
            query.userId = userID;
            query.compilationId = compilationId;
        }
        // 分类树
        if (classList.length) {
            query.gljClass = {$in: classList};
        }
        // 定位
        if (location) {
            // 替换定位，则先看看能不能找到替换的人材机，能找到的话就能定位要被替换人材机所在分类树
            const replaceQuery = {...query, ...replace};
            const replaceData = await model.findOne(replaceQuery);
            if (replaceData) {
                query.gljClass = replaceData.gljClass;
                matchLocation = true;
            }
        }
        // 替换过滤类型
        if (replace) {
            // 人材机类型是“混凝土、砂浆、配合比、商品混凝土、商品砂浆”时，筛选的可替换的人材机类型应是“混凝土、或砂浆、或配合比、或商品混凝土、或商品砂浆”。
            //2021-03-17  zhang 需求更改 当前是材料、主材、设备，可以替换为材料、主材、设备。（因有些混凝土在定额书中未给单价，所以是主材，实际使用时 需要替换为“砼”或“商砼”等）
            //const materialTypes = [202, 203, 204, 205, 206];
            const materialTypes = [201,202, 203, 204, 205, 206,4,5];
            query.gljType = materialTypes.includes(replace.gljType) ? {$in: materialTypes} : replace.gljType;
        }
        // 添加组成物时的查询扩展
        if (queryExtend) {
            Object.assign(query, queryExtend);
        }
        // 搜索关键字
        if (search) {
            query.$or = [{code: {'$regex': search, $options: '$i'}}, {name: {'$regex': search, $options: '$i'}}];
        }
        countQuery = {...query};
        // 上一次分页的最末人材机编码
        if (code) {
            query.code = {$gt: code};
        }
        return {
            model,
            query,
            countQuery,
            matchLocation
        };
    }

    async getGLJPaging (condition) {
        const queryData = await this.getQueryByType(condition);
        // 定位（替换初始化）
        // 替换触发的人材机选择界面，只有在初始化时才定位，其他操作下是正常的分页
        if (queryData.matchLocation) {
            // 返回的数据，为编码小于等于替换的编码，再加附加条数
            const limitQuery = _.cloneDeep(queryData.query);
            limitQuery.code = {$lte: condition.replace.code};
            const lteLimit = await queryData.model.count(limitQuery);
            const additionalLimit = 10;
            const limit = lteLimit + additionalLimit;
            // 显示的数据不能太少，否则数据没有占满整个表格区域(实际上数据条目应该超过表格显示区域)，让人误以为数据只有那么点，不再滚动触发分页
            condition.limit = limit > condition.limit ? limit : condition.limit;
        }
        //const gljs = await queryData.model.find(queryData.query).lean().sort({code: 1}).skip(condition.index).limit(condition.limit);
        // 分页使用skip的话，跳过的条数多的话性能会下降，因此不使用
        const gljs = await queryData.model.find(queryData.query).lean().sort({code: 1}).limit(condition.limit);
        const total = await queryData.model.find(queryData.countQuery).count();
        return condition.type === libType.stdGLJ
            ? {stdGLJ: gljs, complementaryGLJs: [], total: total}
            : {stdGLJ: [], complementaryGLJs: gljs, total: total}
    }

    async getGLJDataSync (gljLibId, userId, compilationId) {
        const stdGljs = await stdGljModel.find({repositoryId: gljLibId}).lean();
        const complementaryGljs = await complementaryGljModel.find({userId, compilationId}).lean();
        return {
            stdGljs,
            complementaryGljs
        }
    }

    //获得用户的补充工料机和用户当前所在编办的标准工料机
    async getGljItems(stdGljLibId, userId, compilationId, projection, callback) {
        let me = this;
        let rst = { stdGljs: [], complementaryGljs: [] };
        //批量获取异步
        async.parallel([
            async function (cb) {
                try {
                    let stdGljs = stdGljLibId ? await stdGljModel.find({ repositoryId: stdGljLibId }, projection).lean() : [];
                    me.sortToNumber(stdGljs);
                    rst.stdGljs = stdGljs;
                    cb(null);
                }
                catch (err) {
                    cb(err);
                }
            },
            function (cb) {
                if (!userId || !compilationId) {
                    rst.complementaryGljs = [];
                    cb(null);
                    return;
                }
                complementaryGljModel.find({ userId: userId, compilationId: compilationId }, '-_id', { lean: true }, function (err, complementaryGljs) {
                    if (err) {
                        cb(err);
                    }
                    else {
                        me.sortToNumber(complementaryGljs);
                        rst.complementaryGljs = complementaryGljs;
                        cb(null);
                    }
                });
            }
        ], function (err) {
            if (err) {
                callback(err, null);
            }
            else {
                callback(0, rst);
            }
        })

    }

    async getGLJItemsSync(stdGljLibId, userId, compilationId, projection) {
        const rst = { stdGljs: [], complementaryGljs: [] };
        const task = [
            stdGljModel.find({ repositoryId: stdGljLibId }, projection).lean(),
            complementaryGljModel.find({ userId: userId, compilationId: compilationId }, '-_id').lean()
        ];
        const [ stdRst, cptRst ] = await Promise.all(task);
        this.sortToNumber(stdRst);
        rst.stdGljs = stdRst;
        this.sortToNumber(cptRst);
        rst.complementaryGljs = cptRst;
        return rst;
    }

    async getStdItems (stdGljLibId, projection, callback) {
        try {
            let stdItems = await stdGljModel.find({repositoryId: stdGljLibId}, projection).lean();
            callback(0, stdItems);
        } catch (err) {
            callback(1, null);
        }
    }

    getGljItemsByCode (repositoryId, codes, callback){
        gljModel.find({"repositoryId": repositoryId,"code": {"$in": codes}},function(err,data){
            if(err) callback(true, "")
            else callback(false, data);
        })
    };

    updateComponent(userId, updateArr, callback){
        let parallelFucs = [];
        for(let i = 0; i < updateArr.length; i++){
            parallelFucs.push((function(obj){
                return function (cb) {
                    if(typeof obj.component === 'undefined'){
                        obj.component = [];
                    }
                    complementaryGljModel.update({userId: userId, ID: obj.ID}, obj, function (err, result) {
                        if(err){
                            cb(err);
                        }
                        else{
                            cb(null, obj);
                        }
                    })
                }
            })(updateArr[i]));
        }
        async.parallel(parallelFucs, function (err, result) {
            if(err){
                callback(err, '更新组成物错误！', null);
            }
            else{
                callback(0, '成功！', result);
            }
        });
    }
    //-oprtor
    mixUpdateGljItems (userId, compilationId, updateItems, addItems, rIds, callback) {
        if (updateItems.length == 0 && rIds.length == 0) {
            GljDao.addGljItems(userId, compilationId, addItems, callback);
        }
        else if(rIds.length > 0 && updateItems.length > 0){
            async.parallel([
                function (cb) {
                    GljDao.removeGljItems(rIds, cb);
                },
                function (cb) {
                    GljDao.updateGljItems(userId, compilationId, updateItems, cb);
                }
            ], function (err) {
                if(err){
                    callback(true, "Fail to update and delete", false)
                }
                else{
                    callback(false, "Save successfully", false);
                }
            })
        }
        else if (rIds.length > 0 && updateItems.length === 0) {
            GljDao.removeGljItems(rIds, callback);
        }
        else if(updateItems.length > 0 || addItems.length > 0){
            GljDao.updateGljItems(userId, compilationId, updateItems, function(err, results){
                if (err) {
                    callback(true, "Fail to update", false);
                } else {
                    if (addItems && addItems.length > 0) {
                        GljDao.addGljItems(userId, compilationId, addItems, callback);
                    } else {
                        callback(false, "Save successfully", results);
                    }
                }
            });
        }
    }

    static removeGljItems (rIds, callback) {
        if (rIds && rIds.length > 0) {
            complementaryGljModel.collection.remove({ID: {$in: rIds}}, null, function(err, docs){
                if (err) {
                    callback(true, "Fail to remove", false);
                } else {
                    callback(false, "Remove successfully", docs);
                }
            })
        } else {
            callback(false, "No records were deleted!", null);
        }
    }

    static addGljItems (userId, compilationId, items, callback) {
        if (items && items.length > 0) {
            counter.counterDAO.getIDAfterCount(counter.moduleName.GLJ, items.length, function(err, result){
                var maxId = result.sequence_value;
                var arr = [];
                for (var i = 0; i < items.length; i++) {
                    var obj = new complementaryGljModel(items[i]);
                    obj.ID = (maxId - (items.length - 1) + i);
                    obj.userId = userId;
                    obj.compilationId = compilationId;
                    arr.push(obj);
                }
                complementaryGljModel.collection.insert(arr, null, function(err, docs){
                    if (err) {
                        callback(true, "Fail to add", false);
                    } else {
                        callback(false, "Add successfully", docs);
                    }
                });
            });
        } else {
            callback(true, "No source", false);
        }
    }

    static updateGljItems(userId, compilationId, items, callback) {
        var functions = [];
        for (var i=0; i < items.length; i++) {
            functions.push((function(doc) {
                return function(cb) {
                    var filter = {};
                    if (doc.ID) {
                        filter.ID = doc.ID;
                    } else {
                        filter.userId = userId;
                        filter.compilationId = compilationId;
                        filter.code = doc.code;
                    }
                    complementaryGljModel.update(filter, doc, cb);
                };
            })(items[i]));
        }
        async.parallel(functions, function(err, results) {
            callback(err, results);
        });
    }

    /**
     * 获取组成物数据
     *
     * @param {Number} gljId
     * @return {Promise}
     */
    async getComponent(gljId) {
        let result = [];
        let libGljData = await complementaryGljModel.findOne({ID: gljId});
        if (libGljData === null || libGljData.component.length <= 0) {
            return result;
        }
        // 标准工料机库
        let componentIdListStd = [];
        // 补充工料机库
        let componentIdListCpt = [];
        let componentConsume = {};
        // 整理数据
        for (let component of libGljData.component) {
            if (component.isStd) {
                componentIdListStd.push(component.ID);
            } else {
                componentIdListCpt.push(component.ID);
            }
            let isStdFlag = component.isStd ? 'std' : 'cpt';
            if(component.consumeAmt != undefined && component.consumeAmt != null) {
                componentConsume[component.ID + '_' + isStdFlag] = component.consumeAmt;
            }else if(component.consumeAmtProperty){
                componentConsume[component.ID + '_' + isStdFlag] = component.consumeAmtProperty;
            }
        }

        // 查找标准库数据
        let condition = {};
        let componentStdGljData = [];
        if (componentIdListStd.length > 0) {
            let gljListModel = new STDGLJLibGLJListModel();
            condition = {ID: {$in: componentIdListStd}};
            componentStdGljData = await gljListModel.findDataByCondition(condition, null, false);
        }

        // 查找补充库数据
        let componentCptGljData = [];
        if (componentIdListCpt.length > 0) {
            condition = {ID: {$in: componentIdListCpt}};
            componentCptGljData = await complementaryGljModel.find(condition);
        }

        if (componentCptGljData === null && componentStdGljData === null) {
            return result;
        }

        // 整理数据
        if (componentStdGljData.length > 0) {
            componentStdGljData = JSON.stringify(componentStdGljData);
            componentStdGljData = JSON.parse(componentStdGljData);
            for(let gljData of componentStdGljData) {
                gljData.connectCode = libGljData.code;
                gljData.consumption = componentConsume[gljData.ID + '_std'];
                gljData.from='std';
                result.push(gljData);
            }
        }

        if (componentCptGljData.length > 0) {
            componentCptGljData = JSON.stringify(componentCptGljData);
            componentCptGljData = JSON.parse(componentCptGljData);
            for(let gljData of componentCptGljData) {
                gljData.connectCode = libGljData.code;
                gljData.consumption = componentConsume[gljData.ID + '_cpt'];
                gljData.from='cpt';
                result.push(gljData);
            }
        }

        return result;
    }

    async getMixedTree(gljLibId, userId, compilationId){
        let rst = {std: [], comple: []};
        if (gljLibId) {
            rst.std = await gljClassModel.find({repositoryId: gljLibId}).lean();
        }
        if (userId && compilationId) {
            rst.comple = await compleClassModel.find({userId: userId, compilationId: compilationId}).lean();
        }
        return rst;
    }
}

module.exports = GljDao;

