/*
 * @Author: baozhoutao@steedos.com
 * @Date: 2022-09-15 13:09:51
 * @LastEditors: baozhoutao@steedos.com
 * @LastEditTime: 2023-03-14 11:59:35
 * @Description: 
 */
const express = require("express");
const router = express.Router();
const { requireAuthentication } = require("@steedos/auth");
const _ = require('lodash');
const objectql = require('@steedos/objectql');
const UUFlowManager = require('../manager/uuflow_manager');

const getFlowVersion = (flow, flowVersionId) => {
    if (flow.current._id === flowVersionId) {
        return flow.current;
    } else {
        return _.find(flow.historys, (history) => {
            return history._id === flowVersionId;
        });
    }
}

const getStep = (flow, flowVersionId, stepId) => {
    const flowVersion = getFlowVersion(flow, flowVersionId);
    const step = _.find(flowVersion.steps, (step) => {
        step.id = step._id;
        return step._id === stepId
    });
    return step;
}

const getSteps = (flow, flowVersionId) => {
    const flowVersion = getFlowVersion(flow, flowVersionId);
    const steps = _.map(flowVersion.steps, (step) => {
        step.id = step._id;
        return step;
    });
    return steps;
}

const getStepsById = (flow, flowVersionId, stepIds) => {
    const flowVersion = getFlowVersion(flow, flowVersionId);
    const steps = _.filter(flowVersion.steps, (step) => {
        step.id = step._id;
        return _.includes(stepIds, step.id)
    });
    return steps;
}

const isSkipStep = function (instance, step) {
    return _.includes(instance.skip_steps, step._id)
}

const getNextSteps = async (flow, flowVersionId, instance, currentStep, judge, autoFormDoc, fields, showSkipStep) => {
    if (!currentStep)
        return;

    if (!instance) {
        return [];
    }

    let nextSteps = [];
    const lines = currentStep.lines;

    switch (currentStep.step_type) {
        case 'condition': //条件
            const stepIds = await UUFlowManager.getNextSteps(instance, flow, currentStep, judge, autoFormDoc);
            nextSteps = getStepsById(flow, flowVersionId, stepIds);
            if (!nextSteps.length)
                throw new Error('未能根据条件找到下一步');
            break;
        case 'end': //结束
            return nextSteps;
        case 'sign': //审批
            if (judge == 'approved') { //核准
                for (const line of lines) {
                    if (line.state == "approved") {
                        const step = getStep(flow, flowVersionId, line.to_step);
                        nextSteps.push(step);
                    }
                }
            } else if (judge == "rejected") { //驳回
                lines.forEach(function (line) {
                    if (line.state == "rejected") {
                        const rejected_step = getStep(flow, flowVersionId, line.to_step);
                        // 驳回时去除掉条件节点
                        if (rejected_step && rejected_step.step_type != "condition")
                            nextSteps.push(rejected_step);
                    }
                });

                const traces = instance.traces;

                traces.forEach(function (trace) {
                    if (trace.is_finished == true) {
                        const finished_step = getStep(flow, flowVersionId, trace.step);
                        if (finished_step.step_type != 'condition' && currentStep.id != finished_step.id)
                            nextSteps.push(finished_step);
                    }
                });

                //驳回时支持结束步骤
                const flow_steps = getSteps(flow, flowVersionId);
                const end_step = _.find(flow_steps, { step_type: "end" });

                nextSteps.push(end_step);
            }
            break;
        default: //start：开始、submit：填写、counterSign：会签
            if (currentStep.step_type === 'counterSign' && currentStep.oneClickRejection && judge === "rejected") {
                lines.forEach(function (line) {
                    if (line.state == "rejected") {
                        const rejected_step = getStep(flow, flowVersionId, line.to_step);
                        // 驳回时去除掉条件节点
                        if (rejected_step && rejected_step.step_type != "condition")
                            nextSteps.push(rejected_step);
                    }
                });

                const traces = instance.traces;

                traces.forEach(function (trace) {
                    if (trace.is_finished == true) {
                        const finished_step = getStep(flow, flowVersionId, trace.step);
                        if (finished_step.step_type != 'condition' && currentStep.id != finished_step.id)
                            nextSteps.push(finished_step);
                    }
                });

                //驳回时支持结束步骤
                const flow_steps = getSteps(flow, flowVersionId);
                const end_step = _.find(flow_steps, { step_type: "end" });

                nextSteps.push(end_step);
            } else {
                lines.forEach(function (line) {
                    if (line.state == "submitted") {
                        const submitted_step = getStep(flow, flowVersionId, line.to_step);
                        if (submitted_step)
                            nextSteps.push(submitted_step);
                    }
                });
            }
            break;
    }

    //去除重复
    nextSteps = _.compact(_.uniqBy(nextSteps, 'id'));

    let condition_next_steps = [];
    for (const nextStep of nextSteps) {
        if (nextStep.step_type == "condition") {
            if (!judge && nextStep.step_type == 'sign') {
                judge = 'approved';
            }
            const conditionSteps = await getNextSteps(flow, flowVersionId, instance, nextStep, judge, autoFormDoc, fields, showSkipStep);
            condition_next_steps = condition_next_steps.concat(conditionSteps);
        }
    }

    nextSteps = nextSteps.concat(condition_next_steps);

    let rev_nextSteps = [];

    for (const nextStep of nextSteps) {
        if (nextStep.step_type != "condition") {
            if (!showSkipStep && isSkipStep(instance, nextStep)) {
                if (!judge && nextStep.step_type == 'sign') {
                    judge = 'approved';
                }
                let nextStepsAfterSkipStep = await getNextSteps(flow, flowVersionId, instance, nextStep, judge, autoFormDoc, fields, showSkipStep);
                let stepsWithOutCurrentStep = [];
                for (const s of nextStepsAfterSkipStep) {
                    if (currentStep.id == s.id) {
                        continue;
                    }
                    stepsWithOutCurrentStep.push(s);
                }
                // 后续步骤不应包含当前步骤
                rev_nextSteps = rev_nextSteps.concat(stepsWithOutCurrentStep);
            } else {
                rev_nextSteps.push(nextStep);
            }
        }
    }

    //去除重复
    rev_nextSteps = _.compact(_.uniqBy(rev_nextSteps, 'id'));

    // 会签节点，如果下一步有多个 则清空下一步
    if (currentStep.step_type == "counterSign" && rev_nextSteps.length > 1 && !currentStep.oneClickRejection) {
        rev_nextSteps = [];
    }
    return rev_nextSteps;
}

