const objectql = require("@steedos/objectql");
const _ = require('underscore');
const { _makeNewID, getCollection } = require('../utils/collection');

const steedosImport = {};

function _formatFieldsID(fields) {
    _.each(fields, function (f) {
        if (!f._id && f.id) {
            f._id = f.id;
            delete f.id;
            if (f.type === 'section' || f.type === 'table') {
                return _formatFieldsID(f.fields);
            }
        }
    });
    return fields;
}

async function upgradeFlowByForm(flow, formVersionId, options) {
    const { now, currentUserId, spaceId } = options;
    const flowCollection = await getCollection('flows');
    const instancesCollection = await getCollection('instances');

    const hasInstances = await instancesCollection.countDocuments({
        space: spaceId,
        flow: flow._id,
        flow_version: flow.current._id
    });

    const flowUpdateObj = { $set: {} };

    if (hasInstances && flow.current.start_date) {
        flowUpdateObj.$push = { 'historys': flow.current };
        const flowCurrent = {
            '_id': _makeNewID(),
            'created': now,
            'created_by': currentUserId,
            'steps': flow.current.steps,
            '_rev': flow.current._rev + 1,
            'flow': flow._id,
            'form_version': formVersionId,
            'modified': now,
            'modified_by': currentUserId
        };

        if (flow.state === "enabled") {
            flowCurrent.start_date = now;
        }

        flowUpdateObj.$set['current'] = flowCurrent;
    } else {
        flowUpdateObj.$set = {
            'current.form_version': formVersionId,
            'current.modified': now,
            'current.modified_by': currentUserId
        };
    }

    flowUpdateObj.$set['modified'] = now;
    flowUpdateObj.$set['modified_by'] = currentUserId;
    return flowCollection.updateOne({ _id: flow._id }, flowUpdateObj);
}

async function upgradeForm(formId, form, currentUserId, spaceId) {
    const now = new Date();
    const formCollection = await getCollection('forms');
    const flowCollection = await getCollection('flows');
    const ff = await formCollection.findOne({ _id: formId, space: spaceId });

    if (!ff) {
        throw new Error("无效的formId");
    }

    const formUpdateObj = {};
    let current = {};
    let pass = false;

    if (ff.app === 'workflow') {
        const instancesCollection = await getCollection('instances');
        const insCount = await instancesCollection.countDocuments({
            space: spaceId,
            form: formId,
            'form_version': form['current']['id']
        });
        pass = insCount > 0;
    } else if (ff.app === 'creator') {
        const recordsCollection = await getCollection('records');
        const recordsCount = await recordsCollection.countDocuments({
            space: spaceId,
            form: formId,
            'form_version': form['current']['id']
        });
        pass = recordsCount > 0;
    }

    if (pass === true && ff["current"]["start_date"]) {
        formUpdateObj.$push = { 'historys': ff["current"] };
        current._id = _makeNewID();
        current._rev = ff["current"]["_rev"] + 1;
        current.created = now;
        current.created_by = currentUserId;

        if (ff.state === 'enabled') {
            current.start_date = now;
        }

        const flows = await flowCollection.find({ form: formId }).toArray();
        for (const flow of flows) {
            await upgradeFlowByForm(flow, current._id, {
                now: now,
                currentUserId: currentUserId,
                spaceId: spaceId
            });
        }
    } else {
        current = ff.current;
    }

    current.modified = now;
    current.modified_by = currentUserId;
    current.form = formId;
    current.fields = _formatFieldsID(form["current"]["fields"]);
    current.form_script = form["current"]["form_script"];
    current.name_forumla = form["current"]["name_forumla"];

    formUpdateObj.$set = {
        'current': current,
        'name': form["name"],
        'modified': now,
        'modified_by': currentUserId,
        'is_valid': form["is_valid"],
        'description': form["description"],
        'help_text': form["help_text"],
        'error_message': form["error_message"],
        'category': form["category"],
        'instance_style': form["instance_style"]
    };

    return formCollection.updateOne({ _id: formId }, formUpdateObj);
}

