"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ServiceResolutionManager = void 0;
const common_1 = require("@inversifyjs/common");
const core_1 = require("@inversifyjs/core");
const InversifyContainerError_1 = require("../../error/models/InversifyContainerError");
const InversifyContainerErrorKind_1 = require("../../error/models/InversifyContainerErrorKind");
class ServiceResolutionManager {
    #autobind;
    #defaultScope;
    #getActivationsResolutionParam;
    #resolutionContext;
    #onPlanHandlers;
    #planParamsOperationsManager;
    #serviceReferenceManager;
    constructor(planParamsOperationsManager, serviceReferenceManager, autobind, defaultScope) {
        this.#planParamsOperationsManager = planParamsOperationsManager;
        this.#serviceReferenceManager = serviceReferenceManager;
        this.#resolutionContext = this.#buildResolutionContext();
        this.#autobind = autobind;
        this.#defaultScope = defaultScope;
        this.#getActivationsResolutionParam = (serviceIdentifier) => this.#serviceReferenceManager.activationService.get(serviceIdentifier);
        this.#onPlanHandlers = [];
        this.#serviceReferenceManager.onReset(() => {
            this.#resetComputedProperties();
        });
    }
    get(serviceIdentifier, options) {
        const planResult = this.#buildPlanResult(false, serviceIdentifier, options);
        const resolvedValue = this.#getFromPlanResult(planResult);
        if ((0, common_1.isPromise)(resolvedValue)) {
            throw new InversifyContainerError_1.InversifyContainerError(InversifyContainerErrorKind_1.InversifyContainerErrorKind.invalidOperation, `Unexpected asynchronous service when resolving service "${(0, common_1.stringifyServiceIdentifier)(serviceIdentifier)}"`);
        }
        return resolvedValue;
    }
    getAll(serviceIdentifier, options) {
        const planResult = this.#buildPlanResult(true, serviceIdentifier, options);
        const resolvedValue = this.#getFromPlanResult(planResult);
        if ((0, common_1.isPromise)(resolvedValue)) {
            throw new InversifyContainerError_1.InversifyContainerError(InversifyContainerErrorKind_1.InversifyContainerErrorKind.invalidOperation, `Unexpected asynchronous service when resolving service "${(0, common_1.stringifyServiceIdentifier)(serviceIdentifier)}"`);
        }
        return resolvedValue;
    }
    async getAllAsync(serviceIdentifier, options) {
        const planResult = this.#buildPlanResult(true, serviceIdentifier, options);
        return this.#getFromPlanResult(planResult);
    }
    async getAsync(serviceIdentifier, options) {
        const planResult = this.#buildPlanResult(false, serviceIdentifier, options);
        return this.#getFromPlanResult(planResult);
    }
    onPlan(handler) {
        this.#onPlanHandlers.push(handler);
    }
    #resetComputedProperties() {
        this.#resolutionContext = this.#buildResolutionContext();
    }
    #buildGetPlanOptions(isMultiple, serviceIdentifier, options) {
        const name = options?.name;
        const optional = options?.optional ?? false;
        const tag = options?.tag;
        if (isMultiple) {
            return {
                chained: options?.chained ?? false,
                isMultiple,
                name,
                optional,
                serviceIdentifier,
                tag,
            };
        }
        else {
            return {
                isMultiple,
                name,
                optional,
                serviceIdentifier,
                tag,
            };
        }
    }
    #buildPlanParams(serviceIdentifier, isMultiple, options) {
        const planParams = {
            autobindOptions: (options?.autobind ?? this.#autobind)
                ? {
                    scope: this.#defaultScope,
                }
                : undefined,
            operations: this.#planParamsOperationsManager.planParamsOperations,
            rootConstraints: this.#buildPlanParamsConstraints(serviceIdentifier, isMultiple, options),
            servicesBranch: [],
        };
        this.#handlePlanParamsRootConstraints(planParams, options);
        return planParams;
    }
    #buildPlanParamsConstraints(serviceIdentifier, isMultiple, options) {
        if (isMultiple) {
            return {
                chained: options?.chained ?? false,
                isMultiple,
                serviceIdentifier,
            };
        }
        else {
            return {
                isMultiple,
                serviceIdentifier,
            };
        }
    }
    #buildPlanResult(isMultiple, serviceIdentifier, options) {
        const getPlanOptions = this.#buildGetPlanOptions(isMultiple, serviceIdentifier, options);
        const planResultFromCache = this.#serviceReferenceManager.planResultCacheService.get(getPlanOptions);
        if (planResultFromCache !== undefined) {
            return planResultFromCache;
        }
        const planResult = (0, core_1.plan)(this.#buildPlanParams(serviceIdentifier, isMultiple, options));
        for (const handler of this.#onPlanHandlers) {
            handler(getPlanOptions, planResult);
        }
        return planResult;
    }
    #buildResolutionContext() {
        return {
            get: this.get.bind(this),
            getAll: this.getAll.bind(this),
            getAllAsync: this.getAllAsync.bind(this),
            getAsync: this.getAsync.bind(this),
        };
    }
    // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
    #getFromPlanResult(planResult) {
        return (0, core_1.resolve)({
            context: this.#resolutionContext,
            getActivations: this.#getActivationsResolutionParam,
            planResult,
            requestScopeCache: new Map(),
        });
    }
    #handlePlanParamsRootConstraints(planParams, options) {
        if (options === undefined) {
            return;
        }
        if (options.name !== undefined) {
            planParams.rootConstraints.name = options.name;
        }
        if (options.optional === true) {
            planParams.rootConstraints.isOptional = true;
        }
        if (options.tag !== undefined) {
            planParams.rootConstraints.tag = {
                key: options.tag.key,
                value: options.tag.value,
            };
        }
        if (planParams.rootConstraints.isMultiple) {
            planParams.rootConstraints.chained =
                options?.chained ?? false;
        }
    }
}
exports.ServiceResolutionManager = ServiceResolutionManager;
//# sourceMappingURL=ServiceResolutionManager.js.map