/*
 * @Author: sunhaolin@hotoa.com
 * @Date: 2022-12-28 10:36:06
 * @LastEditors: 孙浩林 sunhaolin@steedos.com
 * @LastEditTime: 2024-04-16 13:14:20
 * @Description: 
 */
'use strict';
// @ts-check
const _ = require('lodash')
const { getObject } = require('@steedos/objectql')

const DISABLE_DIRECT = process.env.STEEDOS_INSTANCE_TASKS_DISABLE_DIRECT == true || process.env.STEEDOS_INSTANCE_TASKS_DISABLE_DIRECT == 'true';

async function _insert(taskDoc) {
    return await getObject('instance_tasks').insert(taskDoc);
}

async function _directInsert(taskDoc) {
    return await getObject('instance_tasks').directInsert(taskDoc);
}

async function _update(_id, taskDoc) {
    return await getObject('instance_tasks').update(_id, taskDoc);
}

async function _remove(_id) {
    return await getObject('instance_tasks').delete(_id);
}

async function _directRemove(_id) {
    return await getObject('instance_tasks').directDelete(_id);
}

async function _find(query) {
    return await getObject('instance_tasks').find(query);
}

async function _count(query) {
    return await getObject('instance_tasks').count(query);
}

/**
 * 更新相关通知为已读
 * @param {String} instance 申请单ID
 * @param {String} handler 处理人ID
 */
async function _updateNotificationsAsRead(instance, handler) {
    const notificationsObj = getObject('notifications')
    await notificationsObj.directUpdateMany([
        ['owner', '=', handler],
        ['is_read', '!=', true],
        ['related_to.o', '=', 'instances'],
        ['related_to.ids', '=', instance]
    ], {
        is_read: true
    })
}

/**
 * 更新相关通知为已删除
 * @param {String} instance 申请单ID
 * @param {String} handler 处理人ID
 */
async function _updateNotificationsAsDeleted(instance, handler) {
    const notificationsObj = getObject('notifications')
    await notificationsObj.directUpdateMany([
        ['owner', '=', handler],
        ['related_to.o', '=', 'instances'],
        ['related_to.ids', '=', instance]
    ], {
        is_deleted: true
    })
}

/**
 * 新增instance_tasks记录
 * @param {String} insId 申请单ID
 * @param {String} traceId TraceID
 * @param {String} approveId ApproveID
 * @returns 新增的instance_tasks
 */
async function insert_instance_tasks(insId, traceId, approveId) {
    const taskDoc = await _makeTaskDoc(insId, traceId, approveId)
    if (DISABLE_DIRECT) {
        return await _insert(taskDoc)
    }
    return await _directInsert(taskDoc)
}

/**
 * 批量新增instance_tasks记录
 * @param {String} insId 申请单ID
 * @param {String} traceId TraceID
 * @param {String[]} approveIds ApproveID集合
 * @returns 新增的instance_tasks的ID集合
 */
async function insert_many_instance_tasks(insId, traceId, approveIds) {
    const insDoc = await _getInsDoc(insId)
    const traceDoc = await _getTraceDoc(insDoc, traceId)
    const newDocs = []
    for (const aId of approveIds) {
        const approveDoc = await _getApproveDoc(traceDoc, aId)
        const taskDoc = await _generateTaskDoc(insDoc, traceDoc, approveDoc)
        const newTaskDoc = await _insert(taskDoc)
        newDocs.push(newTaskDoc)
    }
    return newDocs
}

/**
 * 更新instance_tasks记录
 * @param {String} insId 申请单ID
 * @param {String} traceId TraceID
 * @param {String} approveId ApproveID
 * @param {Object} doc TaskeDoc
 * @returns 更新后的instance_tasks
 */
async function update_instance_tasks(insId, traceId, approveId, doc) {
    console.time(`update_instance_tasks ${approveId}`);
    let taskDoc = doc
    if (!taskDoc) {
        console.time(`update_instance_tasks_makeTaskDoc ${approveId}`);
        taskDoc = await _makeTaskDoc(insId, traceId, approveId)
        delete taskDoc._id
        console.timeEnd(`update_instance_tasks_makeTaskDoc ${approveId}`);
    }
    console.time(`update_instance_tasks_update ${approveId}`);
    const result = await _update(approveId, taskDoc)
    console.timeEnd(`update_instance_tasks_update ${approveId}`);
    
    // 如果任务已完成，更新相关通知为已读
    if (taskDoc.is_finished) {
        console.time(`update_instance_tasks_notifications ${approveId}`);
        const instance = taskDoc.instance || insId
        const handler = taskDoc.handler
        if (instance && handler) {
            await _updateNotificationsAsRead(instance, handler)
        }
        console.timeEnd(`update_instance_tasks_notifications ${approveId}`);
    }
    
    console.timeEnd(`update_instance_tasks ${approveId}`);
    return result
}

/**
 * 批量更新instance_tasks记录
 * @param {String} insId 
 * @param {String} traceId 
 * @param {String[]} approveIds 
 * @param {String[]} keys 
 * @returns 更新后的instance_tasks
 */
async function update_many_instance_tasks(insId, traceId, approveIds, keys) {
    const insDoc = await _getInsDoc(insId)
    const traceDoc = await _getTraceDoc(insDoc, traceId)
    const results = []
    for (const aId of approveIds) {
        const approveDoc = await _getApproveDoc(traceDoc, aId)
        let taskDoc = await _generateTaskDoc(insDoc, traceDoc, approveDoc)
        delete taskDoc._id
        if (!_.isEmpty(keys)) {
            taskDoc = _.pick(taskDoc, keys)
        }
        const result = await _update(aId, taskDoc)
        results.push(result)
        
        // 如果任务已完成，更新相关通知为已读
        if (taskDoc.is_finished) {
            const instance = taskDoc.instance || insId
            const handler = taskDoc.handler
            if (instance && handler) {
                await _updateNotificationsAsRead(instance, handler)
            }
        }
    }
    return results
}

