const _eval = require('eval');
const objectql = require('@steedos/objectql');
const { getCollection, _makeNewID } = require('../utils/collection');
const PermissionManager = require('./permission_manager');

const getObjectConfig = (objectApiName) => {
    return objectql.getObject(objectApiName).toConfig();
};

const getObjectNameFieldKey = (objectApiName) => {
    return objectql.getObject(objectApiName).NAME_FIELD_KEY;
};

const getRelateds = async (objectApiName) => {
    return await objectql.getObject(objectApiName).getRelateds();
};

const objectFindOne = async (objectApiName, query) => {
    query.top = 1;
    const resolve = await objectql.getObject(objectApiName).find(query);
    if (resolve && resolve.length > 0) {
        return resolve[0];
    } else {
        return null;
    }
};

const objectFind = async (objectApiName, query) => {
    return await objectql.getObject(objectApiName).find(query);
};

const objectUpdate = async (objectApiName, id, data) => {
    return await objectql.getObject(objectApiName).update(id, data);
};

const getRelatedObjectFieldCode = (relatedObjectsKeys, key) => {
    return _.find(relatedObjectsKeys, (relatedObjectsKey) => {
        return key.startsWith(relatedObjectsKey + '.');
    });
};

const getFormTableFieldCode = (formTableFieldsCode, key) => {
    return _.find(formTableFieldsCode, (formTableFieldCode) => {
        return key.startsWith(formTableFieldCode + '.');
    });
};

const getFormTableField = (formTableFields, key) => {
    return _.find(formTableFields, (f) => {
        return f.code == key;
    });
};

const getFormField = (formFields, key) => {
    let ff = null;
    _.forEach(formFields, (f) => {
        if (ff) {
            return;
        }
        if (f.type == 'section') {
            ff = _.find(f.fields, (sf) => {
                return sf.code == key;
            });
        } else if (f.code == key) {
            ff = f;
        }
    });
    return ff;
};

const getFormTableSubField = (tableField, subFieldCode) => {
    return _.find(tableField.fields, (f) => {
        return f.code == subFieldCode;
    });
};

const getFieldOdataValue = async (objName, id, referenceToFieldName) => {
    const obj = objectql.getObject(objName);
    const nameKey = getObjectNameFieldKey(objName);
    if (!obj) {
        return;
    }
    if (_.isString(id)) {
        const _record = await objectFindOne(objName, { filters: [[referenceToFieldName, '=', id]] });
        if (_record) {
            _record['@label'] = _record[nameKey];
            return _record;
        }
    } else if (_.isArray(id)) {
        const _records = [];
        const records = await objectFind(objName, { filters: [[referenceToFieldName, 'in', id]] });
        for (const _record of records) {
            _record['@label'] = _record[nameKey];
            _records.push(_record);
        }
        if (!_.isEmpty(_records)) {
            return _records;
        }
    }
    return;
};

const getSelectUserValue = (userId, spaceId) => {
    const su = Creator.getCollection('space_users').findOne({ space: spaceId, user: userId });
    su.id = userId;
    return su;
};

const getSelectUserValues = (userIds, spaceId) => {
    const sus = [];
    if (_.isArray(userIds)) {
        _.each(userIds, (userId) => {
            const su = getSelectUserValue(userId, spaceId);
            if (su) {
                sus.push(su);
            }
        });
    }
    return sus;
};

const getSelectOrgValue = (orgId, spaceId) => {
    const org = Creator.getCollection('organizations').findOne(orgId, { fields: { _id: 1, name: 1, fullname: 1 } });
    org.id = orgId;
    return org;
};

const getSelectOrgValues = (orgIds, spaceId) => {
    const orgs = [];
    if (_.isArray(orgIds)) {
        _.each(orgIds, (orgId) => {
            const org = getSelectOrgValue(orgId, spaceId);
            if (org) {
                orgs.push(org);
            }
        });
    }
    return orgs;
};

