"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.IntentDispatcher = void 0;
const optional_1 = require("../../types/optional");
const intent_controller_1 = require("./intent-controller");
const net_1 = require("../../types/globals/net");
const types_1 = require("../../types/globals/types");
/**
 * A dispatcher is responsible for taking an intent and invoking the controller
 * registered to handle the intent's kind.
 *
 * Adopters can create a project specific dispatcher that composes `IntentDispatcher`
 * to add additional functionality.
 *
 * @example
 * ```
 * const intent: SomeIntent = {
 *  $kind: "SomeIntent",
 *  field: "some value"
 * };
 * const dispatcher = new IntentDispatcher();
 * const promise = dispatcher.dispatch(intent)
 * ```
 */
class IntentDispatcher {
    constructor() {
        this.dispatchableMap = {};
    }
    /**
     * Registers a controller to handle the intent specified in the `$intentKind` property.
     * Only one controller can be registered per intent. If register is called multiple times
     * with controllers for the same intent kind, the dispatcher will use the last registered
     * controller.
     * @param intentController - The controller to register.
     */
    register(dispatchable) {
        if ((0, optional_1.isNothing)(dispatchable.$intentKind)) {
            throw new Error(`Dispatcher cannot register a controller without an $intentKind`);
        }
        if (dispatchable.$intentKind in this.dispatchableMap) {
            throw new Error(`Dispatcher already has a controller registered for ${dispatchable.$intentKind}`);
        }
        this.dispatchableMap[dispatchable.$intentKind] = dispatchable;
    }
    /**
     * Performs an intent using the controller registered to handle the provided intent.
     * Returns a rejected promise if no controller is registered to handle the intent.
     * @param intent - The intent to perform.
     * @param objectGraph - An object graph for dependency injection.
     */
    async dispatch(intent, objectGraph) {
        if (intent.$kind === "$static") {
            // MAINTAINER'S NOTE: We specially handle static intents here to cover two cases:
            //                      1) static intents created and dispatched wholly inside an app's
            //                         JavaScript business layer;
            //                      2) static intents created from JavaScript running on an older
            //                         version of native JetEngine that does not support static intents
            //                         where the intent is boxed in an opaque intent in the platform layer.
            //                    Static intents are normally not dispatched across an app's layers.
            const data = Reflect.get(intent, "$data");
            if ((0, optional_1.isNothing)(data)) {
                throw new Error("StaticIntent<R> contains no data");
            }
            return data;
        }
        const controller = this.dispatchableMap[intent.$kind];
        if ((0, optional_1.isNothing)(controller) || !(0, intent_controller_1.isIntentController)(controller)) {
            throw new Error(`No controller registered to handle ${intent.$kind}`);
        }
        // Replace the `net` dependency with a proxy which includes instrumentation.
        const pageIntentInstrumentation = intent.$pageIntentInstrumentation;
        const network = objectGraph.optional(types_1.net);
        if ((0, optional_1.isSome)(pageIntentInstrumentation) && (0, optional_1.isSome)(network)) {
            const proxiedNetwork = new net_1.ProxiedNetwork(network, pageIntentInstrumentation);
            const modifiedObjectGraph = objectGraph.adding(types_1.net, proxiedNetwork);
            return await controller.perform(intent, modifiedObjectGraph);
        }
        else {
            return await controller.perform(intent, objectGraph);
        }
    }
    /**
     * Returns the controller registered for the provided intent.
     * @param intent - An intent to find the controller for.
     */
    controller(intent) {
        return this.dispatchableMap[intent.$kind];
    }
    /**
     * An array of all registered controllers.
     */
    get registeredControllers() {
        return Object.values(this.dispatchableMap);
    }
}
exports.IntentDispatcher = IntentDispatcher;
//# sourceMappingURL=dispatcher.js.map