"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
const actions = require("../../automations/actions");
const triggers = require("../../automations/triggers");
const { getAutomationParams, generateAutomationID, DocumentTypes, } = require("../../db/utils");
const { checkForWebhooks, updateTestHistory, removeDeprecated, } = require("../../automations/utils");
const { deleteEntityMetadata } = require("../../utilities");
const { MetadataTypes } = require("../../constants");
const { setTestFlag, clearTestFlag } = require("../../utilities/redis");
const { getAppDB, getProdAppDB, doInAppContext, } = require("@devlego/backend-core/context");
const { events } = require("@devlego/backend-core");
const { app } = require("@devlego/backend-core/cache");
const { automations } = require("@devlego/pro");
const ACTION_DEFS = removeDeprecated(actions.ACTION_DEFINITIONS);
const TRIGGER_DEFS = removeDeprecated(triggers.TRIGGER_DEFINITIONS);
/*************************
 *                       *
 *   BUILDER FUNCTIONS   *
 *                       *
 *************************/
function cleanupAutomationMetadata(automationId) {
    return __awaiter(this, void 0, void 0, function* () {
        yield deleteEntityMetadata(MetadataTypes.AUTOMATION_TEST_INPUT, automationId);
        yield deleteEntityMetadata(MetadataTypes.AUTOMATION_TEST_HISTORY, automationId);
    });
}
function cleanAutomationInputs(automation) {
    if (automation == null) {
        return automation;
    }
    let steps = automation.definition.steps;
    let trigger = automation.definition.trigger;
    let allSteps = [...steps, trigger];
    // live is not a property used anymore
    if (automation.live != null) {
        delete automation.live;
    }
    for (let step of allSteps) {
        if (step == null) {
            continue;
        }
        for (let inputName of Object.keys(step.inputs)) {
            if (!step.inputs[inputName] || step.inputs[inputName] === "") {
                delete step.inputs[inputName];
            }
        }
    }
    return automation;
}
exports.create = function (ctx) {
    return __awaiter(this, void 0, void 0, function* () {
        const db = getAppDB();
        let automation = ctx.request.body;
        automation.appId = ctx.appId;
        // call through to update if already exists
        if (automation._id && automation._rev) {
            return exports.update(ctx);
        }
        automation._id = generateAutomationID();
        automation.type = "automation";
        automation = cleanAutomationInputs(automation);
        automation = yield checkForWebhooks({
            newAuto: automation,
        });
        const response = yield db.put(automation);
        yield events.automation.created(automation);
        for (let step of automation.definition.steps) {
            yield events.automation.stepCreated(automation, step);
        }
        automation._rev = response.rev;
        ctx.status = 200;
        ctx.body = {
            message: "Automation created successfully",
            automation: Object.assign(Object.assign({}, automation), response),
        };
    });
};
const getNewSteps = (oldAutomation, automation) => {
    const oldStepIds = oldAutomation.definition.steps.map(s => s.id);
    return automation.definition.steps.filter(s => !oldStepIds.includes(s.id));
};
const getDeletedSteps = (oldAutomation, automation) => {
    const stepIds = automation.definition.steps.map(s => s.id);
    return oldAutomation.definition.steps.filter(s => !stepIds.includes(s.id));
};
const handleStepEvents = (oldAutomation, automation) => __awaiter(void 0, void 0, void 0, function* () {
    // new steps
    const newSteps = getNewSteps(oldAutomation, automation);
    for (let step of newSteps) {
        yield events.automation.stepCreated(automation, step);
    }
    // old steps
    const deletedSteps = getDeletedSteps(oldAutomation, automation);
    for (let step of deletedSteps) {
        yield events.automation.stepDeleted(automation, step);
    }
});
exports.update = function (ctx) {
    return __awaiter(this, void 0, void 0, function* () {
        const db = getAppDB();
        let automation = ctx.request.body;
        automation.appId = ctx.appId;
        const oldAutomation = yield db.get(automation._id);
        automation = cleanAutomationInputs(automation);
        automation = yield checkForWebhooks({
            oldAuto: oldAutomation,
            newAuto: automation,
        });
        const response = yield db.put(automation);
        automation._rev = response.rev;
        const oldAutoTrigger = oldAutomation && oldAutomation.definition.trigger
            ? oldAutomation.definition.trigger
            : undefined;
        const newAutoTrigger = automation && automation.definition.trigger
            ? automation.definition.trigger
            : {};
        // trigger has been updated, remove the test inputs
        if (oldAutoTrigger && oldAutoTrigger.id !== newAutoTrigger.id) {
            yield events.automation.triggerUpdated(automation);
            yield deleteEntityMetadata(ctx.appId, MetadataTypes.AUTOMATION_TEST_INPUT, automation._id);
        }
        yield handleStepEvents(oldAutomation, automation);
        ctx.status = 200;
        ctx.body = {
            message: `Automation ${automation._id} updated successfully.`,
            automation: Object.assign(Object.assign({}, automation), { _rev: response.rev, _id: response.id }),
        };
    });
};
exports.fetch = function (ctx) {
    return __awaiter(this, void 0, void 0, function* () {
        const db = getAppDB();
        const response = yield db.allDocs(getAutomationParams(null, {
            include_docs: true,
        }));
        ctx.body = response.rows.map(row => row.doc);
    });
};
exports.find = function (ctx) {
    return __awaiter(this, void 0, void 0, function* () {
        const db = getAppDB();
        ctx.body = yield db.get(ctx.params.id);
    });
};
exports.destroy = function (ctx) {
    return __awaiter(this, void 0, void 0, function* () {
        const db = getAppDB();
        const automationId = ctx.params.id;
        const oldAutomation = yield db.get(automationId);
        yield checkForWebhooks({
            oldAuto: oldAutomation,
        });
        // delete metadata first
        yield cleanupAutomationMetadata(automationId);
        ctx.body = yield db.remove(automationId, ctx.params.rev);
        yield events.automation.deleted(oldAutomation);
    });
};
exports.logSearch = function (ctx) {
    return __awaiter(this, void 0, void 0, function* () {
        ctx.body = yield automations.logs.logSearch(ctx.request.body);
    });
};
exports.clearLogError = function (ctx) {
    return __awaiter(this, void 0, void 0, function* () {
        const { automationId, appId } = ctx.request.body;
        yield doInAppContext(appId, () => __awaiter(this, void 0, void 0, function* () {
            const db = getProdAppDB();
            const metadata = yield db.get(DocumentTypes.APP_METADATA);
            if (!automationId) {
                delete metadata.automationErrors;
            }
            else if (metadata.automationErrors &&
                metadata.automationErrors[automationId]) {
                delete metadata.automationErrors[automationId];
            }
            yield db.put(metadata);
            yield app.invalidateAppMetadata(metadata.appId, metadata);
            ctx.body = { message: `Error logs cleared.` };
        }));
    });
};
exports.getActionList = function (ctx) {
    return __awaiter(this, void 0, void 0, function* () {
        ctx.body = ACTION_DEFS;
    });
};
exports.getTriggerList = function (ctx) {
    return __awaiter(this, void 0, void 0, function* () {
        ctx.body = TRIGGER_DEFS;
    });
};
module.exports.getDefinitionList = function (ctx) {
    return __awaiter(this, void 0, void 0, function* () {
        ctx.body = {
            trigger: TRIGGER_DEFS,
            action: ACTION_DEFS,
        };
    });
};
/*********************
 *                   *
 *   API FUNCTIONS   *
 *                   *
 *********************/