const getFileFieldValue = (recordFieldId, fType) => {
    if (_.isEmpty(recordFieldId)) {
        return;
    }
    let collection;
    if (fType == 'image') {
        collection = 'images';
    } else if (fType == 'file') {
        collection = 'files';
    }
    let query;
    if (_.isString(recordFieldId)) {
        query = { _id: { $in: [recordFieldId] } };
    } else {
        query = { _id: { $in: recordFieldId } };
    }
    const files = Creator.Collections[`cfs.${collection}.filerecord`].find(query);
    const value = [];
    files.forEach((f) => {
        const newFile = new FS.File();
        newFile.attachData(f.createReadStream('files'), {
            type: f.original.type
        }, (err) => {
            if (err) {
                throw new Error(err.error, err.reason);
            }
            newFile.name(f.name());
            newFile.size(f.size());
            const metadata = {
                owner: f.metadata.owner
            };
            newFile.metadata = metadata;
            newFile._id = _makeNewID();
            cfs[collection].insert(newFile);
            value.push(newFile._id);
        });
    });
    if (value.length > 0) {
        if (_.isString(recordFieldId)) {
            return value[0];
        } else {
            return value;
        }
    }
};

const getInstanceFieldValue = (objField, formField, record, object_field, spaceId, recordFieldValue, enableAmisform) => {
    if (enableAmisform && formField.steedos_field) {
        return recordFieldValue;
    }
    recordFieldValue = record[objField.name];
    let value;
    if (formField && objField && formField.type == 'odata' && ['lookup', 'master_detail'].includes(objField.type) && _.isString(objField.reference_to)) {
        const referenceToFieldName = objField.reference_to_field || '_id';
        const referenceToObjectName = objField.reference_to;
        let odataFieldValue;
        if (objField.multiple && formField.is_multiselect) {
            odataFieldValue = getFieldOdataValue(referenceToObjectName, recordFieldValue, referenceToFieldName);
        } else if (!objField.multiple && !formField.is_multiselect) {
            odataFieldValue = getFieldOdataValue(referenceToObjectName, recordFieldValue, referenceToFieldName);
        }
        value = odataFieldValue;
    } else if (formField && objField && ['user', 'group'].includes(formField.type) && ['lookup', 'master_detail'].includes(objField.type) && (['users', 'organizations'].includes(objField.reference_to) || ('space_users' == objField.reference_to && 'user' == objField.reference_to_field))) {
        if (!_.isEmpty(recordFieldValue)) {
            let selectFieldValue;
            if (formField.type == 'user') {
                if (objField.multiple && formField.is_multiselect) {
                    selectFieldValue = getSelectUserValues(recordFieldValue, spaceId);
                } else if (!objField.multiple && !formField.is_multiselect) {
                    selectFieldValue = getSelectUserValue(recordFieldValue, spaceId);
                }
            } else if (formField.type == 'group') {
                if (objField.multiple && formField.is_multiselect) {
                    selectFieldValue = getSelectOrgValues(recordFieldValue, spaceId);
                } else if (!objField.multiple && !formField.is_multiselect) {
                    selectFieldValue = getSelectOrgValue(recordFieldValue, spaceId);
                }
            }
            if (selectFieldValue) {
                value = selectFieldValue;
            }
        }
    } else if (formField && objField && formField.type == 'date' && recordFieldValue) {
        value = uuflowManagerForInitApproval.formatDate(recordFieldValue);
    } else if (formField && objField && formField.type == 'time' && recordFieldValue) {
        value = uuflowManagerForInitApproval.formatTime(recordFieldValue);
    } else if (formField && objField && recordFieldValue && (formField.type == 'image' || formField.type == 'file')) {
        value = getFileFieldValue(recordFieldValue, formField.type);
    } else if (formField && objField && recordFieldValue && formField.type == 'lookup' && ['lookup', 'master_detail'].includes(objField.type) && _.isString(objField.reference_to)) {
        value = recordFieldValue;
    } else if (formField && objField && recordFieldValue && (formField.type == 'multiSelect')) {
        value = recordFieldValue.join(',');
    } else if (record.hasOwnProperty(object_field)) {
        value = recordFieldValue;
    }
    return value;
};

const checkRequiredDetails = (requiredDetails, masterRecord) => {
    if (!_.isEmpty(requiredDetails)) {
        requiredDetails.forEach((rd) => {
            const subTableName = rd.split('.')[0];
            const subTableMasterField = rd.split('.')[1];
            const subObjConfig = getObjectConfig(subTableName);
            const masterField = subObjConfig.fields[subTableMasterField];
            const refId = masterField.reference_to_field || '_id';
            const masterFieldValue = masterRecord[refId];
            const subTableRecord = objectFindOne(subTableName, { filters: [[subTableMasterField, '=', masterFieldValue]], fields: [subTableMasterField] });
            if (!subTableRecord) {
                throw new Error(`请先新增${subObjConfig.label}`);
            }
        });
    }
    return;
};