async function upgradeFlow(flowCome, userId, flowId) {
    const now = new Date();
    const flowCollection = await getCollection('flows');
    const formCollection = await getCollection('forms');
    const flow = await flowCollection.findOne({ _id: flowId });
    const spaceId = flow.space;

    // Process steps
    const clientStepIds = [];
    _.each(flowCome['current']['steps'], (step) => clientStepIds.push(step['id']));
    _.each(flowCome['current']['steps'], (step) => {
        if (step['approver_step'] && !clientStepIds.includes(step['approver_step'])) {
            step['approver_step'] = '';
        }
    });

    // Convert field types
    _.each(flowCome['current']['steps'], (st) => {
        st['posx'] = parseFloat(st['posx']);
        st['posy'] = parseFloat(st['posy']);
        if (st['timeout_hours']) {
            st['timeout_hours'] = parseFloat(st['timeout_hours']);
        }
    });

    // Convert id to _id
    _.each(flowCome['current']['steps'], (step) => {
        if (step['id']) {
            step['_id'] = step['id'];
            delete step['id'];
        }
        if (step['lines']) {
            _.each(step['lines'], (line) => {
                if (line['id']) {
                    line['_id'] = line['id'];
                    delete line['id'];
                }
            });
        }
    });

    const stepsStr = JSON.stringify(flow['current']['steps']);
    const flowComeStepsStr = JSON.stringify(flowCome['current']['steps']);
    let pass = false;
    const updateObj = { $set: {} };

    const instancesCollection = await getCollection('instances');
    const insCount = await instancesCollection.countDocuments({
        space: spaceId,
        flow: flowId,
        flow_version: flow.current._id
    });
    pass = insCount > 0;

    if (pass === true && flow.current.start_date && stepsStr === flowComeStepsStr) {
        updateObj.$push = { 'historys': flow.current };
        current = {
            '_id': _makeNewID(),
            'modified': now,
            'modified_by': userId,
            'created': now,
            'created_by': userId,
            'steps': flowCome['current']['steps'],
            'form_version': flow.current.form_version,
            '_rev': flow.current._rev,
            'flow': flowId
        };

        if (flow.state === 'enabled') {
            current['start_date'] = now;
        }

        updateObj.$set.current = current;
    } else {
        updateObj.$set = {
            'current.modified': now,
            'current.modified_by': userId,
            'current.steps': flowCome["current"]["steps"]
        };
    }

    updateObj.$set.name = flowCome['name'];
    updateObj.$set.name_formula = '';
    updateObj.$set.code_formula = '';
    updateObj.$set.is_valid = flowCome['is_valid'];
    updateObj.$set.flowtype = flowCome['flowtype'];
    updateObj.$set.help_text = flowCome['help_text'];
    updateObj.$set.decription = flowCome['descriptions'];
    updateObj.$set.error_message = flowCome['error_message'];
    updateObj.$set.modified = now;
    updateObj.$set.modified_by = userId;
    updateObj.$set.events = flowCome['events'] || '';

    const form = await formCollection.findOne({ _id: flow.form }, { projection: { category: 1 } });
    updateObj.$set.category = form['category'];

    return flowCollection.updateOne({ _id: flowId }, updateObj);
}

async function checkObjectWorkflow(spaceId, objectName, doc) {
    let _obj;
    try {
        _obj = objectql.getObject(objectName);
    } catch (error) {
        if (!_obj) {
            throw new Error("import_flows_error_not_find_object", objectName);
        }
    }

    const _objconfig = _obj.toConfig();
    if (!_objconfig.enable_workflow) {
        throw new Error("import_flows_error_not_allow_enable_workflow", _objconfig.name);
    }

    const fileds = _objconfig.fields;
    const allowValues = _.pluck(Creator.getObjectLookupFieldOptions(objectName, true, false, true), 'value');
    const objectField = _.pluck(doc.field_map, "object_field");
    const diff = _.difference(objectField, allowValues);

    if (diff.length > 0) {
        throw new Error("import_flows_error_not_find_fields", diff.join(","));
    }

    const objectFieldBack = _.pluck(doc.field_map_back, "object_field");
    const diff1 = _.difference(objectFieldBack, allowValues);

    if (diff1.length > 0) {
        throw new Error("import_flows_error_not_find_fields", diff1.join(","));
    }
}

