"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ActionDispatcher = void 0;
const optional_1 = require("@jet/environment/types/optional");
class ActionDispatcher {
    constructor(metricsPipeline) {
        this.implementations = {};
        this.metricsPipeline = metricsPipeline;
    }
    register(type, implementation) {
        if (type in this.implementations) {
            console.error(`An implementation is already registered for ${type}`);
        }
        this.implementations[type] = implementation;
    }
    async perform(action, metricsBehavior) {
        if (!(action.$kind in this.implementations)) {
            // 1. If there is no implementation registered for the type of action
            //    we were passed, we check for a chained dispatcher to forward to.
            //    If one is found, we forward this call without doing any work.
            //    If none is found, we give up.
            if (optional_1.isSome(this.next)) {
                return await this.next.perform(action, metricsBehavior);
            }
            else {
                return "unsupported";
            }
        }
        // 2. We have an implementation for the action we were given.
        //    We are responsible for processing metrics for that action.
        this.processMetrics(action, metricsBehavior);
        if (optional_1.isSome(this.next)) {
            // 3a. If we have another dispatcher we are chained to, we forward to it
            //     if the implementation we have for the given action decides it cannot
            //     support performing it.
            const outcome = await this.implementations[action.$kind](action);
            if (outcome === "unsupported") {
                return await this.next.perform(action, { behavior: "notProcessed" });
            }
            else {
                return outcome;
            }
        }
        else {
            // 3b. We hand off control to the implementation we have for the given action type.
            //     If the implementation cannot perform the action, we give up.
            return await this.implementations[action.$kind](action);
        }
    }
    processMetrics(action, metricsBehavior) {
        if (metricsBehavior.behavior === "notProcessed") {
            return;
        }
        const actionMetrics = action.actionMetrics;
        const context = {
            customMetrics: actionMetrics.custom,
            pageFields: metricsBehavior.context.pageFields,
        };
        action.actionMetrics.data.forEach((data) => {
            void this.metricsPipeline.process(data, context);
        });
    }
}
exports.ActionDispatcher = ActionDispatcher;
