'use strict';

/**
 *
 *
 * @author Zhong
 * @date 2018/6/1
 * @version
 */
const mongoose = require('mongoose');
const billsGuideLibModel = mongoose.model('std_billsGuidance_lib');
const billsGuideItemsModel = mongoose.model('std_billsGuidance_items');
const stdBillsLibModel = mongoose.model('std_bills_lib_list');
const stdBillsModel = mongoose.model('std_bills_lib_bills');
const stdRationModel = mongoose.model('std_ration_lib_ration_items');
const _ = require('lodash');

module.exports = {
    getLibWithBills,
    getItemsBybills,
    getItemsByCode,
};

async function getBillsGuideLibs(findData) {
    return await billsGuideLibModel.find(findData);
}


async function getLibWithBills(libID, isGuidanceLib){
    let billsLibId = libID;
    if (isGuidanceLib) {
        const guidanceLib = await getBillsGuideLibs({ID: libID});
        if(guidanceLib.length === 0){
            throw '不存在此指引库！';
        }
        const billsLib = await stdBillsLibModel.findOne({billsLibId: guidanceLib[0].billsLibId});
        if(!billsLib){
            throw '引用的清单规则库不存在！';
        }
        billsLibId = billsLib.billsLibId;
    }
    const bills = await stdBillsModel.find({ billsLibId }, '-_id').lean();
    return { bills };
}

function getAttrs(field, datas){
    let rst = [];
    for(let data of datas){
        if(data[field]){
            rst.push(data[field]);
        }
    }
    return rst;
}

//定额项目指所引用定额是否被删除
function rationAllExist(rationItems, stdRationIdx) {
    for(let item of rationItems){
        if(!stdRationIdx[item.rationID]){
            return false;
        }
    }
    return true;
}

//将同层树结构转为顺序数组
function chainToArr(nodes){
    let rst = [];
    let tempIdx = {};
    let nodeIdx = {};
    //建索引
    for(let node of nodes){
        tempIdx[node.ID] = {ID: node.ID, NextSiblingID: node.NextSiblingID, preSibling: null, nextSibling: null};
        nodeIdx[node.ID] = node;
    }
    //建链
    for(let i in tempIdx){
        let temp = tempIdx[i];
        if(temp.NextSiblingID != -1){
            let next = tempIdx[temp.NextSiblingID];
            temp.nextSibling = next;
            next.preSibling = temp;
        }
    }
    let firstNode = null;
    for(let i in tempIdx){
        if(!tempIdx[i].preSibling){
            firstNode = tempIdx[i];
            break;
        }
    }
    //获得顺序队列
    while(firstNode){
        rst.push(nodeIdx[firstNode.ID]);
        firstNode = firstNode.nextSibling;
    }
    return rst;
}

async function getItemsBybills(guidanceLibID, billsID){
    const type = {job: 0, ration: 1};
    let items = await billsGuideItemsModel.find({libID: guidanceLibID, billsID: billsID, deleted: false});
    let rationItems = _.filter(items, {type: type.ration});
    let rationIds = getAttrs('rationID', rationItems);
    let stdRations = await stdRationModel.find({ID: {$in: rationIds}, $or: [{isDeleted: null}, {isDeleted: false}]});
    let stdRationIndex = {};
    for(let stdRation of stdRations){
        stdRationIndex[stdRation.ID] = stdRation;
    }
    //判断定额完整性
    if(!rationAllExist(rationItems, stdRationIndex)){
        //建定额链, 排序后再清除不存在的定额，保证顺序正确性
        rationItems = chainToArr(rationItems);
        //清除已被删除的定额
        let removeIds = [];
        _.remove(rationItems, function (item) {
            if(!stdRationIndex[item.rationID]){
                removeIds.push(item.ID);
                return true;
            }
            return false;
        });
        _.remove(items, function (item) {
           return removeIds.includes(item.ID);
        });
        await billsGuideItemsModel.remove({ID: {$in: removeIds}});
        //重组树结构
        let bulkArr = [];
        for(let i = 0, len = rationItems.length; i < len; i++){
            rationItems[i].NextSiblingID = rationItems[i + 1] ? rationItems[i + 1].ID : -1;
            bulkArr.push({updateOne: {filter: {ID: rationItems[i].ID}, update: {$set: {NextSiblingID: rationItems[i].NextSiblingID}}}});
        }
        await billsGuideItemsModel.bulkWrite(bulkArr);
    }
    return items;
}

async function getItemsByCode(guidanceLibID, code){
    let rst = [];
    let guidanceLib = await billsGuideLibModel.findOne({ID: guidanceLibID});
    if (!guidanceLib) {
        return rst;
    }
    let stdBills = await stdBillsModel.findOne({code: code, billsLibId: guidanceLib.billsLibId}).lean();
    if (!stdBills) {
        return rst;
    }
    let items = await getItemsBybills(guidanceLibID, stdBills.ID);
    return items;
}