/**
 * Created by chen on 2017/6/29.
 */

module.exports = {//先 exports再require 防止循环引用
    save: save,
    getData: getData,
    deleteByRation: deleteByRation,
    getQuantityByProjectGLJ: getQuantityByProjectGLJ,
    getLibInfo: getLibInfo,
    getLibOptions,
    getLibOptionsForCompilation,
    getGLJData: getGLJData,
    getGLJDataByCodes:getGLJDataByCodes,
    addGLJ: addGLJ,
    deleteRationGLJ:deleteRationGLJ,
    deleteGLJ:deleteGLJ,
    insertAddTypeGLJ:insertAddTypeGLJ,
    replaceGLJ: replaceGLJ,
    replaceGLJByData:replaceGLJByData,
    mReplaceGLJ: mReplaceGLJ,
    updateRationGLJByEdit: updateRationGLJByEdit,
    getGLJClass: getGLJClass,
    insertGLJAsRation: insertGLJAsRation,
    getRationTypeGLJQuantity:getRationTypeGLJQuantity,
    getInfoFromProjectGLJ:getInfoFromProjectGLJ,
    createNewRecord:createNewRecord,
    getGLJSearchInfo:getGLJSearchInfo,
    updateRationGLJFromDoc:updateRationGLJFromDoc,
    getGLJLibByEngineerID:getGLJLibByEngineerID,
    prepareExtData:prepareExtData,
    setPropertyFromStd:setPropertyFromStd,
    updateProportion: updateProportion
};

let mongoose = require('mongoose');
const uuidV1 = require('uuid/v1');
let consts = require('../../main/models/project_consts')
let commonConsts = consts.commonConst;
let _ = require("lodash");
let ration_glj = mongoose.model('ration_glj');
import GLJListModel from '../../glj/models/glj_list_model';
let std_glj_lib_gljList_model = mongoose.model('std_glj_lib_gljList');
let async_n = require("async");
let ration = mongoose.model('ration');
let ration_coe_facade = require('./ration_coe_facade');
let ration_coe = mongoose.model('ration_coe');
let std_ration_lib_ration_items = mongoose.model('std_ration_lib_ration_items');
let glj_calculate_facade = require('./glj_calculate_facade');
let glj_type_util = require('../../../public/cache/std_glj_type_util');
let quantity_detail_facade = require('../../main/facade/quantity_detail_facade');
let ration_installation_facade = require('../../main/facade/ration_installation_facade');
let logger = require("../../../logs/log_helper").logger;
import stdgljutil  from "../../../public/cache/std_glj_type_util";
import EngineeringLibModel from "../../users/models/engineering_lib_model";
import GljDao from "../../complementary_glj_lib/models/gljModel";
const complementaryGljModel = mongoose.model('complementary_glj_lib');
const stdGljModel = mongoose.model('std_glj_lib_gljList');
const gljClassModel = mongoose.model('std_glj_lib_gljClass');
const projectDao = require('../../pm/models/project_model').project;
const compleClassModel = mongoose.model('complementary_glj_section');
const stdRationLibModel = mongoose.model('std_ration_lib_map');
const compilationModel = mongoose.model('compilation');
const engineeringModel = mongoose.model('engineering_lib');
let gljUtil = require('../../../public/gljUtil');



let operationMap = {
    'ut_create': create_ration_glj,
    'ut_update': update_ration_glj,
    'ut_delete': delete_ration_glj
};
let updateFunctionMap = {
    'normalUpdate': normalUpdate
};


/**
 * 根据项目工料机ID和项目ID取消耗量
 *
 * @param {object} condition
 * @return Array
 */
async function getQuantityByProjectGLJ(condition) {
    let query = {
        'projectID': condition.projectID,
    };
    if(condition.projectGLJIDList){
        query['projectGLJID'] = {$in: condition.projectGLJIDList};
    }
    let startTime = +new Date();
    let results = await  ration_glj.find(query, ['projectGLJID', 'quantity', 'rationID'], {sort: {projectGLJID: 1}});
    let rationList = _.uniq(_.map(results, 'rationID'));
    let getQuantity = +new Date();
    console.log("取工料机消耗量时间-----"+(getQuantity - startTime));
    let rationQuery = {
        'projectID': condition.projectID,
        'ID': {$in: rationList},
        'deleteInfo': null
    };

    /* $and: [
     {'projectID': condition.projectID},
     {'ID': {$in: rationList}},
     {'deleteInfo': null}
     ]*/
    let rations = await ration.find(rationQuery, ['ID', 'quantity']);
    let rationsTime = +new Date();
    console.log("取定额消耗量时间-----"+(rationsTime - getQuantity));
    return combineQuantity(results, rations);
}

function combineQuantity(results, rations) {
    let resultList = [];
    let rationMap = _.indexBy(rations,'ID');
    for(let r of results){
        let tmp = {
            projectGLJID: r.projectGLJID,
            quantity: Number(r.quantity)
        }
        let ration = rationMap[r.rationID];//_.find(rations, {ID: r.rationID})
        if (ration) {
            tmp.rationID = ration.ID;
            tmp.rationQuantity = ration.quantity ? Number(ration.quantity) : undefined;
        }
        resultList.push(tmp);
    }
    return resultList;

    return resultList;
}


