"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BindInWhenOnFluentSyntaxImplementation = exports.BindWhenOnFluentSyntaxImplementation = exports.BindWhenFluentSyntaxImplementation = exports.BindOnFluentSyntaxImplementation = exports.BindToFluentSyntaxImplementation = exports.BindInFluentSyntaxImplementation = void 0;
const common_1 = require("@inversifyjs/common");
const core_1 = require("@inversifyjs/core");
const core_2 = require("@inversifyjs/core");
const BindingConstraintUtils_1 = require("../../container/binding/utils/BindingConstraintUtils");
const InversifyContainerError_1 = require("../../error/models/InversifyContainerError");
const InversifyContainerErrorKind_1 = require("../../error/models/InversifyContainerErrorKind");
const buildBindingIdentifier_1 = require("../calculations/buildBindingIdentifier");
const isAnyAncestorBindingConstraints_1 = require("../calculations/isAnyAncestorBindingConstraints");
const isAnyAncestorBindingConstraintsWithName_1 = require("../calculations/isAnyAncestorBindingConstraintsWithName");
const isAnyAncestorBindingConstraintsWithServiceId_1 = require("../calculations/isAnyAncestorBindingConstraintsWithServiceId");
const isAnyAncestorBindingConstraintsWithTag_1 = require("../calculations/isAnyAncestorBindingConstraintsWithTag");
const isBindingConstraintsWithName_1 = require("../calculations/isBindingConstraintsWithName");
const isBindingConstraintsWithNoNameNorTags_1 = require("../calculations/isBindingConstraintsWithNoNameNorTags");
const isBindingConstraintsWithTag_1 = require("../calculations/isBindingConstraintsWithTag");
const isMultipleResolvedValueMetadataInjectOptions_1 = require("../calculations/isMultipleResolvedValueMetadataInjectOptions");
const isNoAncestorBindingConstraints_1 = require("../calculations/isNoAncestorBindingConstraints");
const isNoAncestorBindingConstraintsWithName_1 = require("../calculations/isNoAncestorBindingConstraintsWithName");
const isNoAncestorBindingConstraintsWithServiceId_1 = require("../calculations/isNoAncestorBindingConstraintsWithServiceId");
const isNoAncestorBindingConstraintsWithTag_1 = require("../calculations/isNoAncestorBindingConstraintsWithTag");
const isNotParentBindingConstraints_1 = require("../calculations/isNotParentBindingConstraints");
const isNotParentBindingConstraintsWithName_1 = require("../calculations/isNotParentBindingConstraintsWithName");
const isNotParentBindingConstraintsWithServiceId_1 = require("../calculations/isNotParentBindingConstraintsWithServiceId");
const isNotParentBindingConstraintsWithTag_1 = require("../calculations/isNotParentBindingConstraintsWithTag");
const isParentBindingConstraints_1 = require("../calculations/isParentBindingConstraints");
const isParentBindingConstraintsWithName_1 = require("../calculations/isParentBindingConstraintsWithName");
const isParentBindingConstraintsWithServiceId_1 = require("../calculations/isParentBindingConstraintsWithServiceId");
const isParentBindingConstraintsWithTag_1 = require("../calculations/isParentBindingConstraintsWithTag");
const isResolvedValueMetadataInjectOptions_1 = require("../calculations/isResolvedValueMetadataInjectOptions");
class BindInFluentSyntaxImplementation {
    #binding;
    constructor(binding) {
        this.#binding = binding;
    }
    getIdentifier() {
        return (0, buildBindingIdentifier_1.buildBindingIdentifier)(this.#binding);
    }
    inRequestScope() {
        this.#binding.scope = core_1.bindingScopeValues.Request;
        return new BindWhenOnFluentSyntaxImplementation(this.#binding);
    }
    inSingletonScope() {
        this.#binding.scope = core_1.bindingScopeValues.Singleton;
        return new BindWhenOnFluentSyntaxImplementation(this.#binding);
    }
    inTransientScope() {
        this.#binding.scope = core_1.bindingScopeValues.Transient;
        return new BindWhenOnFluentSyntaxImplementation(this.#binding);
    }
}
exports.BindInFluentSyntaxImplementation = BindInFluentSyntaxImplementation;
class BindToFluentSyntaxImplementation {
    #callback;
    #containerModuleId;
    #defaultScope;
    #serviceIdentifier;
    constructor(callback, containerModuleId, defaultScope, serviceIdentifier) {
        this.#callback = callback;
        this.#containerModuleId = containerModuleId;
        this.#defaultScope = defaultScope;
        this.#serviceIdentifier = serviceIdentifier;
    }
    to(type) {
        const classMetadata = (0, core_1.getClassMetadata)(type);
        const binding = {
            cache: {
                isRight: false,
                value: undefined,
            },
            id: (0, core_2.getBindingId)(),
            implementationType: type,
            isSatisfiedBy: BindingConstraintUtils_1.BindingConstraintUtils.always,
            moduleId: this.#containerModuleId,
            onActivation: undefined,
            onDeactivation: undefined,
            scope: classMetadata.scope ?? this.#defaultScope,
            serviceIdentifier: this.#serviceIdentifier,
            type: core_1.bindingTypeValues.Instance,
        };
        this.#callback(binding);
        return new BindInWhenOnFluentSyntaxImplementation(binding);
    }
    toSelf() {
        if (typeof this.#serviceIdentifier !== 'function') {
            throw new Error('"toSelf" function can only be applied when a newable function is used as service identifier');
        }
        return this.to(this.#serviceIdentifier);
    }
    toConstantValue(value) {
        const binding = {
            cache: {
                isRight: false,
                value: undefined,
            },
            id: (0, core_2.getBindingId)(),
            isSatisfiedBy: BindingConstraintUtils_1.BindingConstraintUtils.always,
            moduleId: this.#containerModuleId,
            onActivation: undefined,
            onDeactivation: undefined,
            scope: core_1.bindingScopeValues.Singleton,
            serviceIdentifier: this.#serviceIdentifier,
            type: core_1.bindingTypeValues.ConstantValue,
            value: value,
        };
        this.#callback(binding);
        return new BindWhenOnFluentSyntaxImplementation(binding);
    }
    toDynamicValue(builder) {
        const binding = {
            cache: {
                isRight: false,
                value: undefined,
            },
            id: (0, core_2.getBindingId)(),
            isSatisfiedBy: BindingConstraintUtils_1.BindingConstraintUtils.always,
            moduleId: this.#containerModuleId,
            onActivation: undefined,
            onDeactivation: undefined,
            scope: this.#defaultScope,
            serviceIdentifier: this.#serviceIdentifier,
            type: core_1.bindingTypeValues.DynamicValue,
            value: builder,
        };
        this.#callback(binding);
        return new BindInWhenOnFluentSyntaxImplementation(binding);
    }
    toResolvedValue(factory, injectOptions) {
        const binding = {
            cache: {
                isRight: false,
                value: undefined,
            },
            factory,
            id: (0, core_2.getBindingId)(),
            isSatisfiedBy: BindingConstraintUtils_1.BindingConstraintUtils.always,
            metadata: this.#buildResolvedValueMetadata(injectOptions),
            moduleId: this.#containerModuleId,
            onActivation: undefined,
            onDeactivation: undefined,
            scope: this.#defaultScope,
            serviceIdentifier: this.#serviceIdentifier,
            type: core_1.bindingTypeValues.ResolvedValue,
        };
        this.#callback(binding);
        return new BindInWhenOnFluentSyntaxImplementation(binding);
    }
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    toFactory(builder) {
        const binding = {
            cache: {
                isRight: false,
                value: undefined,
            },
            factory: builder,
            id: (0, core_2.getBindingId)(),
            isSatisfiedBy: BindingConstraintUtils_1.BindingConstraintUtils.always,
            moduleId: this.#containerModuleId,
            onActivation: undefined,
            onDeactivation: undefined,
            scope: core_1.bindingScopeValues.Singleton,
            serviceIdentifier: this.#serviceIdentifier,
            type: core_1.bindingTypeValues.Factory,
        };
        this.#callback(binding);
        return new BindWhenOnFluentSyntaxImplementation(binding);
    }
    /**
     * @deprecated Use `toFactory()` instead. Providers will be removed in v8.
     * Providers exist for historical reasons from v5 when async dependencies weren't supported.
     * Factories are more flexible and can handle both sync and async operations.
     */
    // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-deprecated
    toProvider(provider) {
        // eslint-disable-next-line @typescript-eslint/no-deprecated
        const binding = {
            cache: {
                isRight: false,
                value: undefined,
            },
            id: (0, core_2.getBindingId)(),
            isSatisfiedBy: BindingConstraintUtils_1.BindingConstraintUtils.always,
            moduleId: this.#containerModuleId,
            onActivation: undefined,
            onDeactivation: undefined,
            provider,
            scope: core_1.bindingScopeValues.Singleton,
            serviceIdentifier: this.#serviceIdentifier,
            type: core_1.bindingTypeValues.Provider,
        };
        this.#callback(binding);
        return new BindWhenOnFluentSyntaxImplementation(binding);
    }
    toService(service) {
        const binding = {
            id: (0, core_2.getBindingId)(),
            isSatisfiedBy: BindingConstraintUtils_1.BindingConstraintUtils.always,
            moduleId: this.#containerModuleId,
            serviceIdentifier: this.#serviceIdentifier,
            targetServiceIdentifier: service,
            type: core_1.bindingTypeValues.ServiceRedirection,
        };
        this.#callback(binding);
    }
    #buildResolvedValueMetadata(options) {
        const resolvedValueMetadata = {
            arguments: (options ?? []).map((injectOption) => {
                if ((0, isResolvedValueMetadataInjectOptions_1.isResolvedValueMetadataInjectOptions)(injectOption)) {
                    if ((0, isMultipleResolvedValueMetadataInjectOptions_1.isMultipleResolvedValueMetadataInjectOptions)(injectOption)) {
                        return {
                            chained: injectOption.chained ?? false,
                            kind: core_1.ResolvedValueElementMetadataKind.multipleInjection,
                            name: injectOption.name,
                            optional: injectOption.optional ?? false,
                            tags: new Map((injectOption.tags ?? []).map((tag) => [
                                tag.key,
                                tag.value,
                            ])),
                            value: injectOption.serviceIdentifier,
                        };
                    }
                    else {
                        return {
                            kind: core_1.ResolvedValueElementMetadataKind.singleInjection,
                            name: injectOption.name,
                            optional: injectOption.optional ?? false,
                            tags: new Map((injectOption.tags ?? []).map((tag) => [
                                tag.key,
                                tag.value,
                            ])),
                            value: injectOption.serviceIdentifier,
                        };
                    }
                }
                else {
                    return {
                        kind: core_1.ResolvedValueElementMetadataKind.singleInjection,
                        name: undefined,
                        optional: false,
                        tags: new Map(),
                        value: injectOption,
                    };
                }
            }),
        };
        return resolvedValueMetadata;
    }
}
exports.BindToFluentSyntaxImplementation = BindToFluentSyntaxImplementation;
class BindOnFluentSyntaxImplementation {
    #binding;
    constructor(binding) {
        this.#binding = binding;
    }
    getIdentifier() {
        return (0, buildBindingIdentifier_1.buildBindingIdentifier)(this.#binding);
    }
    onActivation(activation) {
        this.#binding.onActivation = activation;
        return new BindWhenFluentSyntaxImplementation(this.#binding);
    }
    onDeactivation(deactivation) {
        this.#binding.onDeactivation = deactivation;
        if (this.#binding.scope !== core_1.bindingScopeValues.Singleton) {
            throw new InversifyContainerError_1.InversifyContainerError(InversifyContainerErrorKind_1.InversifyContainerErrorKind.invalidOperation, `Binding for service "${(0, common_1.stringifyServiceIdentifier)(this.#binding.serviceIdentifier)}" has a deactivation function, but its scope is not singleton. Deactivation functions can only be used with singleton bindings.`);
        }
        return new BindWhenFluentSyntaxImplementation(this.#binding);
    }
}
exports.BindOnFluentSyntaxImplementation = BindOnFluentSyntaxImplementation;
class BindWhenFluentSyntaxImplementation {
    #binding;
    constructor(binding) {
        this.#binding = binding;
    }
    getIdentifier() {
        return (0, buildBindingIdentifier_1.buildBindingIdentifier)(this.#binding);
    }
    when(constraint) {
        this.#binding.isSatisfiedBy = constraint;
        return new BindOnFluentSyntaxImplementation(this.#binding);
    }
    whenAnyAncestor(constraint) {
        return this.when((0, isAnyAncestorBindingConstraints_1.isAnyAncestorBindingConstraints)(constraint));
    }
    whenAnyAncestorIs(serviceIdentifier) {
        return this.when((0, isAnyAncestorBindingConstraintsWithServiceId_1.isAnyAncestorBindingConstraintsWithServiceId)(serviceIdentifier));
    }
    whenAnyAncestorNamed(name) {
        return this.when((0, isAnyAncestorBindingConstraintsWithName_1.isAnyAncestorBindingConstraintsWithName)(name));
    }
    whenAnyAncestorTagged(tag, tagValue) {
        return this.when((0, isAnyAncestorBindingConstraintsWithTag_1.isAnyAncestorBindingConstraintsWithTag)(tag, tagValue));
    }
    whenDefault() {
        return this.when(isBindingConstraintsWithNoNameNorTags_1.isBindingConstraintsWithNoNameNorTags);
    }
    whenNamed(name) {
        return this.when((0, isBindingConstraintsWithName_1.isBindingConstraintsWithName)(name));
    }
    whenNoParent(constraint) {
        return this.when((0, isNotParentBindingConstraints_1.isNotParentBindingConstraints)(constraint));
    }
    whenNoParentIs(serviceIdentifier) {
        return this.when((0, isNotParentBindingConstraintsWithServiceId_1.isNotParentBindingConstraintsWithServiceId)(serviceIdentifier));
    }
    whenNoParentNamed(name) {
        return this.when((0, isNotParentBindingConstraintsWithName_1.isNotParentBindingConstraintsWithName)(name));
    }
    whenNoParentTagged(tag, tagValue) {
        return this.when((0, isNotParentBindingConstraintsWithTag_1.isNotParentBindingConstraintsWithTag)(tag, tagValue));
    }
    whenParent(constraint) {
        return this.when((0, isParentBindingConstraints_1.isParentBindingConstraints)(constraint));
    }
    whenParentIs(serviceIdentifier) {
        return this.when((0, isParentBindingConstraintsWithServiceId_1.isParentBindingConstraintsWithServiceId)(serviceIdentifier));
    }
    whenParentNamed(name) {
        return this.when((0, isParentBindingConstraintsWithName_1.isParentBindingConstraintsWithName)(name));
    }
    whenParentTagged(tag, tagValue) {
        return this.when((0, isParentBindingConstraintsWithTag_1.isParentBindingConstraintsWithTag)(tag, tagValue));
    }
    whenTagged(tag, tagValue) {
        return this.when((0, isBindingConstraintsWithTag_1.isBindingConstraintsWithTag)(tag, tagValue));
    }
    whenNoAncestor(constraint) {
        return this.when((0, isNoAncestorBindingConstraints_1.isNoAncestorBindingConstraints)(constraint));
    }
    whenNoAncestorIs(serviceIdentifier) {
        return this.when((0, isNoAncestorBindingConstraintsWithServiceId_1.isNoAncestorBindingConstraintsWithServiceId)(serviceIdentifier));
    }
    whenNoAncestorNamed(name) {
        return this.when((0, isNoAncestorBindingConstraintsWithName_1.isNoAncestorBindingConstraintsWithName)(name));
    }
    whenNoAncestorTagged(tag, tagValue) {
        return this.when((0, isNoAncestorBindingConstraintsWithTag_1.isNoAncestorBindingConstraintsWithTag)(tag, tagValue));
    }
}
exports.BindWhenFluentSyntaxImplementation = BindWhenFluentSyntaxImplementation;
class BindWhenOnFluentSyntaxImplementation extends BindWhenFluentSyntaxImplementation {
    #bindOnFluentSyntax;
    constructor(binding) {
        super(binding);
        this.#bindOnFluentSyntax = new BindOnFluentSyntaxImplementation(binding);
    }
    onActivation(activation) {
        return this.#bindOnFluentSyntax.onActivation(activation);
    }
    onDeactivation(deactivation) {
        return this.#bindOnFluentSyntax.onDeactivation(deactivation);
    }
}
exports.BindWhenOnFluentSyntaxImplementation = BindWhenOnFluentSyntaxImplementation;
class BindInWhenOnFluentSyntaxImplementation extends BindWhenOnFluentSyntaxImplementation {
    #bindInFluentSyntax;
    constructor(binding) {
        super(binding);
        this.#bindInFluentSyntax = new BindInFluentSyntaxImplementation(binding);
    }
    inRequestScope() {
        return this.#bindInFluentSyntax.inRequestScope();
    }
    inSingletonScope() {
        return this.#bindInFluentSyntax.inSingletonScope();
    }
    inTransientScope() {
        return this.#bindInFluentSyntax.inTransientScope();
    }
}
exports.BindInWhenOnFluentSyntaxImplementation = BindInWhenOnFluentSyntaxImplementation;
//# sourceMappingURL=BindingFluentSyntaxImplementation.js.map