
const { getCollection, _makeNewID } = require('../utils/collection');
const UUFlowManager = require('../manager/uuflow_manager.js');
const pushManager = require('../manager/push_manager.js');
const approveManager = require('../manager/approve_manager.js');
const {
    update_instance_tasks,
    insert_instance_tasks,
    remove_instance_tasks,
    direct_remove_many_instance_tasks
} = require('../manager/instance_tasks_manager')

module.exports = {
    cc_do: async function (approve, cc_user_ids, description, ctx = {}) {
        var setObj = {};
        var ins_id = approve.instance;
        var trace_id = approve.trace;
        var approve_id = approve._id;

        const instancesCollection = await getCollection('instances');
        const usersCollection = await getCollection('users');
        const spaceUsersCollection = await getCollection('space_users');
        const organizationsCollection = await getCollection('organizations');

        const instance = await instancesCollection.findOne({ _id: ins_id }, {
            projection: {
                space: 1,
                traces: 1,
                cc_users: 1,
                values: 1
            }
        });
        var current_user_id = ctx.userId || this.userId;
        var space_id = instance.space;
        var new_approves = [];

        const from_user = await usersCollection.findOne({ _id: current_user_id })
        const from_user_name = from_user.name;

        for (const [idx, userId] of cc_user_ids.entries()) {

            const user = await usersCollection.findOne({ _id: userId }, {
                projection: {
                    name: 1
                }
            })

            const space_user = await spaceUsersCollection.findOne({ user: userId, space: space_id }, {
                projection: {
                    organization: 1
                }
            })

            var org_id = space_user.organization;

            const organization = await organizationsCollection.findOne({ _id: org_id }, {
                projection: {
                    name: 1,
                    fullname: 1
                }
            })

            var agent = await UUFlowManager.getAgent(space_id, userId);
            var handler_id = userId;
            var handler_info = user;
            var handler_space_user = space_user;
            var handler_org_info = organization;
            if (agent) {
                handler_id = agent;
                handler_info = await usersCollection.findOne({ _id: agent }, {
                    projection: {
                        name: 1
                    }
                })

                handler_space_user = await UUFlowManager.getSpaceUser(space_id, agent);
                handler_org_info = await UUFlowManager.getSpaceUserOrgInfo(handler_space_user);
                cc_user_ids[idx] = agent;
            }
            var appr = {
                '_id': _makeNewID(),
                'instance': ins_id,
                'trace': trace_id,
                'is_finished': false,
                'user': userId,
                'user_name': user.name,
                'handler': handler_id,
                'handler_name': handler_info.name,
                'handler_organization': handler_space_user.organization,
                'handler_organization_name': handler_org_info.name,
                'handler_organization_fullname': handler_org_info.fullname,
                'type': 'cc',
                'start_date': new Date(),
                'is_read': false,
                'from_user': current_user_id,
                'from_user_name': from_user_name,
                'opinion_fields_code': approve.opinion_fields_code,
                'sign_field_code': (approve.opinion_fields_code && approve.opinion_fields_code.length == 1) ? approve.opinion_fields_code[0] : "",
                'from_approve_id': approve_id,
                'cc_description': description
            };
            if (agent) {
                appr.agent = agent;
            }
            await UUFlowManager.setRemindInfo(instance.values, appr)
            new_approves.push(appr);
        }

        setObj.modified = new Date();
        setObj.modified_by = ctx.userId;

        await instancesCollection.updateOne({
            _id: ins_id,
            'traces._id': trace_id
        }, {
            $set: setObj,
            $push: {
                'traces.$.approves': {
                    $each: new_approves
                },
                cc_users: {
                    $each: cc_user_ids
                }
            },
            $inc: {
                cc_count: cc_user_ids.length
            }
        });
        // 新增记录
        for (const a of new_approves) {
            await insert_instance_tasks(ins_id, trace_id, a._id)
        }

        const updatedInstance = await instancesCollection.findOne({ _id: ins_id });

        await pushManager.send_instance_notification("trace_approve_cc", updatedInstance, "", from_user, cc_user_ids);

        flow_id = instance.flow;
        approve.cc_user_ids = cc_user_ids; // 记录下本次传阅的人员ID作为hook接口中的参数
        // 如果已经配置webhook并已激活则触发
        await pushManager.triggerWebhook(flow_id, instance, approve, 'cc_do', current_user_id, cc_user_ids)
        return true;
    },

    cc_read: async function (instance, approve, ctx = {}) {
        const db = {
            instances: await getCollection('instances')
        };
        var setObj = {};
        var ins_id = approve.instance;
        var trace_id = approve.trace;
        var approve_id = approve._id;
        // var instance = await db.instances.findOne({ _id: ins_id }, {
        //     fields: {
        //         traces: 1
        //     }
        // });
        var current_user_id = ctx.userId || this.userId;
        var current_trace = _.find(instance.traces, function (t) {
            return t._id == trace_id;
        })

        var index = 0;

        current_trace.approves.forEach(function (a, idx) {
            if (approve_id == a._id && a.type == 'cc' && a.handler == current_user_id && !a.is_read) {
                index = idx;
            }
        });

        // setObj['traces.$.approves.' + index + '.is_read'] = true;
        // setObj['traces.$.approves.' + index + '.read_date'] = new Date();
        const key_str = 'traces.$.approves.' + index + '.';
        const approveDoc = {
            is_read: true,
            read_date: new Date()
        }
        for (const key in approveDoc) {
            if (Object.hasOwnProperty.call(approveDoc, key)) {
                setObj[key_str + key] = approveDoc[key]
            }
        }


        await db.instances.update({
            _id: ins_id,
            'traces._id': trace_id
        }, {
            $set: setObj
        });
        await update_instance_tasks(ins_id, trace_id, current_trace.approves[index]._id, approveDoc)
        return true;
    },

    cc_submit: async function (ins_id, description, myApprove, ccHasEditPermission, ctx = {}) {
        const db = {
            instances: await getCollection('instances'),
            forms: await getCollection('forms'),
            users: await getCollection('users')
        };
        var setObj = {};

        var instance = await db.instances.findOne({_id: ins_id});
        var traces = instance.traces;
        var current_user_id = ctx.userId;

        var flow = await UUFlowManager.getFlow(instance.flow);
        var values = myApprove.values || {};

        var approve_id = myApprove._id;

        var myTrace;

        var is_finished; // 传阅正真结束的标记

        for (let tidx = 0; tidx < traces.length; tidx++) {
            const t = traces[tidx];
            if (t.approves) {
                for (let aidx = 0; aidx < t.approves.length; aidx++) {
                    const a = t.approves[aidx];
                    if (approve_id == a._id && a.type == 'cc' && a.handler == current_user_id && a.is_finished == false) {
                        var upobj = {};
                        var key_str = 'traces.$.approves.' + aidx + '.';
                        const approveDoc = {
                            'is_finished': true,
                            'is_read': true,
                            'finish_date': new Date(),
                            'judge': "submitted",
                            'cost_time': new Date() - a.start_date,
                        }
                        if (approve_id == a._id && !t.is_finished && ccHasEditPermission) {
                            myTrace = t;
                            var step = await UUFlowManager.getStep(instance, flow, t.step);
                            approveDoc['values'] = await UUFlowManager.getApproveValues(values, step["permissions"], instance.form, instance.form_version)
                        }
                        //设置意见，意见只添加到最后一条approve中
                        if (approve_id == a._id) {
                            approveDoc['description'] = description
                        }
                        for (const key in approveDoc) {
                            if (Object.hasOwnProperty.call(approveDoc, key)) {
                                upobj[key_str + key] = approveDoc[key]
                            }
                        }
                        await db.instances.updateOne({
                            _id: ins_id,
                            'traces._id': t._id
                        }, {
                            $set: upobj
                        })
                        // 更新
                        await update_instance_tasks(ins_id, a.trace, a._id, approveDoc)
                        is_finished = true;
                    }
                }
            }

        }

        if (myApprove && is_finished) {

            setObj.modified = new Date();
            setObj.modified_by = ctx.userId;

            if (ccHasEditPermission && myApprove && !myTrace.is_finished) {
                var ins = await UUFlowManager.getInstance(ins_id);
                var updated_values = await UUFlowManager.getUpdatedValues(ins, approve_id);
                setObj.values = updated_values;
                setObj.name = await UUFlowManager.getInstanceName(instance);
                // 计算extras
                var form = await db.forms.findOne({ _id : instance.form});
                setObj.extras = await UUFlowManager.caculateExtras(setObj.values, form, instance.form_version);
            }

            await db.instances.updateOne({
                _id: ins_id,
                cc_users: current_user_id
            }, {
                $set: setObj,
                $unset: {
                    'cc_users.$': true
                },
                $addToSet: {
                    outbox_users: {
                        $each: [current_user_id, myApprove.user]
                    }
                }
            });

            await db.instances.updateOne({
                _id: ins_id,
            }, {
                $pull: {
                    cc_users: null
                }
            });

            await UUFlowManager.updateCCcount(ins_id)

            const instance = await db.instances.findOne({ _id: ins_id});

            const current_user_info = await db.users.findOne({ _id: current_user_id});
            //传阅提交不通知传阅者
            if (false && description && myApprove && myApprove.from_user) {
                await pushManager.send_instance_notification("trace_approve_cc_submit", instance, "", current_user_info, [myApprove.from_user]);
            }

            await pushManager.send_message_to_specifyUser("current_user", current_user_id);

            const flow_id = instance.flow;
            // 如果已经配置webhook并已激活则触发
            await pushManager.triggerWebhook(flow_id, instance, myApprove, 'cc_submit', current_user_id, []);
        }

        return true;
    },

    cc_remove: async function (instanceId, approveId) {
        const db = {
            instances: await getCollection('instances'),
        };

        var setObj = {};

        var instance = await db.instances.findOne({_id: instanceId}, {
            projection: {
                traces: 1,
                cc_users: 1
            }
        });
        var traces = instance.traces;
        var trace_id, remove_user_id, multi = false;

        traces.forEach(function (t) {
            if (t.approves) {
                t.approves.forEach(function (a, idx) {
                    if (a._id == approveId) {
                        trace_id = a.trace;
                        remove_user_id = a.handler;
                        setObj['traces.$.approves.' + idx + '.judge'] = 'terminated';
                        setObj['traces.$.approves.' + idx + '.is_finished'] = true;
                        setObj['traces.$.approves.' + idx + '.finish_date'] = new Date();
                        setObj['traces.$.approves.' + idx + '.is_read'] = true;
                        setObj['traces.$.approves.' + idx + '.read_date'] = new Date();
                    }
                });
            }
        })

        if (!trace_id || !remove_user_id)
            return;

        var multi = 0;
        traces.forEach(function (t) {
            if (t.approves) {
                t.approves.forEach(function (a) {
                    if (a.handler == remove_user_id && a.type == 'cc' && a.is_finished == false) {
                        multi++;
                    }
                });
            }
        })

        setObj.modified = new Date();
        setObj.modified_by = ctx.userId;

        if (multi > 1) {
            await db.instances.updateOne({
                _id: instanceId,
                'traces._id': trace_id
            }, {
                $set: setObj
            });
        } else {
            await db.instances.updateOne({
                _id: instanceId,
                'traces._id': trace_id
            }, {
                $set: setObj,
                $pull: {
                    cc_users: remove_user_id
                }
            });
        }

        await UUFlowManager.updateCCcount(instanceId)

        await remove_instance_tasks(approveId)


        await pushManager.send_message_to_specifyUser("current_user", remove_user_id);
        return true;
    },

    batch_cancel_cc: async function (instance_id, approve_ids) {
        const db = {
            instances: await getCollection('instances'),
        };

        var setObj = {};

        var instance = await db.instances.findOne(instance_id, {
            projection: {
                traces: 1,
                cc_users: 1
            }
        });
        var traces = instance.traces;
        var remove_user_ids = [];
        var removeApproveIds = []

        traces.forEach(function (t, tIdx) {
            if (t.approves) {
                t.approves.forEach(function (a, idx) {
                    if (approve_ids.includes(a._id)) {
                        removeApproveIds.push(a._id)
                        remove_user_ids.push(a.user);
                        setObj['traces.' + tIdx + '.approves.' + idx + '.judge'] = 'terminated';
                        setObj['traces.' + tIdx + '.approves.' + idx + '.is_finished'] = true;
                        setObj['traces.' + tIdx + '.approves.' + idx + '.finish_date'] = new Date();
                        setObj['traces.' + tIdx + '.approves.' + idx + '.is_read'] = true;
                        setObj['traces.' + tIdx + '.approves.' + idx + '.read_date'] = new Date();
                    }
                });
            }
        })

        if (_.isEmpty(remove_user_ids))
            return;

        setObj.modified = new Date();
        setObj.modified_by = ctx.userId;

        await db.instances.updateOne({
            _id: instance_id
        }, {
            $set: setObj,
        });
        for (const ruId of remove_user_ids) {
            await db.instances.updateOne({
                _id: instance_id,
                cc_users: ruId
            }, {
                $unset: {
                    'cc_users.$': true
                }
            });
        }
        await db.instances.updateOne({
            _id: instance_id,
        }, {
            $pull: {
                cc_users: null
            }
        });

        await UUFlowManager.updateCCcount(instance_id)

        await direct_remove_many_instance_tasks(removeApproveIds)

        for (const u_id of remove_user_ids) {
            await pushManager.send_message_to_specifyUser("current_user", u_id);
        }
        return true;
    },

    cc_save: async function (ins_id, description, myApprove, ccHasEditPermission) {
        const db = {
            instances: await getCollection('instances'),
            forms: await getCollection('forms'),
        };
        var setObj = {};
        const approveDoc = {}

        var instance = await db.instances.findOne({ _id: ins_id});
        var traces = instance.traces;
        var current_user_id = ctx.userId;

        var trace_id = myApprove.trace;
        var approve_id = myApprove._id;

        var current_trace = _.find(traces, function (t) {
            return t._id == trace_id;
        });
        var current_approve = _.find(current_trace.approves, function (a) {
            return a._id == approve_id;
        });

        var myTrace;

        for (const t of traces) {
            if (t.approves) {
                for (const [idx, a] of t.approves.entries()) {
                    if (a.handler == current_user_id && a.type == 'cc' && a.is_finished == false) {
                        var upobj = {};
                        upobj['traces.$.approves.' + idx + '.judge'] = "submitted";
                        upobj['traces.$.approves.' + idx + '.read_date'] = new Date();
                        await db.instances.update({
                            _id: ins_id,
                            'traces._id': t._id
                        }, {
                            $set: upobj
                        })

                    }
                };
            }
        }

        var index = 0;
        var currentStepId;

        //设置意见，意见只添加到最后一条approve中
        traces.forEach(function (t) {
            if (myApprove && t._id === myApprove.trace) {
                currentStepId = t.step;
                myTrace = t;
                if (t.approves) {
                    t.approves.forEach(function (a, idx) {
                        if (a._id === myApprove._id) {
                            index = idx;
                        }
                    });
                }
            }
        });

        setObj['traces.$.approves.' + index + '.description'] = description;
        approveDoc['description'] = description

        var updateObj = {};

        if (ccHasEditPermission && myApprove && !myTrace.is_finished) {

            var key_str = 'traces.$.approves.' + index + '.';

            var flow = await UUFlowManager.getFlow(instance.flow);

            var step = UUFlowManager.getStep(instance, flow, currentStepId);

            var permissions_values = await UUFlowManager.getApproveValues(myApprove.values, step.permissions, instance.form, instance.form_version);

            var change_values = approveManager.getChangeValues(instance.values, permissions_values);

            setObj.values = _.extend((instance.values || {}), permissions_values);
            // 计算extras
            var form = await db.forms.findOne({ _id: instance.form });
            setObj.extras = await UUFlowManager.caculateExtras(setObj.values, form, instance.form_version);
            approveDoc['extras'] = setObj.extras

            if (!_.isEmpty(change_values)) {
                var pushObj = {};
                pushObj[key_str + 'values_history'] = {
                    values: change_values,
                    create: new Date()
                }
                updateObj.$push = pushObj;

                const values_history = current_approve.values_history || []
                values_history.push(pushObj[key_str + 'values_history'])
                approveDoc['values_history'] = values_history
            }

            setObj.name = await UUFlowManager.getInstanceName(instance)
            approveDoc['instance_name'] = setObj.name
        }

        updateObj.$set = setObj;

        await db.instances.updateOne({
            _id: ins_id,
            'traces._id': myApprove.trace
        }, updateObj);
        await update_instance_tasks(ins_id, myApprove.trace, myApprove._id, approveDoc)
        return true;
    }

}