function get_lib_glj_info(ration_glj) {
    return function (result, cb) {
        std_glj_lib_gljList_model.findOne({'ID': ration_glj.GLJID}, (err, glj) => {
            if (err) {
                cb(err, '')
            } else if (glj) {
                ration_glj.name = glj.name;
                ration_glj.code = glj.code;
                ration_glj.original_code = glj.code;
                ration_glj.unit = glj.unit;
                ration_glj.specs = glj.specs;
                ration_glj.basePrice = glj.basePrice;
                ration_glj.marketPrice = glj.basePrice;
                ration_glj.shortName = glj.shortName;
                ration_glj.type = glj.gljType;
                ration_glj.repositoryId = glj.repositoryId;
                ration_glj.adjCoe = glj.adjCoe;
                getInfoFromProjectGLJ(ration_glj).then(function (info) {
                    if (info&&info.length>0) {
                        let tem = {};
                        tem.newRecode = createNewRecord(info[0]);
                        tem.showData = info[0];
                        result.datas.push(tem);
                        cb(null, result);
                    } else {
                        cb(new Error('get project glj error'), null);
                    }
                });
            } else {
                cb(null, result);
            }
        })
    }
}


function createNewRecord(ration_glj) {
    let newRecoed = {};
    newRecoed.ID = ration_glj.ID;
    newRecoed.projectID = ration_glj.projectID;
    newRecoed.GLJID = ration_glj.GLJID;
    newRecoed.rationID = ration_glj.rationID;
    newRecoed.rationItemQuantity = ration_glj.rationItemQuantity;
    newRecoed.customQuantity = ration_glj.customQuantity;
    newRecoed.quantity = ration_glj.quantity;
    newRecoed.rationProportion = ration_glj.rationProportion;
    newRecoed.adjustProportion = ration_glj.adjustProportion;
    newRecoed.name = ration_glj.name;
    newRecoed.code = ration_glj.code;
    newRecoed.original_code = ration_glj.original_code;
    newRecoed.unit = ration_glj.unit;
    newRecoed.specs = ration_glj.specs;
    newRecoed.from = ration_glj.from ? ration_glj.from : 'std';
    newRecoed.createType = ration_glj.createType ? ration_glj.createType : 'normal';
    newRecoed.shortName = ration_glj.shortName;
    newRecoed.billsItemID = ration_glj.billsItemID;
    newRecoed.type = ration_glj.type;
    newRecoed.model = ration_glj.model;
    newRecoed.repositoryId = ration_glj.repositoryId;
    newRecoed.projectGLJID = ration_glj.projectGLJID;
    newRecoed.adjCoe = ration_glj.adjCoe;
    return newRecoed
}

async function getInfoFromProjectGLJ(ration_glj,unitPriceFileId,ext) {
    let data = getGLJSearchInfo(ration_glj);
    try {
        let projectGljModel = new GLJListModel();
        let result = await projectGljModel.addList(data,unitPriceFileId,ext);
        let typeString = result.type+"";
        ration_glj.marketPrice = result.unit_price.market_price;
        ration_glj.adjustPrice = result.unit_price.base_price;
        ration_glj.basePrice = result.unit_price.base_price;
        ration_glj.projectGLJID = result.id;
        if (typeString.startsWith("2")||typeString=='4'||typeString=='5') {//只有材料类型才显示是否暂估
            ration_glj.isEstimate = result.is_evaluate;
        }
        if (result.hasOwnProperty('subList') && result.subList.length > 0) {
            ration_glj.subList = getMixRatioShowDatas(result.subList);
        }
        return [ration_glj,result];
    } catch (err) {
        logger.err(err);
        return null;
    }

}

function getMixRatioShowDatas(subList) {
    var temRationGLJs = [];
    for (let pg of subList) {
        var tem = {
            projectGLJID: pg.id,
            code: pg.code,
            name: pg.name,
            specs: pg.specs,
            unit: pg.unit,
            shortName: pg.unit_price.short_name,
            rationItemQuantity: pg.ratio_data.consumption,
            basePrice: pg.unit_price.base_price,
            marketPrice: pg.unit_price.market_price,
            adjustPrice: pg.adjust_price,
            isEstimate: pg.is_evaluate,
            isMixRatio: true,
            isAdd: pg.unit_price.is_add,
            GLJID: pg.glj_id
        }
        temRationGLJs.push(tem);
    }
    temRationGLJs = _.sortBy(temRationGLJs, 'code');
    return temRationGLJs;
}

function create_ration_glj(user_id, datas) {
    return function (callback) {
        let ration_glj_list = datas.ration_glj_list;
        var tasks = [];
        tasks.push(startingTask("get glj info"))
        for (let i = 0; i < ration_glj_list.length; i++) {
            ration_glj_list[i].ID = uuidV1();
            tasks.push(get_lib_glj_info(ration_glj_list[i]))
        }
        async_n.waterfall(tasks, (err, results) => {
            if (err) {
                callback(err, results)
            } else {
                let newRecords = [];
                let showDatas = [];
                for (let r of results.datas) {
                    if (r) {
                        newRecords.push(r.newRecode);
                        showDatas.push(r.showData);
                    }
                }
                if (newRecords.length > 0) {
                    ration_glj.insertMany(newRecords, (err, doc) => {
                        if (err) {
                            callback(err, null);
                        } else {
                            let returndata = {
                                updateTpye: commonConsts.UT_CREATE,
                                moduleName: 'ration_glj',
                                data: {
                                    newRecords: newRecords,
                                    showDatas: showDatas
                                }
                            }
                            callback(null, returndata)
                        }
                    });
                } else {
                    logger.info("can't find gljs")
                    callback(null, null)
                }
            }
        })
    }

}