/**
 * 删除instance_tasks记录
 * @param {String} approveId 
 * @returns 1
 */
async function remove_instance_tasks(approveId) {
    // 删除前先获取任务信息用于更新通知
    const taskDoc = await getObject('instance_tasks').findOne(approveId)
    const result = await _remove(approveId)
    
    // 删除任务后，更新相关通知为已删除
    if (taskDoc) {
        const { instance, handler } = taskDoc
        if (instance && handler) {
            await _updateNotificationsAsDeleted(instance, handler)
        }
    }
    
    return result
}

/**
 * 删除instance_tasks记录
 * @param {String[]} approveIds
 * @returns 1
 */
async function remove_many_instance_tasks(approveIds) {
    const results = []
    for (const aId of approveIds) {
        // 删除前先获取任务信息用于更新通知
        const taskDoc = await getObject('instance_tasks').findOne(aId)
        const r = await _remove(aId)
        results.push(r)
        
        // 删除任务后，更新相关通知为已删除
        if (taskDoc) {
            const { instance, handler } = taskDoc
            if (instance && handler) {
                await _updateNotificationsAsDeleted(instance, handler)
            }
        }
    }
    return results
}

/**
 * 删除instance相关的所有instance_tasks记录
 * @param {String} insId 申请单ID 
 * @returns 1
 */
async function remove_instance_tasks_by_instance_id(insId) {
    const results = []
    const taskDocs = await _find({
        filters: [
            ['instance', '=', insId]
        ],
        fields: ['_id', 'instance', 'handler']
    })
    for (const t of taskDocs) {
        const r = await _remove(t._id)
        results.push(r)
        
        // 删除任务后，更新相关通知为已删除
        if (t.instance && t.handler) {
            await _updateNotificationsAsDeleted(t.instance, t.handler)
        }
    }
    return results
}

/**
 * 删除instance_tasks记录，不触发钩子
 * @param {String[]} approveIds
 * @returns 1
 */
async function direct_remove_many_instance_tasks(approveIds) {
    const results = []
    for (const aId of approveIds) {
        if (DISABLE_DIRECT) {
            const r = await _remove(aId)
            results.push(r)
        } else {
            const r = await _directRemove(aId)
            results.push(r)
        }
    }
    return results
}

/**
 * 计数instance_tasks
 * @param {object} query
 * @returns 1
 */
async function count_instance_tasks(query) {
    if(process.env.STEEDOS_WORKFLOW_DISABLE_BADGE === 'true'){
        return 0;
    } 
    const result = await _count(query)
    return result
}

/**
 * 使用insId、traceId、approveId整理成instnce_tasks结构
 * @param {String} insId 申请单ID
 * @param {String} traceId TraceID
 * @param {String} approveId ApproveID
 * @returns instance_tasks文档
 */
async function _makeTaskDoc(insId, traceId, approveId) {
    const insDoc = await _getInsDoc(insId)
    const traceDoc = await _getTraceDoc(insDoc, traceId)
    const approveDoc = await _getApproveDoc(traceDoc, approveId)
    return await _generateTaskDoc(insDoc, traceDoc, approveDoc)
}

/**
 * 使用instance、trace、approve整理成instnce_tasks结构
 * @param {Object} insDoc 申请单文档
 * @param {Object} traceDoc Trace文档
 * @param {Object} approveDoc Approve文档
 * @returns instance_tasks文档
 */
function _generateTaskDoc(insDoc, traceDoc, approveDoc) {
    return {
        ...approveDoc,
        'space': insDoc.space,
        'instance_name': insDoc.name,
        'submitter': insDoc.submitter,
        'submitter_name': insDoc.submitter_name,
        'applicant': insDoc.applicant,
        'applicant_name': insDoc.applicant_name,
        'applicant_organization_name': insDoc.applicant_organization_name,
        'submit_date': insDoc.submit_date,
        'flow': insDoc.flow,
        'flow_name': insDoc.flow_name,
        'form': insDoc.form,
        'step': traceDoc.step,
        'step_name': traceDoc.name,
        'category_name': insDoc.category_name,
        'instance_state': insDoc.state,
        'distribute_from_instance': insDoc.distribute_from_instance,
        'forward_from_instance': insDoc.forward_from_instance,
        'keywords': insDoc.keywords,
        'is_archived': insDoc.is_archived,
        'category': insDoc.category,
        'extras': insDoc.extras,
    }
}

/**
 * 查找申请单文档
 * @param {String} insId 申请单ID
 * @returns 申请单文档
 */
async function _getInsDoc(insId) {
    return await getObject('instances').findOne(insId)
}

/**
 * 查找Trace
 * @param {Object} insDoc 申请单文档
 * @param {String} traceId TraceID
 * @returns Trace文档
 */
function _getTraceDoc(insDoc, traceId) {
    return _.find(insDoc.traces, function (t) {
        return t._id === traceId
    })
}

/**
 * 查找Approve
 * @param {Object} traceDoc Trace文档
 * @param {String} approveId ApproveID
 * @returns Approve文档
 */
function _getApproveDoc(traceDoc, approveId) {
    return _.find(traceDoc.approves, function (a) {
        return a._id === approveId
    })
}

module.exports = {
    insert_instance_tasks,
    insert_many_instance_tasks,
    update_instance_tasks,
    update_many_instance_tasks,
    remove_instance_tasks,
    remove_many_instance_tasks,
    remove_instance_tasks_by_instance_id,
    direct_remove_many_instance_tasks,
    count_instance_tasks
}