"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ContainerModuleManager = void 0;
const core_1 = require("@inversifyjs/core");
const BindingFluentSyntaxImplementation_1 = require("../../binding/models/BindingFluentSyntaxImplementation");
const InversifyContainerError_1 = require("../../error/models/InversifyContainerError");
const InversifyContainerErrorKind_1 = require("../../error/models/InversifyContainerErrorKind");
class ContainerModuleManager {
    #bindingManager;
    #deactivationParams;
    #defaultScope;
    #planResultCacheManager;
    #serviceReferenceManager;
    constructor(bindingManager, deactivationParams, defaultScope, planResultCacheManager, serviceReferenceManager) {
        this.#bindingManager = bindingManager;
        this.#deactivationParams = deactivationParams;
        this.#defaultScope = defaultScope;
        this.#planResultCacheManager = planResultCacheManager;
        this.#serviceReferenceManager = serviceReferenceManager;
    }
    async load(...modules) {
        // eslint-disable-next-line @typescript-eslint/await-thenable
        await Promise.all(this.#load(...modules));
    }
    loadSync(...modules) {
        const results = this.#load(...modules);
        for (const result of results) {
            if (result !== undefined) {
                throw new InversifyContainerError_1.InversifyContainerError(InversifyContainerErrorKind_1.InversifyContainerErrorKind.invalidOperation, 'Unexpected asynchronous module load. Consider using Container.load() instead.');
            }
        }
    }
    async unload(...modules) {
        // eslint-disable-next-line @typescript-eslint/await-thenable
        await Promise.all(this.#unload(...modules));
        /*
         * Removing module related objects here so unload is deterministic.
         *
         * Removing modules as soon as resolveModuleDeactivations takes effect leads to
         * module deactivations not triggering previously deleted deactivations,
         * introducing non determinism depending in the order in which modules are
         * deactivated.
         */
        this.#clearAfterUnloadModules(modules);
    }
    unloadSync(...modules) {
        const results = this.#unload(...modules);
        for (const result of results) {
            if (result !== undefined) {
                throw new InversifyContainerError_1.InversifyContainerError(InversifyContainerErrorKind_1.InversifyContainerErrorKind.invalidOperation, 'Unexpected asynchronous module unload. Consider using Container.unload() instead.');
            }
        }
        /*
         * Removing module related objects here so unload is deterministic.
         *
         * Removing modules as soon as resolveModuleDeactivations takes effect leads to
         * module deactivations not triggering previously deleted deactivations,
         * introducing non determinism depending in the order in which modules are
         * deactivated.
         */
        this.#clearAfterUnloadModules(modules);
    }
    #buildContainerModuleLoadOptions(moduleId) {
        return {
            bind: (serviceIdentifier) => {
                return new BindingFluentSyntaxImplementation_1.BindToFluentSyntaxImplementation((binding) => {
                    this.#setBinding(binding);
                }, moduleId, this.#defaultScope, serviceIdentifier);
            },
            isBound: this.#bindingManager.isBound.bind(this.#bindingManager),
            onActivation: (serviceIdentifier, activation) => {
                this.#serviceReferenceManager.activationService.add(activation, {
                    moduleId,
                    serviceId: serviceIdentifier,
                });
            },
            onDeactivation: (serviceIdentifier, deactivation) => {
                this.#serviceReferenceManager.deactivationService.add(deactivation, {
                    moduleId,
                    serviceId: serviceIdentifier,
                });
            },
            rebind: this.#bindingManager.rebind.bind(this.#bindingManager),
            rebindSync: this.#bindingManager.rebindSync.bind(this.#bindingManager),
            unbind: this.#bindingManager.unbind.bind(this.#bindingManager),
            unbindSync: this.#bindingManager.unbindSync.bind(this.#bindingManager),
        };
    }
    #clearAfterUnloadModules(modules) {
        for (const module of modules) {
            this.#serviceReferenceManager.activationService.removeAllByModuleId(module.id);
            this.#serviceReferenceManager.bindingService.removeAllByModuleId(module.id);
            this.#serviceReferenceManager.deactivationService.removeAllByModuleId(module.id);
        }
        this.#serviceReferenceManager.planResultCacheService.clearCache();
    }
    #load(...modules) {
        return modules.map((module) => module.load(this.#buildContainerModuleLoadOptions(module.id)));
    }
    #setBinding(binding) {
        this.#serviceReferenceManager.bindingService.set(binding);
        this.#planResultCacheManager.invalidateService({
            binding: binding,
            kind: core_1.CacheBindingInvalidationKind.bindingAdded,
        });
    }
    #unload(...modules) {
        return modules.map((module) => (0, core_1.resolveModuleDeactivations)(this.#deactivationParams, module.id));
    }
}
exports.ContainerModuleManager = ContainerModuleManager;
//# sourceMappingURL=ContainerModuleManager.js.map