function update_ration_glj(user_id, datas) {
    if (datas.updateFunction) {
        return updateFunctionMap[datas.updateFunction](user_id, datas);
    } else {
        return normalUpdate(user_id, datas);
    }
}

function normalUpdate(user_id, datas) {
    return function (callback) {
        ration_glj.update(datas.query, datas.doc, (err, result) => {
            if (err) {
                callback(err, '');
            } else {
                let returndata = {
                    moduleName: 'ration_glj',
                    data: {
                        updateTpye: commonConsts.UT_UPDATE,
                        query: datas.query,
                        doc: datas.doc
                    }
                }
                callback(null, returndata)
            }
        })
    }
}

async function doCustomQuantityUpdate(datas) {
    let result = await ration_glj.findOneAndUpdate(datas.query, datas.doc);
    let cal_result = await glj_calculate_facade.calculateQuantity({
        projectID: datas.query.projectID,
        rationID: datas.query.rationID
    });
    cal_result.glj_result.forEach(function (item) {
        if (!item.doc.hasOwnProperty('customQuantity')) {
            item.doc.customQuantity = null;
        }
    });
    return cal_result;
}


function delete_ration_glj(user_id, datas) {
    return function (callback) {
        deleteByID(datas, callback);
        //callback(new Error("删除子数据失败！"),null)
        //这个方法已经不用，先注释，稳定后再删除
        /* if (datas.deleteType == "RATION") {
         deleteByRation(datas, callback);
         } else if (datas.deleteType == "BILL") {
         deleteByBill(user_id, datas, callback);
         } else {
         deleteByID(datas, callback);
         }*/
    }
}

function deleteByRation(datas, callback) {
    let data = datas.updateData;
    let tasks = [];
    tasks.push(deleteGLJList(data));
    tasks.push(ration_coe_facade.delete_ration_coe(data));
    tasks.push(quantity_detail_facade.deleteByRation(data));
    tasks.push(ration_installation_facade.deleteByRation(data));
    async_n.parallel(tasks, function (err, result) {
        commonCallback(callback, result, err)
    })
}

function deleteGLJList(data) {
    return function (callback) {
        ration_glj.deleteMany({projectID: data.projectID, rationID: data.ID}, (err, result) => {
            commonCallback(callback, result, err)
        });
    }
}


function deleteByBill(user_id, datas, callback) {
    let tasks = [];
    tasks.push(startingTask("deleteByBill"));
    tasks.push(getRationsByBill(datas));
    tasks.push(deleteRationsbyBill(user_id, datas));
    tasks.push(deleteByMultiRations(datas));
    async_n.waterfall(tasks, function (err, results) {
        if (err) {
            callback(err, '');
        } else {
            callback(null, results);
        }
    })
}


function deleteByID(datas, callback) {
    deleteAndUpdateState(datas).then(function (result) {
        if (result.err) {
            callback(result.err, '');
        } else {
            let returndata = {
                moduleName: 'ration_glj',
                data: {
                    updateTpye: commonConsts.UT_DELETE,
                    query: datas.query,
                    adjustState: result.adjustState
                }
            }
            callback(null, returndata)
        }
    })
}
async function deleteGLJ(IDs) {
    await ration_glj.deleteMany({'ID': {$in: IDs}});
}

async function deleteAndUpdateState(datas) {
    let result = {
        err: null
    }
    try {
        await ration_glj.deleteOne(datas.query);
        let stateResult = await glj_calculate_facade.calculateQuantity({
            projectID: datas.query.projectID,
            rationID: datas.doc.rationID
        });
        result.adjustState = stateResult.adjustState;
    } catch (err) {
        result.err = err;
    }
    return result;
}

function startingTask(processName) {
    return function (asyncCallBack) {
        var result = {
            processName: processName,
            datas: []
        };
        asyncCallBack(null, result);
    };
}

function getRationsByBill(datas) {
    return function (results, callback) {
        ration.find({
            projectID: datas.updateData.projectID,
            billsItemID: datas.updateData.ID
        }, function (err, rations) {
            if (err) {
                callback(err, '')
            } else {
                results.rations = rations;
                callback(null, results)
            }
        })
    }
}

function deleteRationsbyBill(user_id, datas) {
    return function (results, callback) {
        let deleteInfo = {
            deleteInfo: {deleted: true, deleteDateTime: new Date(), deleteBy: user_id}
        };
        ration.update({
            projectID: datas.updateData.projectID,
            billsItemID: datas.updateData.ID
        }, deleteInfo, {multi: true}, (err, deleteresults) => {
            if (err) {
                callback(err, '');
            } else {
                callback(null, results);
            }
        });
    }
}


function deleteByMultiRations(datas) {
    return function (results, deleteCallBack) {
        var delete_tasks = [];
        var deleteOne = function (ration) {
            return function (callback) {
                ration_glj.deleteMany({projectID: ration.projectID, rationID: ration.ID}, function (err, result) {
                    commonCallback(callback, result, err)
                });
            }
        }
        let rations = results.rations;
        for (let i = 0; i < rations.length; i++) {
            delete_tasks.push(deleteOne(rations[i]._doc));
            delete_tasks.push(ration_coe_facade.delete_ration_coe(rations[i]._doc));
            delete_tasks.push(quantity_detail_facade.deleteByRation(rations[i]._doc));
            delete_tasks.push(ration_installation_facade.deleteByRation(rations[i]._doc));
        }
        delete_tasks.push(quantity_detail_facade.deleteByBill(datas.updateData));
        async_n.parallel(delete_tasks, (err, results) => {
            if (err) {
                deleteCallBack(err, '')
            } else {
                deleteCallBack(null, results)
            }
        })
    }
}


