const SharedConstants = require('../../shared/constants/SharedConstants');
const { promiseTimeout, checkExitSignal } = require('../utils/UtilTimeout');
const { setActiveProcess } = require('../utils/UtilProcess');

function createScheduleDocument(nextRunDate, rulesetDoc, userId) {
    var scheduleDoc = { systemHeader: {} };

    scheduleDoc.scheduleActive = true;
    scheduleDoc.nextRunDate = nextRunDate;
    scheduleDoc.systemHeader.systemType = SharedConstants.SYSTEM_TYPE_SCHEDULE;

    scheduleDoc.rulesetRel = [
        {
            documentId: rulesetDoc.documentId,
            name: rulesetDoc.systemHeader.summaryName
        }
    ];

    return new Promise((resolve, reject) => {
        DataService.insert(scheduleDoc, userId).then((result) => {
            resolve(result);
        }).catch((error) => {
            reject(error);
        });
    });
}

const deactivateScheduleDocument = (scheduleDoc, userId) => {
    scheduleDoc.scheduleActive = false;
    return DataService.update(scheduleDoc.documentId, scheduleDoc, userId).then((updated) => {
        logger.info("Deactivated schedule document: " + scheduleDoc.documentId);
        return updated;
    });
};

async function processScheduleDocument(scheduleDoc) {
    const ServerRuleUtils = require('./ServerRuleUtils');
    const processRuleFlow = ServerRuleUtils.processRuleFlow;

    if (scheduleDoc.rulesetRel
        && scheduleDoc.rulesetRel.length > 0
        && scheduleDoc.systemHeader
        && scheduleDoc.systemHeader.createdBy) {
        setActiveProcess('scheduleDocuments', true);

        var rulesetId = scheduleDoc.rulesetRel[0].documentId;
        var userId = scheduleDoc.systemHeader.createdBy;

        try {
            const user = await RuleDataService.getUser(userId);
            const rulesetDocument = await DataService.findOne(rulesetId, userId);

            let executionTimeoutInSeconds = config.get("systemActionRulesetExecutionTimeoutSeconds");
            if (!executionTimeoutInSeconds) {
                executionTimeoutInSeconds = 3000;
            }

            const executionTimeout = executionTimeoutInSeconds * 1000;

            scheduleDoc.lastRunDate = scheduleDoc.nextRunDate;

            const process = new Promise((resolve, reject) => {
                var ruleSetDSL = rulesetDocument.ruleSetDSL;

                if (ruleSetDSL && ruleSetDSL.length) {
                    var documentContext = {};
                    documentContext.user = user;

                    processRuleFlow(
                        scheduleDoc,
                        documentContext,
                        ruleSetDSL,
                        ServerRuleUtils,
                        (result) => {
                            resolve(result);
                        });
                } else {
                    var error = "Invalid ruleSetDSL.";
                    logger.error(error);
                    reject(new Error(error));
                }
            });

            await promiseTimeout(executionTimeout, process);
        } catch (error) {
            scheduleDoc.statusDescription = error.message;
            var error = "Error in processScheduleDocument: " + statusDescription;
            logger.error(error);
        } finally {
            setActiveProcess('scheduleDocuments', false);

            const updated = await DataService.update(scheduleDoc.documentId, scheduleDoc, userId);
            await checkExitSignal();

            return updated;
        }
    } else {
        var error = "Shedule document " + scheduleDoc.documentId + " should have rules attached! ";
        logger.error(error);
        throw new Error(error);
    }
}

function getCurrentScheduledDocuments() {
    return RuleDataService.getCurrentScheduledDocuments();
}

async function processScheduleDocuments() {
    const scheduleDocuments = await RuleDataService.getCurrentScheduledDocuments();
    for (const scheduleDocument of scheduleDocuments) {
        await processScheduleDocument(scheduleDocument);
    }

    return scheduleDocuments;
}

module.exports = {
    createScheduleDocument,
    processScheduleDocument,
    getCurrentScheduledDocuments,
    processScheduleDocuments,
    deactivateScheduleDocument
};