router.post('/api/workflow/v2/nextStep', requireAuthentication, async function (req, res) {
    try {
        const userSession = req.user;
        const { instanceId, flowId, step, judge, values, flowVersionId } = req.body;
        const flow = await objectql.getObject('flows').findOne(flowId);
        const instance = await objectql.getObject('instances').findOne(instanceId);
        
        const resNextSteps = await getNextSteps(flow, flowVersionId, instance, step, judge, values);
        res.status(200).send({
            'nextSteps': resNextSteps
        });
    } catch (error) {
        console.error(error);
        res.status(200).send({
            error: error.message
        });
    }
});

const calcSteps = async function(instance, flow, flowVersionId, formFields, values, step, track){
	if (!step) {
		step = getStep(flow, flowVersionId, instance.traces[0].step);
	}
	if (!track) {
		track = []
	}

	var _steps = [];
	var judge = 'approved';
	
	_steps.push(step);
	try {
        var nextSteps = await getNextSteps(flow, flowVersionId, instance, step, judge, values);
        for (const nextStep of nextSteps) {
            try {
                if (!_.includes(track, nextStep._id)) {
                    const __steps = await calcSteps(instance, flow, flowVersionId, formFields, values, nextStep, track.concat(_.map(_steps, '_id')));
                    _steps = _steps.concat(__steps)
                }
            } catch (e) {
                console.log(e)
            }
        }
    } catch (error) {
        console.log(error)
    }
	_steps = _.compact(_.uniqBy(_steps, 'id'));
	return _steps;
}

router.post('/api/workflow/v2/nextSteps', requireAuthentication, async function (req, res) {
    try {
        const userSession = req.user;
        const { instanceId, flowId, values, flowVersionId } = req.body;
        const flow = await objectql.getObject('flows').findOne(flowId);
        const instance = await objectql.getObject('instances').findOne(instanceId);
        const form = await objectql.getObject('forms').findOne(instance.form);
        const { fields } = await UUFlowManager.getFormVersion(form, instance.form_version);
        const resNextSteps = await calcSteps(instance, flow, flowVersionId, fields, values);
        res.status(200).send({
            'nextSteps': resNextSteps
        });
    } catch (error) {
        console.error(error);
        res.status(200).send({
            error: error.message
        });
    }
})

router.get('/api/workflow/v2/get_instance_steps/:instanceId', requireAuthentication, async function (req, res) {
    try {
        const { instanceId } = req.params;
        if(instanceId === 'none'){
            return res.status(200).send({ status: 0 });
        }
        const instance = await objectql.getObject('instances').findOne(instanceId);
        if(!instance){
            return res.status(200).send({
                error: 'not find instance'
            });
        }
        return res.status(200).send({ status: 0, data: {
            step_approve: instance.step_approve,
            skip_steps: instance.skip_steps
        } });
    } catch (error) {
        console.error(error);
        res.status(200).send({
            error: error.message
        });
    }
})

router.post('/api/workflow/v2/set_instance_steps', requireAuthentication, async function (req, res) {
    try {
        const userSession = req.user;
        const { instanceId, stepId, selected, handler } = req.body;
        if(instanceId === 'none'){
            return res.status(200).send({ status: 0 });
        }
        const instance = await objectql.getObject('instances').findOne(instanceId);
        if(!instance){
            return res.status(200).send({
                error: 'not find instance'
            });
        }
        if(instance.state != 'draft'){
            return res.status(200).send({
                error: 'not allow'
            });
        }
        const doc = {
            ['step_approve.' + stepId]: handler
        };
        if(selected){
            doc.$pull = {
                skip_steps: stepId
            }
        }else{
            doc.$push = {
                skip_steps: stepId
            }
        }
        await objectql.getObject('instances').update(instanceId, doc);

        return res.status(200).send({ status: 0 });
    } catch (error) {
        console.error(error);
        res.status(200).send({
            error: error.message
        });
    }
})

exports.default = router;