/*
 function  deleteByRation(doc) {
 return function (callback){
 ration_glj.deleteMany({projectID: doc.updateData.projectID, rationID: doc.updateData.ID},callback);
 }
 }
 */

function save(user_id, datas, callback) {
    let operations = [];
    if (_.isArray(datas)) {
        for (let i = 0; i < datas.length; i++) {
            operations.push(operationMap[datas[i].updateType](user_id, datas[i]));
        }
    } else {
        operations.push(operationMap[datas.updateType](user_id, datas));
    }
    async_n.parallel(operations, function (err, results) {
        if (err) {
            callback(err, '');
        } else {
            if (results.length == 1) {
                callback(null, results[0])
            } else {
                callback(null, results)
            }
        }
    })
}

async function getLibInfo(req) {
    let gljLibId = null, engineerID, sessionCompilation = req.session.sessionCompilation;
    engineerID = req.params.engineerID;
    if(engineerID){
        gljLibId = await getGLJLibByEngineerID(engineerID);
    }else {
        throw new Error("工程专业ID为空！");
    }

    let data = {
        userID: req.session.sessionUser.id,
        gljLibId: gljLibId,
        compilationId: sessionCompilation._id
    };
    return data;
}

async function getGLJLibByEngineerID  (engineerID) {
    let engineeringLibModel = new EngineeringLibModel() ;
    let engineeringInfo = await engineeringLibModel.findDataByCondition({'_id': engineerID});
    let gljLibId = engineeringInfo.glj_lib.length > 0 && typeof engineeringInfo.glj_lib !== 'undefined' ? engineeringInfo.glj_lib[0].id : null;
    return gljLibId
}

// 获取人材机选择页面，可选的定额库-人材机库映射选项
async function getLibOptions(engineerID) {
    // 找到工程专业绑定的定额库，再获取这些定额库引用的人材机库
    const engineeringLibModel = new EngineeringLibModel();
    const engineeringInfo = await engineeringLibModel.findDataByCondition({ _id: engineerID });
    const rationLibs = engineeringInfo.ration_lib;
    const rationLibIDs = rationLibs.map(lib => lib.id);
    const rationLibData = await stdRationLibModel.find({ ID: { $in: rationLibIDs } }, 'ID gljLib').lean();
    const rst = [];
    rationLibs.forEach(lib => {
        rationLibData.forEach(stdLib => {
            if (+lib.id === +stdLib.ID) {
                lib.gljLibId = stdLib.gljLib;
                rst.push(lib);
            }
        });
    });
    return rst;
}

// 获取人材机选择库页面，可选的定额库为费用定额下，所有工程专业的定额库（需要去重）
async function getLibOptionsForCompilation(compilationId) {
    const compilation = await compilationModel.findOne({ _id: mongoose.Types.ObjectId(compilationId) }, 'ration_valuation bill_valuation');
    const valuationIDs = [];
    if (compilation.ration_valuation[0] && compilation.ration_valuation[0].enable) {
        valuationIDs.push(compilation.ration_valuation[0].id);
    }
    if (compilation.bill_valuation[0] && compilation.bill_valuation[0].enable) {
        valuationIDs.push(compilation.bill_valuation[0].id);
    }
    const engineeringLibs = await engineeringModel.find({ valuationID: { $in: valuationIDs }, visible: true }, 'ration_lib');
    const rationLibs = [];
    const rationLibIDs = [];
    let hasDefalut = false;
    engineeringLibs.forEach(lib => {
        lib.ration_lib.forEach(rLib => {
            if (!rationLibIDs.includes(rLib.id)) {
                rationLibIDs.push(rLib.id);
                rationLibs.push({
                    isDefault: hasDefalut ? false: rLib.isDefault,
                    name: rLib.name,
                    id: rLib.id
                });
                if (rLib.isDefault) {
                    hasDefalut = true;
                }
            }
        });
    });
    const stdRationLibs = await stdRationLibModel.find({ ID: { $in: rationLibIDs } }, 'ID gljLib').lean();
    rationLibs.forEach(lib => {
        stdRationLibs.forEach(stdLib => {
            if (+lib.id === +stdLib.ID) {
                lib.gljLibId = stdLib.gljLib;
            }
        });
    });
    return rationLibs;
}

function getGLJData(info, callback) {
    let gljDao = new GljDao();
    let datas = {};
    let gljDistTypeCache = stdgljutil.getStdGljTypeCacheObj().toArray();
    datas.distTypeTree = gljDistTypeCache;
    async_n.parallel([
        async function (cb) {
            try {
                datas.treeData = await gljDao.getMixedTree(info.gljLibId, info.userID, info.compilationId);
                cb(null);
            } catch (err) {
                cb(err);
            }
        },
        function (cb) {
            gljDao.getGljItems(info.gljLibId, info.userID, info.compilationId, {_id: 0}, function (err, data) {
                if (err) {
                    cb(err);
                } else {
                    datas.stdGLJ = data.stdGljs;
                    datas.complementaryGLJs = data.complementaryGljs;
                    cb(null);
                }
            });
        }
    ], function (err) {
        if (err) {
            callback(true, null);
        }
        else {
            callback(false, datas);
        }
    })

}

