"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Actions = exports.Action = void 0;
const util_1 = require("../util");
function callMaybeFn(maybeFn, options) {
    if (maybeFn instanceof Function) {
        return maybeFn(options);
    }
    else {
        return Promise.resolve(maybeFn);
    }
}
function mapMaybeFn(maybeFn, map) {
    if (maybeFn instanceof Function) {
        return async (...args) => map(await maybeFn(...args));
    }
    else {
        return map(maybeFn);
    }
}
class Action {
    constructor(id, metadataFn, inputSpec, getInputFn, runFn) {
        this.id = id;
        this.metadataFn = metadataFn;
        this.inputSpec = inputSpec;
        this.getInputFn = getInputFn;
        this.runFn = runFn;
        this._INPUT = null;
        this.prevInputSpec = {};
    }
    static withInput(id, metadata, inputSpec, getInput, run) {
        return new Action(id, mapMaybeFn(metadata, (m) => ({ ...m, hasInput: true })), inputSpec, getInput, run);
    }
    static withoutInput(id, metadata, run) {
        return new Action(id, mapMaybeFn(metadata, (m) => ({ ...m, hasInput: false })), null, async () => null, run);
    }
    async exportMetadata(options) {
        const childEffects = options.effects.child(`setupActions/${this.id}`);
        childEffects.constRetry = (0, util_1.once)(() => {
            this.exportMetadata(options);
        });
        const metadata = await callMaybeFn(this.metadataFn, {
            effects: childEffects,
        });
        await options.effects.action.export({ id: this.id, metadata });
        return metadata;
    }
    async getInput(options) {
        let spec = {};
        if (this.inputSpec) {
            const inputSpec = await callMaybeFn(this.inputSpec, options);
            const built = await inputSpec?.build(options);
            if (built) {
                this.prevInputSpec[options.effects.eventId] = built;
                spec = built.spec;
            }
        }
        return {
            eventId: options.effects.eventId,
            spec,
            value: (await this.getInputFn(options)) || null,
        };
    }
    async run(options) {
        let spec = {};
        if (this.inputSpec) {
            const prevInputSpec = this.prevInputSpec[options.effects.eventId];
            if (!prevInputSpec) {
                throw new Error(`getActionInput has not been called for EventID ${options.effects.eventId}`);
            }
            options.input = prevInputSpec.validator.parse(options.input);
            spec = prevInputSpec.spec;
        }
        return ((await this.runFn({
            effects: options.effects,
            input: options.input,
            spec,
        })) ?? null);
    }
}
exports.Action = Action;
class Actions {
    constructor(actions) {
        this.actions = actions;
    }
    static of() {
        return new Actions({});
    }
    addAction(action) {
        return new Actions({ ...this.actions, [action.id]: action });
    }
    async init(effects) {
        for (let action of Object.values(this.actions)) {
            const fn = async () => {
                let res = () => { };
                const complete = new Promise((resolve) => {
                    res = resolve;
                });
                const e = effects.child(action.id);
                e.constRetry = (0, util_1.once)(() => complete.then(() => fn()).catch(console.error));
                try {
                    await action.exportMetadata({ effects: e });
                }
                finally {
                    res();
                }
            };
            await fn();
        }
        await effects.action.clear({ except: Object.keys(this.actions) });
    }
    get(actionId) {
        return this.actions[actionId];
    }
}
exports.Actions = Actions;
//# sourceMappingURL=setupActions.js.map