"use strict";
/*
 * @poppinss/hooks
 *
 * (c) Harminder Virk <virk@adonisjs.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.Hooks = void 0;
/**
 * Exposes the API to register before/after lifecycle hooks for a given action
 * with option to resolve handlers from the IoC container.
 *
 * The hooks class doesn't provide autocomplete for actions and the arguments
 * the handler will receive, since we expect this class to be used internally
 * for user facing objects.
 */
class Hooks {
    constructor(resolver) {
        this.resolver = resolver;
        this.hooks = {
            before: new Map(),
            after: new Map(),
        };
    }
    /**
     * Raise exceptins when resolver is not defined
     */
    ensureResolver() {
        if (!this.resolver) {
            throw new Error('IoC container resolver is required to register string based hooks handlers');
        }
    }
    /**
     * Resolves the hook handler using the resolver when it is defined as string
     * or returns the function reference back
     */
    resolveHandler(handler) {
        if (typeof handler === 'string') {
            this.ensureResolver();
            return this.resolver.resolve(handler);
        }
        return handler;
    }
    /**
     * Returns handlers set for a given action or undefined
     */
    getActionHandlers(lifecycle, action) {
        return this.hooks[lifecycle].get(action);
    }
    /**
     * Adds the resolved handler to the actions set
     */
    addResolvedHandler(lifecycle, action, handler) {
        const handlers = this.getActionHandlers(lifecycle, action);
        if (handlers) {
            handlers.add(handler);
        }
        else {
            this.hooks[lifecycle].set(action, new Set([handler]));
        }
    }
    /**
     * Returns a boolean whether a handler has been already registered or not
     */
    has(lifecycle, action, handler) {
        const handlers = this.getActionHandlers(lifecycle, action);
        if (!handlers) {
            return false;
        }
        return handlers.has(this.resolveHandler(handler));
    }
    /**
     * Register hook handler for a given event and lifecycle
     */
    add(lifecycle, action, handler) {
        this.addResolvedHandler(lifecycle, action, this.resolveHandler(handler));
        return this;
    }
    /**
     * Remove a pre-registered handler
     */
    remove(lifecycle, action, handler) {
        const handlers = this.getActionHandlers(lifecycle, action);
        if (!handlers) {
            return;
        }
        handlers.delete(this.resolveHandler(handler));
    }
    /**
     * Remove all handlers for a given action or lifecycle. If action is not
     * defined, then all actions for that given lifecycle are removed
     */
    clear(lifecycle, action) {
        if (!action) {
            this.hooks[lifecycle].clear();
            return;
        }
        this.hooks[lifecycle].delete(action);
    }
    /**
     * Merges hooks of a given hook instance. To merge from more than
     * one instance, you can call the merge method for multiple times
     */
    merge(hooks) {
        hooks.hooks.before.forEach((actionHooks, action) => {
            actionHooks.forEach((handler) => {
                this.addResolvedHandler('before', action, handler);
            });
        });
        hooks.hooks.after.forEach((actionHooks, action) => {
            actionHooks.forEach((handler) => {
                this.addResolvedHandler('after', action, handler);
            });
        });
    }
    /**
     * Executes the hook handler for a given action and lifecycle
     */
    async exec(lifecycle, action, ...data) {
        const handlers = this.getActionHandlers(lifecycle, action);
        if (!handlers) {
            return;
        }
        for (let handler of handlers) {
            if (typeof handler === 'function') {
                await handler(...data);
            }
            else {
                await this.resolver.call(handler, undefined, data);
            }
        }
    }
}
exports.Hooks = Hooks;