function getGLJSearchInfo(ration_glj) {
    let data = {
        glj_id: ration_glj.GLJID,
        project_id: ration_glj.projectID,
        code: ration_glj.code,
        original_code: ration_glj.original_code,
        name: ration_glj.name,
        //shortName: ration_glj.shortName,
        specs: ration_glj.specs?ration_glj.specs:'',
        unit: ration_glj.unit?ration_glj.unit:'',
        type: ration_glj.subType ? ration_glj.subType : ration_glj.type,//如果有subType，则是通过插入定额级的工料机进来的
        model: ration_glj.model,
        type_of_work: ration_glj.subType ? ration_glj.subType : ration_glj.type,
        base_price: ration_glj.basePrice,
        market_price: ration_glj.marketPrice,
        repositoryId: ration_glj.repositoryId,
        adjCoe: ration_glj.adjCoe,
        materialType:ration_glj.materialType,
        materialCoe:ration_glj.materialCoe,
        grossWeightCoe:ration_glj.grossWeightCoe,
        purchaseStorageRate:ration_glj.purchaseStorageRate,
        offSiteTransportLossRate:ration_glj.offSiteTransportLossRate,
        handlingLossRate:ration_glj.handlingLossRate,
        from: ration_glj.from ? ration_glj.from : 'std'//std:标准工料机库, cpt:补充工料机库
    };
    let glj_type_object = glj_type_util.getStdGljTypeCacheObj();
    let type = glj_type_object.getItemById(data.type);
    data.shortName = type.shortName;

    if (data.from == 'cpt') {//从补充工料机来的数据即为新增数据
        data.is_add = 1;
    }
    return data;
}

async function prepareExtData(projectID,compilation) {
    let ext , unitFileId ;
    let property = await projectDao.getProjectProperty(projectID);
    ext = projectDao.getExtendData(property,compilation);
    unitFileId = property.unitPriceFile !== undefined ? property.unitPriceFile.id : 0;
    return [unitFileId,ext];
}

function setPropertyFromStd(newGLJ,std_glj) {
    newGLJ.GLJID = std_glj.ID;
    newGLJ.name = std_glj.name;
    newGLJ.code = std_glj.code;
    newGLJ.original_code = std_glj.code;
    newGLJ.unit = std_glj.unit;
    newGLJ.specs = std_glj.specs;
    newGLJ.model = std_glj.model;
    newGLJ.basePrice = std_glj.basePrice;
    newGLJ.marketPrice = std_glj.basePrice;
    newGLJ.shortName = std_glj.shortName;
    newGLJ.type = std_glj.gljType;
    newGLJ.repositoryId = std_glj.repositoryId;
    newGLJ.adjCoe = std_glj.adjCoe;
    newGLJ.materialType = std_glj.materialType;
    newGLJ.materialCoe = std_glj.materialCoe;
    newGLJ.grossWeightCoe = std_glj.grossWeightCoe;
    newGLJ.purchaseStorageRate = std_glj.purchaseStorageRate;
    newGLJ.offSiteTransportLossRate = std_glj.offSiteTransportLossRate;
    newGLJ.handlingLossRate = std_glj.handlingLossRate;
    newGLJ.createType = 'normal';
}


async function addGLJ(rgList,compilation) {
    if (rgList.length <= 0) return {};
    let [newRecodes,projectGLJList] = await insertAddTypeGLJ(rgList,compilation);
    let stateResult = await glj_calculate_facade.calculateQuantity({
        projectID: rgList[0].projectID,
        rationID: rgList[0].rationID
    });
    let result = {
        projectGLJList:projectGLJList,
        newRecodes: newRecodes,
        showData: rgList,
        adjustState: stateResult.adjustState
    };
    return result;
}

async function insertAddTypeGLJ(rgList,compilation) {
    let newRecodes = [],GLJMap=null;
    let projectGLJList = [];
    let [unitFileId,ext] = await  prepareExtData(rgList[0].projectID,compilation);

    for (let g of rgList) {
        let projectGljModel = new GLJListModel();
        let result = await projectGljModel.addList(getGLJSearchInfo(g),unitFileId,ext);
        let typeString = result.type+'';
        g.marketPrice = result.unit_price.market_price;
        g.adjustPrice = result.unit_price.base_price;
        g.basePrice = result.unit_price.base_price;
        g.isAdd = result.unit_price.is_add;
        g.projectGLJID = result.id;
        if (typeString.startsWith("2")||typeString=='4'||typeString=='5') {//只有材料类型才显示是否暂估
            g.isEstimate = result.is_evaluate;
        }
        g.ID = uuidV1();
        if (result.subList && result.subList.length > 0) {
            g.subList = getMixRatioShowDatas(result.subList);
            //对于混凝土，砂浆，配合比，组成物还要插入定额工料机
            if(gljUtil.isConcreteType(g.type)){
                if(GLJMap == null){
                    let oldGLJList = await ration_glj.find({'rationID':g.rationID});
                    GLJMap = _.indexBy(oldGLJList,"projectGLJID");
                }
                addMixRatioToRationGLJ(g,result.subList,newRecodes,GLJMap);
            }
        }
        newRecodes.push(createNewRecord(g));
        projectGLJList.push(result);
    }
    await ration_glj.insertMany(newRecodes);
    return [newRecodes,projectGLJList];
}