const uuflowManagerForInitApproval = {};

uuflowManagerForInitApproval.check_authorization = (req) => {
    const query = req.query;
    const userId = query["X-User-Id"];
    const authToken = query["X-Auth-Token"];

    if (!userId || !authToken) {
        throw new Error(401, 'Unauthorized');
    }

    const hashedToken = Accounts._hashLoginToken(authToken);
    const user = Meteor.users.findOne({
        _id: userId,
        "services.resume.loginTokens.hashedToken": hashedToken
    });

    if (!user) {
        throw new Error(401, 'Unauthorized');
    }

    return user;
};

uuflowManagerForInitApproval.getSpace = async (space_id) => {
    const coll = await getCollection('spaces');
    const space = await coll.findOne({_id: space_id});
    if (!space) {
        throw new Error('error!', "space_id有误或此space已经被删除");
    }
    return space;
};

uuflowManagerForInitApproval.getFlow = async (flow_id) => {
    const coll = await getCollection('flows');
    const flow = coll.findOne({_id: flow_id});
    if (!flow) {
        throw new Error('error!', "id有误或此流程已经被删除");
    }
    return flow;
};

uuflowManagerForInitApproval.getSpaceUser = async (space_id, user_id) => {
    const coll = await getCollection('space_users');
    const space_user = coll.findOne({ space: space_id, user: user_id });
    if (!space_user) {
        throw new Error('error!', "user_id对应的用户不属于当前space");
    }
    return space_user;
};

uuflowManagerForInitApproval.getSpaceUserOrgInfo = async (space_user) => {
    const coll = await getCollection('organizations');
    const info = new Object();
    info.organization = space_user.organization;
    const org = coll.findOne({_id: space_user.organization}, { fields: { name: 1, fullname: 1 } });
    info.organization_name = org.name;
    info.organization_fullname = org.fullname;
    return info;
};

uuflowManagerForInitApproval.isFlowEnabled = (flow) => {
    if (flow.state !== "enabled") {
        throw new Error('error!', "流程未启用,操作失败");
    }
};

uuflowManagerForInitApproval.isFlowSpaceMatched = (flow, space_id) => {
    if (flow.space !== space_id) {
        throw new Error('error!', "流程和工作区ID不匹配");
    }
};

uuflowManagerForInitApproval.getForm = async (form_id) => {
    const coll = await getCollection('forms');
    const form = coll.findOne({_id: form_id});
    if (!form) {
        throw new Error('error!', '表单ID有误或此表单已经被删除');
    }
    return form;
};

uuflowManagerForInitApproval.getCategory = async (category_id) => {
    const coll = await getCollection('categories');
    return coll.findOne({_id: category_id});
};

uuflowManagerForInitApproval.checkSyncDirection = async (object_name, flow_id) => {
    const coll = await getCollection('object_workflows')
    const ow = await coll.findOne({
        object_name: object_name,
        flow_id: flow_id
    });
    if (!ow) {
        throw new Error('error!', '未找到对象流程映射记录。');
    }
    const syncDirection = ow.sync_direction || 'both';
    if (!['both', 'obj_to_ins'].includes(syncDirection)) {
        throw new Error('error!', '不支持的同步方向。');
    }
};