exports.trigger = function (ctx) {
    return __awaiter(this, void 0, void 0, function* () {
        const db = getAppDB();
        let automation = yield db.get(ctx.params.id);
        yield triggers.externalTrigger(automation, Object.assign(Object.assign({}, ctx.request.body), { appId: ctx.appId }));
        ctx.body = {
            message: `Automation ${automation._id} has been triggered.`,
            automation,
        };
    });
};
function prepareTestInput(input) {
    // prepare the test parameters
    if (input.id && input.row) {
        input.row._id = input.id;
    }
    if (input.revision && input.row) {
        input.row._rev = input.revision;
    }
    return input;
}
exports.test = function (ctx) {
    return __awaiter(this, void 0, void 0, function* () {
        const db = getAppDB();
        let automation = yield db.get(ctx.params.id);
        yield setTestFlag(automation._id);
        const testInput = prepareTestInput(ctx.request.body);
        const response = yield triggers.externalTrigger(automation, Object.assign(Object.assign({}, testInput), { appId: ctx.appId }), { getResponses: true });
        // save a test history run
        yield updateTestHistory(ctx.appId, automation, Object.assign(Object.assign({}, ctx.request.body), { occurredAt: new Date().getTime() }));
        yield clearTestFlag(automation._id);
        ctx.body = response;
        yield events.automation.tested(automation);
    });
};