async function deleteRationGLJ(data){
    let deleteIDs = [data.ID];
    if(gljUtil.isConcreteType(data.type)){//如果是混凝土、砂浆、配合比，还要删除没有被引用的组成物工料机
        let projectGljModel = new GLJListModel();
        let rationGLJList = await ration_glj.find({'rationID':data.rationID});
        let unitFileId  = await projectDao.getUnitPriceFileId(data.projectID);
        let projectGLJMap = {},referenceMap={};
        let deleteMix = [];
        for(let r of rationGLJList){
            projectGLJMap[r.projectGLJID] = r;
            if(gljUtil.isConcreteType(r.type)){
                if(r.ID == data.ID){//是要删除的工料机
                    deleteMix = await projectGljModel.getCompositionGLJByData(r,unitFileId);
                }else {
                    let comList = await projectGljModel.getCompositionGLJByData(r,unitFileId);
                    for(let c of comList){
                        referenceMap[c.id] = c;
                    }
                }
            }
        }
        for(let d of deleteMix){
            //删除不属于其它的组成物并且定额消耗为0
            if(referenceMap[d.id]) continue;
            if(projectGLJMap[d.id]&&(projectGLJMap[d.id].rationItemQuantity == '0'||projectGLJMap[d.id].rationItemQuantity == 0)){
                deleteIDs.push(projectGLJMap[d.id].ID);
            }
        }
        await ration_glj.deleteMany({'ID': {"$in": deleteIDs}});
    } else{
        await ration_glj.deleteOne({ID:data.ID});
    }
    let calcResult = await glj_calculate_facade.calculateQuantity({
        projectID: data.projectID,
        rationID: data.rationID
    });
    calcResult.deleteList = deleteIDs;
    return calcResult;
}


function addMixRatioToRationGLJ(g,subList,newRecodes,GLJMap){
    let newMap = {};
    for(let mr of subList ){
        //先查找该定额下是否已经有了工料机了，有就不用再插入了
        newMap[mr.id] = mr;//新增的定额工料机映射表
        if(GLJMap[mr.id]||_.find(newRecodes,{'projectGLJID':mr.id})) continue;
        //没有的情况下，生成一条定额工料机计录
        let newMr = {
            projectID:g.projectID,
            GLJID:mr.glj_id,
            rationID:g.rationID,
            rationItemQuantity:0,
            quantity:0,
            name:mr.name,
            code:mr.code,
            original_code:mr.original_code,
            unit:mr.unit,
            specs:mr.specs,
            from:mr.from,
            createType:'add',
            shortName:mr.unit_price.short_name,
            billsItemID:g.billsItemID,
            type:mr.type,
            model:mr.model,
            repositoryId:g.repositoryId,
            projectGLJID:mr.id,
            adjCoe:mr.adjCoe
        };
        newMr.ID = uuidV1();
        newRecodes.push(newMr);
    }
    return newMap;
}

async  function replaceGLJByData(data,compilation) {
    let projectGljModel = new GLJListModel(),newRecodes=[],deleteList=[];
    let [unitFileId,ext] = await  prepareExtData(data.projectID,compilation);
    let result = await projectGljModel.addList(getGLJSearchInfo(data),unitFileId,ext);
    data.projectGLJID = result.id;
    if(data.toCommercial == true){//从混凝土改成商品混凝土，
       let [contype,newR] =  await concreteTypeToCommercial(data);
        newRecodes.push(newR);
        data=contype;
    }else {
        let [newList,tdelList] = await replaceMixRatio(data,result,unitFileId);
        newRecodes = newList;
        deleteList = tdelList;
        let updateResult = await ration_glj.findOneAndUpdate({ID: data.ID, projectID: data.projectID}, data);//更新定额工料机
    }

    return {data:data,newRecodes:newRecodes,deleteList:deleteList,projectGLJ:result};
}


async function concreteTypeToCommercial(data) {
   //旧的自定义消耗量改为0
   let contype =  await ration_glj.findOneAndUpdate({ID: data.originalID}, {customQuantity:'0'});
    //因为商品混凝土是没有组成物的，所以不用考虑组成物的情况
    let new_glj =  createComercialConcreteData(data);
    await ration_glj.create(new_glj);
    return [contype,new_glj];
}

function createComercialConcreteData(data){
    data.ID = uuidV1();
    return createNewRecord(data);
}


async function replaceMixRatio(g,result,unitFileId){
    let newRecodes=[],deleteList = [];
    if(gljUtil.isConcreteType(g.type)||gljUtil.isCommercialConcreteType(g.type)){//混凝土大类，商品混凝土属于相同大类，替换前和替换后只判断一个就好了
        let IDMap = {}, projectGLJMap = {},referenceMap={},concreteList=[],newMap={};
        let projectGljModel = new GLJListModel();
        let rationGLJList = await ration_glj.find({'rationID':g.rationID});
        for(let r of rationGLJList){
            IDMap[r.ID] = r;
            projectGLJMap[r.projectGLJID] = r;
            if(gljUtil.isConcreteType(r.type)&&r.ID!=g.ID) concreteList.push(r) //除了本身，记录一下其它混凝土类型
        }
        if (result.subList && result.subList.length > 0){
            newMap =  addMixRatioToRationGLJ(g,result.subList,newRecodes,projectGLJMap);//先生成要添加的工料机
        }
        let oldMixList = await projectGljModel.getCompositionGLJByData(IDMap[g.ID],unitFileId);
        for(let c of concreteList){//找出要删除的子定额工料机（没人引用，并且定额消耗量为0）
            let temList = await projectGljModel.getCompositionGLJByData(c,unitFileId);
            for(let t of temList){
                referenceMap[t.id] = t;
            }
        }
        for(let o of oldMixList){
            if(newMap[o.id]||referenceMap[o.id]) continue;//如果两个地方有一个存在，那么就不用删除
            //没有被其它地方引用并且定额消耗量为0，就删除对应的定额工料机
            if(projectGLJMap[o.id] && (projectGLJMap[o.id].rationItemQuantity == '0'||projectGLJMap[o.id].rationItemQuantity == 0)) deleteList.push(projectGLJMap[o.id].ID)
        }
    }

    if(deleteList.length > 0) await ration_glj.deleteMany({'ID': {"$in": deleteList}});//删除定额工料机
    if(newRecodes.length > 0)  await ration_glj.insertMany(newRecodes);
    return [newRecodes,deleteList]
}