uuflowManagerForInitApproval.create_instance = async (instance_from_client, user_info) => {

    const insColl = await getCollection('instances');

    await uuflowManagerForInitApproval.checkSyncDirection(instance_from_client["record_ids"][0].o, instance_from_client["flow"]);

    await uuflowManagerForInitApproval.checkIsInApproval(instance_from_client["record_ids"][0], instance_from_client["space"]);

    const space_id = instance_from_client["space"];
    const flow_id = instance_from_client["flow"];
    const user_id = user_info._id;
    let trace_from_client = null;
    let approve_from_client = null;
    if (instance_from_client["traces"] && instance_from_client["traces"][0]) {
        trace_from_client = instance_from_client["traces"][0];
        if (trace_from_client["approves"] && trace_from_client["approves"][0]) {
            approve_from_client = instance_from_client["traces"][0]["approves"][0];
        }
    }

    const space = await uuflowManagerForInitApproval.getSpace(space_id);
    const flow = await uuflowManagerForInitApproval.getFlow(flow_id);
    const space_user = await uuflowManagerForInitApproval.getSpaceUser(space_id, user_id);
    const space_user_org_info = await uuflowManagerForInitApproval.getSpaceUserOrgInfo(space_user);
    uuflowManagerForInitApproval.isFlowEnabled(flow);
    uuflowManagerForInitApproval.isFlowSpaceMatched(flow, space_id);

    const form = await uuflowManagerForInitApproval.getForm(flow.form);

    const permissions = await PermissionManager.getFlowPermissions(flow_id, user_id);

    if (!permissions.includes("add")) {
        throw new Error('error!', "当前用户没有此流程的新建权限");
    }

    const now = new Date();
    const ins_obj = {};
    ins_obj._id = await _makeNewID();
    ins_obj.space = space_id;
    ins_obj.flow = flow_id;
    ins_obj.flow_version = flow.current._id;
    ins_obj.form = flow.form;
    ins_obj.form_version = flow.current.form_version;
    ins_obj.name = flow.name;
    ins_obj.submitter = user_id;
    ins_obj.submitter_name = user_info.name;
    ins_obj.applicant = instance_from_client["applicant"] ? instance_from_client["applicant"] : user_id;
    ins_obj.applicant_name = instance_from_client["applicant_name"] ? instance_from_client["applicant_name"] : user_info.name;
    ins_obj.applicant_organization = instance_from_client["applicant_organization"] ? instance_from_client["applicant_organization"] : space_user.organization;
    ins_obj.applicant_organization_name = instance_from_client["applicant_organization_name"] ? instance_from_client["applicant_organization_name"] : space_user_org_info.organization_name;
    ins_obj.applicant_organization_fullname = instance_from_client["applicant_organization_fullname"] ? instance_from_client["applicant_organization_fullname"] : space_user_org_info.organization_fullname;
    ins_obj.applicant_company = instance_from_client["applicant_company"] ? instance_from_client["applicant_company"] : space_user.company_id;
    ins_obj.state = 'draft';
    ins_obj.code = '';
    ins_obj.is_archived = false;
    ins_obj.is_deleted = false;
    ins_obj.created = now;
    ins_obj.created_by = user_id;
    ins_obj.modified = now;
    ins_obj.modified_by = user_id;

    ins_obj.record_ids = instance_from_client["record_ids"];

    if (space_user.company_id) {
        ins_obj.company_id = space_user.company_id;
    }

    const trace_obj = {};
    trace_obj._id = _makeNewID();
    trace_obj.instance = ins_obj._id;
    trace_obj.is_finished = false;
    const start_step = _.find(flow.current.steps, (step) => {
        return step.step_type === 'start';
    });
    trace_obj.step = start_step._id;
    trace_obj.name = start_step.name;

    trace_obj.start_date = now;
    const appr_obj = {};
    appr_obj._id = _makeNewID();
    appr_obj.instance = ins_obj._id;
    appr_obj.trace = trace_obj._id;
    appr_obj.is_finished = false;
    appr_obj.user = instance_from_client["applicant"] ? instance_from_client["applicant"] : user_id;
    appr_obj.user_name = instance_from_client["applicant_name"] ? instance_from_client["applicant_name"] : user_info.name;
    appr_obj.handler = user_id;
    appr_obj.handler_name = user_info.name;
    appr_obj.handler_organization = space_user.organization;
    appr_obj.handler_organization_name = space_user_org_info.name;
    appr_obj.handler_organization_fullname = space_user_org_info.fullname;
    appr_obj.type = 'draft';
    appr_obj.start_date = now;
    appr_obj.read_date = now;
    appr_obj.is_read = true;
    appr_obj.is_error = false;
    appr_obj.description = '';
    const relatedTablesInfo = {};
    appr_obj.values = await uuflowManagerForInitApproval.initiateValues(ins_obj.record_ids[0], flow_id, space_id, form.current.fields, relatedTablesInfo);

    trace_obj.approves = [appr_obj];
    ins_obj.traces = [trace_obj];

    ins_obj.values = appr_obj.values;

    ins_obj.inbox_users = instance_from_client.inbox_users || [];

    ins_obj.current_step_name = start_step.name;

    if (flow.auto_remind === true) {
        ins_obj.auto_remind = true;
    }

    if (form.category) {
        const category = await uuflowManagerForInitApproval.getCategory(form.category);
        if (category) {
            ins_obj.category_name = category.name;
            ins_obj.category = category._id;
        }
    }

    const new_ins_id = ins_obj._id;

    await insColl.insert(ins_obj);

    await uuflowManagerForInitApproval.initiateRecordInstanceInfo(ins_obj.record_ids[0], new_ins_id, space_id);

    //TODO 处理附件
    // await uuflowManagerForInitApproval.initiateAttach(ins_obj.record_ids[0], space_id, ins_obj._id, appr_obj._id);

    return new_ins_id;
};