async function objectWorkflow(spaceId, flowId, objectName, doc) {
    const objectWorkflowsCollection = await getCollection('object_workflows');
    delete doc._id;

    const oldDoc = await objectWorkflowsCollection.findOne({
        space: spaceId,
        flow_id: flowId,
        object_name: objectName
    });

    const docData = Object.assign({}, doc, {
        space: spaceId,
        flow_id: flowId,
        object_name: objectName
    });

    if (oldDoc) {
        return objectWorkflowsCollection.updateOne({ _id: oldDoc._id }, { $set: docData });
    } else {
        docData._id = _makeNewID();
        return objectWorkflowsCollection.insertOne(docData);
    }
}

// 如果没有name字段，则给name赋值code
function addFieldName(fields) {
    return _.map(fields, function (f) {
        if (!f.name && f.code) {
            f.name = f.code;
        }
        if (f.type === 'section' || f.type === 'table') {
            f.fields = addFieldName(f.fields);
        }
        return f;
    });
}

async function workflow(uid, spaceId, form, enabled, company_id, options = {}) {
    const { upgrade = false, formId: upgradeFormId, flowId: upgradeFlowId } = options;
    const new_form_ids = [];
    const new_flow_ids = [];

    if (_.isEmpty(form)) {
        throw new Error("无效的json data");
    }

    if (company_id) {
        const companyCollection = await getCollection('company');
        const companyCount = await companyCollection.countDocuments({
            _id: company_id,
            space: spaceId
        });
        if (companyCount === 0) {
            throw new Error("无效的字段: company_id");
        }
    }

    try {
        // Handle category
        if (form?.category_name) {
            const categoriesCollection = await getCollection('categories');
            const category = await categoriesCollection.findOne({
                space: spaceId,
                name: form.category_name
            }, { projection: { _id: 1 } });

            if (_.isEmpty(category)) {
                const category_id = _makeNewID();
                const new_category = {
                    _id: category_id,
                    name: form.category_name,
                    space: spaceId,
                    created: new Date(),
                    created_by: uid,
                    modified: new Date(),
                    modified_by: uid,
                    owner: uid
                };

                if (company_id) {
                    new_category.company_id = company_id;
                    new_category.company_ids = [company_id];
                }

                await categoriesCollection.insertOne(new_category);
                form.category = category_id;
            } else {
                form.category = category._id;
            }
            delete form.category_name;
        }

        // Handle instance number rules
        if (form?.instance_number_rules) {
            const instanceNumberRulesCollection = await getCollection('instance_number_rules');
            
            for (const nr of form.instance_number_rules) {
                const rules = await instanceNumberRulesCollection.findOne({
                    space: spaceId,
                    name: nr.name
                });

                if (!rules) {
                    nr.space = spaceId;
                    nr._id = _makeNewID();
                    nr.created = new Date();
                    nr.created_by = uid;
                    nr.modified = new Date();
                    nr.modified_by = uid;
                    delete nr.company_id;
                    delete nr.company_ids;

                    if (company_id) {
                        nr.company_id = company_id;
                        nr.company_ids = [company_id];
                    }

                    await instanceNumberRulesCollection.insertOne(nr);
                }
            }
            delete form.instance_number_rules;
        }

        // Handle form
        const form_id = _makeNewID();
        const flows = form.flows;
        delete form.flows;

        form._id = form_id;
        form.space = spaceId;
        form.state = enabled ? 'enabled' : 'disabled';
        form.is_valid = true;
        form.created = new Date();
        form.created_by = uid;
        form.modified = form.created;
        form.modified_by = uid;
        form.historys = [];

        const fields = addFieldName(form.current?.fields || []);
        
        form.current = {
            _id: _makeNewID(),
            _rev: 1,
            form: form_id,
            created: new Date(),
            created_by: uid,
            modified: new Date(),
            modified_by: uid,
            fields: fields,
            form_script: form.current?.form_script || '',
            name_forumla: form.current?.name_forumla || ''
        };

        delete form.company_id;
        delete form.company_ids;

        if (company_id) {
            form.company_id = company_id;
            form.company_ids = [company_id];
        }

        form.import = true;
        form.owner = uid;

        const formsCollection = await getCollection('forms');
        if (upgrade) {
            await upgradeForm(upgradeFormId, form, uid, spaceId);
        } else {
            await formsCollection.insertOne(form);
            new_form_ids.push(form_id);
        }

        // Handle flows
        const organizationsCollection = await getCollection('organizations');
        const spaceUsersCollection = await getCollection('space_users');
        const rolesCollection = await getCollection('roles');
        const flowRolesCollection = await getCollection('flow_roles');
        const flowsCollection = await getCollection('flows');

        for (const flow of flows) {
            const flowObjectWorkflows = flow.object_workflows;
            delete flow.object_workflows;

            const flow_id = _makeNewID();
            flow._id = flow_id;
            flow.form = form_id;
            flow.category = form.category;
            flow.state = enabled ? 'enabled' : 'disabled';
            flow.is_valid = true;
            flow.current_no = 0;
            flow.created = new Date();
            flow.created_by = uid;
            flow.modified = flow.created;
            flow.modified_by = uid;
            
            delete flow.company_id;
            delete flow.company_ids;

            if (company_id) {
                flow.company_id = company_id;
                flow.company_ids = [company_id];
            }

            // Handle permissions
            if (flow.perms && flow.space === spaceId) {
                const perms = {
                    _id: _makeNewID(),
                    users_can_add: [],
                    orgs_can_add: [],
                    users_can_monitor: [],
                    orgs_can_monitor: [],
                    users_can_admin: [],
                    orgs_can_admin: []
                };

                const { users_can_add, orgs_can_add, users_can_monitor, orgs_can_monitor, users_can_admin, orgs_can_admin } = perms;

                // Process users_can_add
                if (!_.isEmpty(flow.perms.users_can_add)) {
                    const users = await spaceUsersCollection.find({
                        user: { $in: flow.perms.users_can_add }
                    }, { projection: { user: 1 } }).toArray();
                    perms.users_can_add = _.pluck(users, 'user');
                }

                // Process orgs_can_add
                if (!_.isEmpty(flow.perms.orgs_can_add)) {
                    const orgs = await organizationsCollection.find({
                        _id: { $in: flow.perms.orgs_can_add }
                    }, { projection: { _id: 1 } }).toArray();
                    perms.orgs_can_add = _.pluck(orgs, '_id');

                    if (_.isEmpty(perms.orgs_can_add)) {
                        if (company_id) {
                            perms.orgs_can_add = [company_id];
                        } else {
                            const rootOrgs = await organizationsCollection.find({
                                space: spaceId,
                                parent: null
                            }, { projection: { _id: 1 } }).toArray();
                            perms.orgs_can_add = rootOrgs.map(org => org._id);
                        }
                    }
                }

                // Similar processing for other permission types...
                // (Implement similar logic for users_can_monitor, orgs_can_monitor, etc.)

                flow.perms = perms;
            } else if (!flow.perms || flow.space !== spaceId) {
                let orgs_can_add = [];
                if (company_id) {
                    orgs_can_add = [company_id];
                } else {
                    const rootOrgs = await organizationsCollection.find({
                        space: spaceId,
                        parent: null
                    }, { projection: { _id: 1 } }).toArray();
                    orgs_can_add = rootOrgs.map(org => org._id);
                }

                flow.perms = {
                    _id: _makeNewID(),
                    users_can_add: [],
                    orgs_can_add: orgs_can_add,
                    users_can_monitor: [],
                    orgs_can_monitor: [],
                    users_can_admin: [],
                    orgs_can_admin: []
                };
            }

            flow.space = spaceId;
            flow.current = {
                _id: _makeNewID(),
                flow: flow_id,
                _rev: 1,
                form_version: form.current._id,
                created: new Date(),
                created_by: uid,
                modified: new Date(),
                modified_by: uid,
                steps: flow.current?.steps || []
            };

            // Process steps
            if (flow.current?.steps) {
                for (const step of flow.current.steps) {
                    // Process approver_users
                    if (_.isArray(step.approver_users)) {
                        const _accepted_approve_users = [];
                        for (const uid of step.approver_users) {
                            const user = await spaceUsersCollection.findOne({
                                user: uid,
                                user_accepted: true,
                                space: spaceId
                            });
                            if (user) {
                                _accepted_approve_users.push(uid);
                            }
                        }
                        step.approver_users = _accepted_approve_users;
                    }

                    // Process approver_orgs
                    if (_.isArray(step.approver_orgs)) {
                        const _accepted_approver_orgs = [];
                        for (const oid of step.approver_orgs) {
                            const org = await organizationsCollection.findOne({
                                _id: oid,
                                space: spaceId
                            });
                            if (org) {
                                _accepted_approver_orgs.push(oid);
                            }
                        }
                        step.approver_orgs = _accepted_approver_orgs;
                    }

                    // Process approver_roles
                    if (_.isEmpty(step.approver_roles_name)) {
                        delete step.approver_roles_name;
                        if (_.isEmpty(step.approver_roles)) {
                            step.approver_roles = [];
                        }

                        if (!_.isEmpty(step.approver_hr_roles_name)) {
                            const approver_hr_roles = [];
                            for (const [index, role_name] of step.approver_hr_roles_name.entries()) {
                                const role_query = { space: spaceId, name: role_name };
                                let role = await rolesCollection.findOne(role_query, { projection: { _id: 1 } });

                                if (!role) {
                                    const apiName = step.approver_hr_roles_api_name?.[index] || "";
                                    const role_id = _makeNewID();
                                    role = {
                                        _id: role_id,
                                        name: role_name,
                                        api_name: apiName,
                                        space: spaceId,
                                        created: new Date(),
                                        created_by: uid,
                                        modified: new Date(),
                                        modified_by: uid,
                                        owner: uid
                                    };
                                    await rolesCollection.insertOne(role);
                                    approver_hr_roles.push(role_id);
                                } else {
                                    approver_hr_roles.push(role._id);
                                }
                            }
                            step.approver_hr_roles = approver_hr_roles;
                            delete step.approver_roles_name;
                        }
                    } else {
                        const approve_roles = [];
                        let approveRolesByIds = [];

                        if (_.isArray(step.approver_roles) && !_.isEmpty(step.approver_roles)) {
                            approveRolesByIds = await flowRolesCollection.find({
                                _id: { $in: step.approver_roles },
                                space: spaceId
                            }, { projection: { _id: 1, name: 1, company_id: 1 } }).toArray();
                        }

                        for (const [index, role_name] of step.approver_roles_name.entries()) {
                            const roleApiName = step.approver_roles_api_name?.[index] || "";
                            const approveRoleById = _.find(approveRolesByIds, (_role) => _role._id === step.approver_roles[index]);

                            const flow_role_query = { space: spaceId, name: role_name };
                            if ((!approveRoleById && company_id) || (approveRoleById?.company_id && company_id)) {
                                flow_role_query.company_id = company_id;
                            } else {
                                flow_role_query.$or = [
                                    { company_id: { $exists: false } },
                                    { company_id: null },
                                    { company_id: '' }
                                ];
                            }

                            let role = await flowRolesCollection.findOne(flow_role_query, { projection: { _id: 1 } });

                            if (!role) {
                                const role_id = _makeNewID();
                                role = {
                                    _id: role_id,
                                    name: role_name,
                                    api_name: roleApiName,
                                    space: spaceId,
                                    created: new Date(),
                                    created_by: uid,
                                    modified: new Date(),
                                    modified_by: uid,
                                    owner: uid
                                };

                                if (company_id) {
                                    role.company_id = company_id;
                                    role.company_ids = [company_id];
                                }

                                await flowRolesCollection.insertOne(role);
                                approve_roles.push(role_id);
                            } else {
                                approve_roles.push(role._id);
                            }
                        }
                        step.approver_roles = approve_roles;
                        delete step.approver_roles_name;
                    }
                }
            }

            flow.import = true;
            flow.owner = uid;

            if (upgrade) {
                await upgradeFlow(flow, uid, upgradeFlowId);
                for (const _objectWorkflow of flowObjectWorkflows) {
                    await objectWorkflow(spaceId, upgradeFlowId, _objectWorkflow.object_name, _objectWorkflow);
                }
            } else {
                await flowsCollection.insertOne(flow);
                new_flow_ids.push(flow_id);
                for (const _objectWorkflow of flowObjectWorkflows) {
                    await objectWorkflow(spaceId, flow_id, _objectWorkflow.object_name, _objectWorkflow);
                }
            }
        }

        return new_flow_ids;
    } catch (error) {
        // Clean up in case of error
        const formsCollection = await getCollection('forms');
        const flowsCollection = await getCollection('flows');

        for (const id of new_form_ids) {
            await formsCollection.deleteOne({ _id: id });
        }

        for (const id of new_flow_ids) {
            await flowsCollection.deleteOne({ _id: id });
        }

        throw error;
    }
}

steedosImport.objectWorkflow = objectWorkflow;
steedosImport.workflow = workflow;

module.exports = steedosImport;