async function replaceGLJ(data,compilation) {
    let rdata = {};
    let r_result =await replaceGLJByData(data,compilation);
    data = r_result.data;
    let stateResult = await glj_calculate_facade.calculateQuantity({
        projectID: data.projectID,
        rationID: data.rationID
    }, null,true);
    rdata.data = data;
    rdata.projectGLJ = r_result.projectGLJ;
    rdata.adjustState = stateResult.adjustState;
    rdata.name = stateResult.rationName;
    rdata.newRecodes = r_result.newRecodes;
    rdata.deleteList = r_result.deleteList;
    rdata.glj_result = stateResult.glj_result;
    return rdata;
}

async function replaceMixRatioForMReplace(tasks,result,unitFileId) {
    let allNewRecodes = [],allDeleteList=[];
    for(let t of tasks){//要新增一条商品混凝土
        if(t.insertOne){
            t.insertOne.document.projectGLJID = result.id;
            t.insertOne.document = createComercialConcreteData(t.insertOne.document);
            allNewRecodes.push(t.insertOne.document);
        }else if(t.updateOne&&t.updateOne.update.isConcrete == true){//过滤掉只更新自定义消耗量的task
            let tem = _.cloneDeep(t.updateOne.update);
            tem.ID = t.updateOne.filter.ID;
            tem.rationID = t.updateOne.filter.rationID;
            delete t.updateOne.filter.rationID;
            delete t.updateOne.update.isConcrete;
            let [newRecodes,deleteList] = await replaceMixRatio(tem,result,unitFileId);
            allNewRecodes = allNewRecodes.concat(newRecodes);
            allDeleteList = allDeleteList.concat(deleteList);
        }
    }
    return {newRecodes:allNewRecodes,deleteList:allDeleteList}
}

async function mReplaceGLJ(data,compilation) {
    let mresult = {},mixResult = null,noNeedCal=true;
    let projectGljModel = new GLJListModel();
    let [unitFileId,ext] = await prepareExtData(data.doc.projectID,compilation);
    //
    let result = await projectGljModel.addList(getGLJSearchInfo(data.doc),unitFileId,ext);
    let typeString = result.type+'';
    let newDoc = {};
    newDoc.projectGLJID = result.id;
    let rationList = [];//await ration_glj.distinct('rationID', data.query);
    for(let t of data.tasks){
        if(t.updateOne){
            rationList.push(t.updateOne.filter.rationID);
            if(t.updateOne.update.code) t.updateOne.update.projectGLJID = result.id;//如果是不是只修改自定义消耗的task，更新项目工料机ID
        }
    }
    if(gljUtil.isConcreteType(result.unit_price.type)||gljUtil.isCommercialConcreteType(result.unit_price.type)) {
        mixResult = await replaceMixRatioForMReplace(data.tasks,result,unitFileId);
        noNeedCal = null;
    }
    await ration_glj.bulkWrite(data.tasks);

    let [stateList,glj_result] = await changAdjustState(data, rationList,noNeedCal);
    data.doc = newDoc;
    mresult.data = data;
    mresult.stateList = stateList;
    mresult.newRecodes = mixResult?mixResult.newRecodes:[];
    mresult.deleteList = mixResult?mixResult.deleteList:[];
    mresult.glj_result = glj_result;
    mresult.projectGLJ = result;
    return mresult
}

async function updateRationGLJByEdit(data) {
    var doc = data.doc;
    var result;
    if (doc.hasOwnProperty('customQuantity')) {
        result = await doCustomQuantityUpdate(data)
    } else {
        result = await doRationGLJUpdate(data);
    }
    return result;
}

async function updateRationGLJFromDoc(rg,doc,priceInfo) {
    let gljListModel = new GLJListModel();
    let projectGLJ = getGLJSearchInfo(rg);
    for (let key in doc) {
        projectGLJ[key] = doc[key]
    }
    projectGLJ.base_price = priceInfo.base_price;
    projectGLJ.market_price = priceInfo.market_price;
    let projcetGLJ_n = await gljListModel.modifyGLJ(projectGLJ, rg);
    doc.code = projcetGLJ_n.code;
    doc.projectGLJID = projcetGLJ_n.id;
    if (projcetGLJ_n.unit_price.is_add == 1) {
        doc.createType = 'replace';
        doc.rcode = projcetGLJ_n.original_code;
    } else {
        doc.createType = 'normal';
        doc.rcode = '';
    }
    await ration_glj.findOneAndUpdate({ID:rg.ID}, doc);
    return [projcetGLJ_n,doc]
}