uuflowManagerForInitApproval.initiateValues = async (recordIds, flowId, spaceId, fields, relatedTablesInfo) => {
    const owColl = await getCollection('object_workflows');
    const flowColl = await getCollection('flows');
    const formColl = await getCollection('forms')
    const fieldCodes = [];
    _.each(fields, (f) => {
        if (f.type == 'section') {
            _.each(f.fields, (ff) => {
                fieldCodes.push(ff.code);
            });
        } else {
            fieldCodes.push(f.code);
        }
    });

    const values = {};
    const objectName = recordIds.o;
    const object = getObjectConfig(objectName);
    const recordId = recordIds.ids[0];
    const ow = await owColl.findOne({
        object_name: objectName,
        flow_id: flowId
    });
    const record = await objectFindOne(objectName, { filters: [['_id', '=', recordId]] });
    const flow = await flowColl.findOne({_id: flowId}, { fields: { form: 1, enableAmisform: 1 } });
    const enableAmisform = flow.enable_amisform;
    if (ow && record) {
        const requiredDetails = ow.required_details || [];
        checkRequiredDetails(requiredDetails, record);

        const form = await formColl.findOne({_id: flow.form});
        const formFields = form.current.fields || [];
        const relatedObjects = await getRelateds(objectName);
        const relatedObjectsKeys = _.pluck(relatedObjects, 'object_name');
        const formTableFields = _.filter(formFields, (formField) => {
            return formField.type == 'table';
        });
        const formTableFieldsCode = _.pluck(formTableFields, 'code');

        const tableFieldCodes = [];
        const tableFieldMap = [];
        const tableToRelatedMap = {};

        ow.field_map?.forEach((fm) => {
            const object_field = fm.object_field;
            const workflow_field = fm.workflow_field;
            if (!object_field || !workflow_field) {
                throw new Error(400, '未找到字段，请检查对象流程映射字段配置');
            }
            const relatedObjectFieldCode = getRelatedObjectFieldCode(relatedObjectsKeys, object_field);
            const formTableFieldCode = getFormTableFieldCode(formTableFieldsCode, workflow_field);
            const objField = object.fields[object_field];
            const formField = getFormField(formFields, workflow_field);
            const recordFieldValue = record[object_field];
            if (relatedObjectFieldCode) {
                const oTableCode = object_field.split('.')[0];
                const oTableFieldCode = object_field.split('.')[1];
                const tableToRelatedMapKey = oTableCode;
                if (!tableToRelatedMap[tableToRelatedMapKey]) {
                    tableToRelatedMap[tableToRelatedMapKey] = {};
                }

                if (formTableFieldCode) {
                    const wTableCode = workflow_field.split('.')[0];
                    tableToRelatedMap[tableToRelatedMapKey]['_FROM_TABLE_CODE'] = wTableCode;
                }

                tableToRelatedMap[tableToRelatedMapKey][oTableFieldCode] = workflow_field;
            } else if (workflow_field.indexOf('.') > 0 && object_field.indexOf('.$.') > 0) {
                const wTableCode = workflow_field.split('.')[0];
                const oTableCode = object_field.split('.$.')[0];
                if (record.hasOwnProperty(oTableCode) && _.isArray(record[oTableCode])) {
                    tableFieldCodes.push(JSON.stringify({
                        workflow_table_field_code: wTableCode,
                        object_table_field_code: oTableCode
                    }));
                    tableFieldMap.push(fm);
                } else if (oTableCode.indexOf('.') > 0) {
                    const oTableCodeReferenceFieldCode = oTableCode.split('.')[0];
                    const gridCode = oTableCode.split('.')[1];
                    const oTableCodeReferenceField = object.fields[oTableCodeReferenceFieldCode];
                    if (oTableCodeReferenceField && ['lookup', 'master_detail'].includes(oTableCodeReferenceField.type) && _.isString(oTableCodeReferenceField.reference_to)) {
                        if (record[oTableCode]) {
                            return;
                        }
                        const referenceToFieldName = oTableCodeReferenceField.reference_to_field || '_id';
                        const referenceToObjectName = oTableCodeReferenceField.reference_to;
                        const referenceToFieldValue = record[oTableCodeReferenceField.name];
                        const referenceToDoc = getFieldOdataValue(referenceToObjectName, referenceToFieldValue, referenceToFieldName);
                        if (referenceToDoc[gridCode]) {
                            record[oTableCode] = referenceToDoc[gridCode];
                            tableFieldCodes.push(JSON.stringify({
                                workflow_table_field_code: wTableCode,
                                object_table_field_code: oTableCode
                            }));
                            tableFieldMap.push(fm);
                        }
                    }
                }
            } else if (object_field.indexOf('.') > 0 && object_field.indexOf('.$.') == -1) {
                const objectFieldName = object_field.split('.')[0];
                const lookupFieldName = object_field.split('.')[1];
                if (object) {
                    const objectField = object.fields[objectFieldName];
                    if (objectField && formField && ['lookup', 'master_detail'].includes(objectField.type) && _.isString(objectField.reference_to)) {
                        const lookupObjectRecord = objectFindOne(objectField.reference_to, { filters: [['_id', '=', record[objectFieldName]]], fields: [lookupFieldName] });
                        if (!lookupObjectRecord) {
                            return;
                        }
                        const objectFieldObjectName = objectField.reference_to;
                        const lookupFieldObj = getObjectConfig(objectFieldObjectName);
                        const objectLookupField = lookupFieldObj.fields[lookupFieldName];

                        values[workflow_field] = getInstanceFieldValue(objectLookupField, formField, lookupObjectRecord, lookupFieldName, spaceId, record[lookupFieldName], enableAmisform);
                    }
                }
            } else {
                values[workflow_field] = getInstanceFieldValue(objField, formField, record, object_field, spaceId, record[object_field], enableAmisform);
            }
        });

        _.uniq(tableFieldCodes).forEach((tfc) => {
            const c = JSON.parse(tfc);
            values[c.workflow_table_field_code] = [];
            record[c.object_table_field_code].forEach((tr) => {
                const newTr = {};
                _.each(tr, (tdValue, k) => {
                    tableFieldMap.forEach((tfm) => {
                        if (tfm.object_field === (c.object_table_field_code + '.$.' + k)) {
                            const wTdCode = tfm.workflow_field.split('.')[1];
                            newTr[wTdCode] = tdValue;
                        }
                    });
                });
                if (!_.isEmpty(newTr)) {
                    values[c.workflow_table_field_code].push(newTr);
                }
            });
        });

        for (const [key, map] of Object.entries(tableToRelatedMap)) {
            const tableCode = map._FROM_TABLE_CODE;
            const formTableField = getFormTableField(formTableFields, tableCode);
            if (!tableCode) {
                console.warn('tableToRelated: [' + key + '] missing corresponding table.');
            } else {
                const relatedObjectName = key;
                const tableValues = [];
                const relatedTableItems = [];
                const relatedObject = getObjectConfig(relatedObjectName);
                const relatedField = _.find(relatedObject.fields, (f) => {
                    return ['lookup', 'master_detail'].includes(f.type) && f.reference_to == objectName;
                });

                const relatedFieldName = relatedField.name;

                const relatedRecords = await objectFind(relatedObjectName, {
                    filters: [
                        [relatedFieldName, '=', recordId]
                    ]
                });

                for (const relatedRecord of relatedRecords) {
                    const tableValueItem = {};
                    for (const [fieldKey, valueKey] of Object.entries(map)) {
                        if (fieldKey !== '_FROM_TABLE_CODE') {
                            let tableFieldValue;
                            let formFieldKey;
                            if (valueKey.startsWith(tableCode + '.')) {
                                formFieldKey = (valueKey.split(".")[1]);
                            } else {
                                formFieldKey = valueKey;
                            }

                            const formField = getFormTableSubField(formTableField, formFieldKey);
                            const relatedObjectField = relatedObject.fields[fieldKey];
                            if (!formField || !relatedObjectField) {
                                continue;
                            }
                            tableFieldValue = getInstanceFieldValue(relatedObjectField, formField, relatedRecord, fieldKey, spaceId, relatedRecord[fieldKey], enableAmisform);
                            tableValueItem[formFieldKey] = tableFieldValue;
                        }
                    }
                    if (!_.isEmpty(tableValueItem)) {
                        tableValueItem._id = relatedRecord._id;
                        tableValues.push(tableValueItem);
                        relatedTableItems.push({ _table: { _id: relatedRecord._id, _code: tableCode } });
                    }
                }
                values[tableCode] = tableValues;
                relatedTablesInfo[relatedObjectName] = relatedTableItems;
            }
        }

        if (ow.field_map_script) {
            _.extend(values, uuflowManagerForInitApproval.evalFieldMapScript(ow.field_map_script, objectName, spaceId, recordId));
        }
    }

    const filterValues = {};
    _.each(_.keys(values), (k) => {
        if (fieldCodes.includes(k)) {
            filterValues[k] = values[k];
        }
    });

    return filterValues;
};

