import { Class, reflector, isDecoratorAndValue } from '@ditsmod/core';
import { YouCanNotSetThisAction } from '#errors';
/**
 * Applies to importing `OpenapiModule.withParams()`. OAS parameter's property, indicates the parameter
 * should or not be bound to presence last param in a route path.
 */
export const BOUND_TO_PATH_PARAM = 'x-bound-to-path-param';
/**
 * Applies to importing `OpenapiModule.withParams()`. OAS parameter's property, indicates the parameter
 * should or not be bound to HTTP method in a route path.
 */
export const BOUND_TO_HTTP_METHOD = 'x-bound-to-http-method';
/**
 * Helper for OpenAPI `ParameterObject`s.
 */
export class Parameters {
    parameters = [];
    countOfLastPushedParams;
    // prettier-ignore
    required(paramsIn, modelOrString, ...params) {
        return this.setParams(true, paramsIn, modelOrString, ...params);
    }
    // prettier-ignore
    optional(paramsIn, modelOrString, ...params) {
        return this.setParams(false, paramsIn, modelOrString, ...params);
    }
    // prettier-ignore
    getParams(paramsIn, isRequired, modelOrString, ...params) {
        if (isRequired !== undefined) {
            this.setParams(isRequired, paramsIn, modelOrString, ...params);
        }
        return [...this.parameters];
    }
    bindTo(to, options = false) {
        const params = this.getLastAddedParams();
        const key = to == 'httpMethod' ? BOUND_TO_HTTP_METHOD : BOUND_TO_PATH_PARAM;
        params.forEach((param) => (param[key] = options));
        return this;
    }
    describe(...descriptions) {
        const params = this.getLastAddedParams();
        params.forEach((param, i) => (param.description = descriptions[i]));
        return this;
    }
    use(Plugin) {
        Object.getOwnPropertyNames(Plugin.prototype)
            .filter((p) => p != 'constructor')
            .forEach((p) => {
            this[p] = Plugin.prototype[p];
        });
        return this;
    }
    getLastAddedParams() {
        if (!this.countOfLastPushedParams) {
            throw new YouCanNotSetThisAction();
        }
        return this.parameters.slice(-this.countOfLastPushedParams);
    }
    setParams(isRequired, paramsIn, modelOrString, ...params) {
        let paramsObjects;
        if (typeof modelOrString == 'string') {
            params.unshift(modelOrString);
            paramsObjects = this.transformParams(params, paramsIn, isRequired);
        }
        else {
            paramsObjects = this.transformParams(params, paramsIn, isRequired);
            paramsObjects = this.setMetadata(modelOrString, paramsObjects);
        }
        this.parameters.push(...paramsObjects);
        this.countOfLastPushedParams = paramsObjects.length;
        return this;
    }
    transformParams(params, paramsIn, isRequired) {
        return params.map((param) => {
            return { in: paramsIn, name: param, required: isRequired };
        });
    }
    /**
     * Sets metadata from a model to parameters.
     */
    setMetadata(model, paramsObjects) {
        return paramsObjects.map((paramObject) => {
            const propertyDecorator = reflector.getMetadata(model)?.[paramObject.name];
            if (propertyDecorator) {
                const schemas = propertyDecorator.decorators
                    .filter((item) => isDecoratorAndValue(item))
                    .map((val) => val.value.schema);
                paramObject.schema = Object.assign({}, ...schemas, paramObject.schema);
                if (paramObject.schema.description) {
                    paramObject.description ??= paramObject.schema.description;
                }
                this.setPropertyType(paramObject.schema, propertyDecorator.type);
                if (paramObject.schema.type == 'array' && !paramObject.schema.items) {
                    paramObject.schema.items = {};
                }
            }
            return paramObject;
        });
    }
    setPropertyType(schema, propertyType) {
        if (schema.type === undefined) {
            if ([Boolean, Number, String, Array, Object].includes(propertyType)) {
                schema.type = (propertyType.name?.toLowerCase() || 'null');
            }
            else if (propertyType instanceof Class) {
                schema.type = 'object';
            }
            else {
                schema.type = 'null';
            }
        }
    }
}
// prettier-ignore
export function getParams(paramsIn, isRequired, modelOrString, ...params) {
    return new Parameters().getParams(paramsIn, isRequired, modelOrString, ...params);
}
//# sourceMappingURL=parameters.js.map