async function doRationGLJUpdate(data) {
    let resutl = {};
    let priceInfo = data.priceInfo;
    let rg = await ration_glj.findOne(data.query);
    let [projcetGLJ_n,doc] = await updateRationGLJFromDoc(rg,data.doc,priceInfo);
    //取价格
    let gljListModel = new GLJListModel();
    gljListModel.getGLJPrice(projcetGLJ_n);
    doc.basePrice = projcetGLJ_n.unit_price.base_price;
    doc.marketPrice = projcetGLJ_n.unit_price.market_price;
    doc.adjustPrice = projcetGLJ_n.adjust_price;
    doc.isAdd = projcetGLJ_n.unit_price.is_add;
    resutl.doc = doc;
    let stateResult = await glj_calculate_facade.calculateQuantity({
        projectID: data.query.projectID,
        rationID: data.query.rationID
    },null,true);
    resutl.adjustState = stateResult.adjustState;
    resutl.name = stateResult.rationName;
    return resutl;
}

async function updateProportion(proportionList, projectID, rationID) {
    const bulks = proportionList.map(item => {
        return {
            updateOne: {
                filter: {ID: item.ID},
                update: {$set: {adjustProportion: item.adjustProportion}}
            }
        }
    });
    if (bulks.length) {
        await ration_glj.bulkWrite(bulks);
    }
    const calcRst = await glj_calculate_facade.calculateQuantity({ projectID, rationID }, null, true);
    const rationGLJ = {
        quantityRefresh: true,
        glj_result: calcRst.glj_result
    };
    const ration = {
        ID: calcRst.rationID,
        adjustState: calcRst.adjustState,
        name: calcRst.rationName
    };
    return { ration_glj: rationGLJ, ration, add:[], delete:[], replace: [] };
}

async function getGLJClass(info, data) {
    let result = {
        exist: false
    }
    //检查补充工料机中是否已经存在一样的记录了
    let condition = {
        userId: info.userID,
        compilationId: info.compilationId,
        code: data.code,
        name: data.name,
        unit: data.unit,
        gljType: data.type,
        basePrice: data.basePrice
    }
    if (data.specs != null && data.specs != undefined && data.specs != '') {
        condition['specs'] = data.specs;
    }
    let glj = await complementaryGljModel.find(condition);
    if (glj.length > 0) { //如果已存在就直接返回，不用再新增了
        result.exist = true;
        return result
    }
    //查找工料机类型树
    let items = await compleClassModel.find({
        userId: info.userID,
        compilationId: info.compilationId
    });
    result.items = items;
    return result;
}

async function insertGLJAsRation(data,compilation) {
    let gljList = data.gljList;
    if (data.hasOwnProperty("selectedSerialNo")) { //如果需要，更新序列号。
        let query = {
            projectID: data.projectID,
            billsItemID: data.billsItemID,
            serialNo: {$gt: data.selectedSerialNo}
        }
        await ration.update(query, {$inc: {serialNo: gljList.length}}, {multi: true});
    }
    let [unitFileId,ext] = await prepareExtData(data.projectID,compilation);
    for (let glj of gljList) {
        let p_glj = getGLJSearchInfo(glj);
        let projectGljModel = new GLJListModel();
        let result = await projectGljModel.addList(p_glj,unitFileId,ext);//逐条添加到项目工料机
        let typeString = result.type+'';
        glj.marketPrice = result.unit_price.market_price;
        glj.adjustPrice = result.unit_price.base_price;
        glj.basePrice = result.unit_price.base_price;
        glj.isAdd = result.unit_price.is_add;
        glj.projectGLJID = result.id;
        if (typeString.startsWith("2")||typeString=='4'||typeString=='5') {//只有材料类型才显示是否暂估
            glj.isEstimate = result.is_evaluate;
        }
    }
    await ration.insertMany(gljList);
    return gljList;
}

async function  getRationTypeGLJQuantity(projectID) {
    let rations = await ration.find({'projectID': projectID,'type':3,'deleteInfo': null}, ['ID', 'projectGLJID','quantity']);
    return rations;
}

async function changAdjustState(data, rationList,noNeedCal) {
    let stateList = [],glj_result=[];
    for (let r of rationList) {
        let stateResult = await glj_calculate_facade.calculateQuantity({
            projectID: data.query.projectID,
            rationID: r
        }, noNeedCal,true);
        if(stateResult){
            stateList.push({rationID: r, adjustState: stateResult.adjustState,name:stateResult.rationName});
            glj_result = glj_result.concat(stateResult.glj_result)
        }
    }
    return [stateList,glj_result];
}

async function getGLJDataByCodes(data,compilation) {
    let  gljLibId = await getGLJLibByEngineerID(data.engineerID);
    let gljDatas = [];
    if(gljLibId){
        let stdList =   await  std_glj_lib_gljList_model.find({'repositoryId':gljLibId,code:{'$in':data.codes}});
        if(stdList.length > 0){
            let property = await projectDao.getProjectProperty(data.projectID);
            let ext = projectDao.getExtendData(property,compilation);//多单价处理
            for(let s of stdList){
                let tem = JSON.parse(JSON.stringify(s));
                if(ext && ext.priceField && tem && tem.priceProperty){
                    tem.basePrice =  tem.priceProperty[ext.priceField];
                }
                gljDatas.push(tem);

            }
        }
    }
    return gljDatas
}


async function testError() {
    throw  new Error('test Error');
}

function getData(projectID, callback) {
    ration_glj.find({'projectID': projectID}, (err, datas) => {
        if (err) {
            callback(1, '', null);
        } else {
            callback(0, consts.projectConst.RATION_GLJ, datas);
        }
    })
}

function commonCallback(callback, result, err) {
    if (err) {
        callback(err, '');
    } else {
        callback(null, result);
    }
}