uuflowManagerForInitApproval.evalFieldMapScript = (field_map_script, objectName, spaceId, objectId) => {
    const record = objectFindOne(objectName, { filters: [['_id', '=', objectId]] });
    const script = "module.exports = function (record) { " + field_map_script + " }";
    const func = _eval(script, "field_map_script");
    const values = func(record);
    if (_.isObject(values)) {
        return values;
    } else {
        console.error("evalFieldMapScript: 脚本返回值类型不是对象");
    }
    return {};
};

uuflowManagerForInitApproval.initiateAttach = (recordIds, spaceId, insId, approveId) => {
    Creator.Collections['cms_files'].find({
        space: spaceId,
        parent: recordIds
    }).forEach((cf) => {
        _.each(cf.versions, (versionId, idx) => {
            const f = Creator.Collections['cfs.files.filerecord'].findOne(versionId);
            const newFile = new FS.File();
            newFile.attachData(f.createReadStream('files'), {
                type: f.original.type
            }, (err) => {
                if (err) {
                    throw new Error(err.error, err.reason);
                }
                newFile.name(f.name());
                newFile.size(f.size());
                const metadata = {
                    owner: f.metadata.owner,
                    owner_name: f.metadata.owner_name,
                    space: spaceId,
                    instance: insId,
                    approve: approveId,
                    parent: cf._id
                };
                if (idx === 0) {
                    metadata.current = true;
                }
                newFile.metadata = metadata;
                cfs.instances.insert(newFile);
            });
        });
    });
    return;
};

uuflowManagerForInitApproval.initiateRecordInstanceInfo = async (recordIds, insId, spaceId) => {
    await objectUpdate(recordIds.o, recordIds.ids[0], {
        instances: [{
            _id: insId,
            state: 'draft'
        }],
        locked: true,
        instance_state: 'draft'
    });
    return;
};

uuflowManagerForInitApproval.initiateRelatedRecordInstanceInfo = (relatedTablesInfo, insId, spaceId) => {
    _.each(relatedTablesInfo, (tableItems, relatedObjectName) => {
        const relatedCollection = Creator.getCollection(relatedObjectName, spaceId);
        _.each(tableItems, (item) => {
            relatedCollection.direct.update(item._table._id, {
                $set: {
                    instances: [{
                        _id: insId,
                        state: 'draft'
                    }],
                    _table: item._table
                }
            });
        });
    });
    return;
};

uuflowManagerForInitApproval.checkIsInApproval = async (recordIds, spaceId) => {
    const record = await objectFindOne(recordIds.o, { filters: [['_id', '=', recordIds.ids[0]]], fields: ['instances'] });
    if (record && record.instances && record.instances[0].state !== 'completed' && Creator.Collections.instances.find(record.instances[0]._id).count() > 0) {
        throw new Error('error!', "此记录已发起流程正在审批中，待审批结束方可发起下一次审批！");
    }
    return;
};

uuflowManagerForInitApproval.formatDate = (date) => {
    return moment(date).format("YYYY-MM-DD");
};

uuflowManagerForInitApproval.formatTime = (date) => {
    return moment(date).utcOffset(0).format("1970-01-01THH:mm:00.000[Z]");
};
 
module.exports = uuflowManagerForInitApproval;