var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { injectable, reflector, MetadataPerMod2, } from '@ditsmod/core';
import { AppOptions, isCtrlDecor, RouteMeta, RoutesExtension, } from '@ditsmod/rest';
import { isOasRoute, isReferenceObject } from '#utils/type-guards.js';
import { BOUND_TO_HTTP_METHOD, BOUND_TO_PATH_PARAM } from '#utils/parameters.js';
import { getLastParameterObjects, getLastReferenceObjects } from '#utils/get-last-params.js';
import { CompilingOasRoutesFailed, ThrowParamNotFoundInPath } from '#errors';
let OpenapiRoutesExtension = class OpenapiRoutesExtension extends RoutesExtension {
    appOptions;
    metadataPerMod2;
    constructor(appOptions, metadataPerMod2) {
        super(appOptions, metadataPerMod2);
        this.appOptions = appOptions;
        this.metadataPerMod2 = metadataPerMod2;
    }
    getControllersMetadata(prefixPerApp, restMetadataPerMod2) {
        const { baseMeta, prefixPerMod, applyControllers, meta } = restMetadataPerMod2;
        const oasOptions = baseMeta.extensionsMeta.oasOptions;
        const prefixParams = oasOptions?.paratemers;
        const prefixTags = oasOptions?.tags;
        const aControllerMetadata = [];
        if (applyControllers)
            for (const Controller of meta.controllers) {
                const classMeta = reflector.getMetadata(Controller);
                for (const methodName of classMeta) {
                    for (const decoratorAndValue of classMeta[methodName].decorators) {
                        if (!isOasRoute(decoratorAndValue)) {
                            continue;
                        }
                        const oasRoute = decoratorAndValue.value;
                        const providersPerRou = [];
                        const providersPerReq = [];
                        const ctrlDecorator = classMeta.constructor.decorators.find(isCtrlDecor);
                        const scope = ctrlDecorator?.value.scope;
                        if (scope == 'ctx') {
                            baseMeta.providersPerMod.unshift(Controller);
                        }
                        const guards = [];
                        const { httpMethod, path: controllerPath, operationObject, interceptors } = oasRoute;
                        const prefix = [prefixPerApp, prefixPerMod].filter((s) => s).join('/');
                        const fullPath = this.getPath(prefix, controllerPath);
                        guards.push(...this.normalizeGuards(httpMethod, fullPath, decoratorAndValue.value.guards));
                        const controllerFactory = { useFactory: [Controller, Controller.prototype[methodName]] };
                        providersPerReq.push(...((ctrlDecorator?.value).providersPerReq || []), controllerFactory);
                        const clonedOperationObject = { ...(operationObject || {}) };
                        const { parameters } = clonedOperationObject;
                        const httpMethods = Array.isArray(httpMethod) ? httpMethod : [httpMethod];
                        const { paramsRefs, paramsInPath, paramsNonPath } = this.mergeParams(httpMethods, fullPath, Controller.name, prefixParams, parameters);
                        clonedOperationObject.parameters = [...paramsRefs, ...paramsInPath, ...paramsNonPath];
                        clonedOperationObject.tags = [...(clonedOperationObject.tags || []), ...(prefixTags || [])];
                        // For now, here ReferenceObjects is ignored, if it is intended for a path.
                        const oasPath = this.transformToOasPath(baseMeta.name, fullPath, paramsInPath);
                        providersPerRou.push(...(ctrlDecorator?.value.providersPerRou || []));
                        const routeMeta = {
                            oasPath,
                            operationObject: clonedOperationObject,
                            Controller,
                            methodName,
                        };
                        providersPerRou.push({ token: RouteMeta, useValue: routeMeta });
                        aControllerMetadata.push({
                            providersPerRou,
                            providersPerReq,
                            fullPath,
                            httpMethods,
                            routeMeta,
                            scope,
                            guards,
                            interceptors,
                        });
                    }
                }
            }
        return aControllerMetadata;
    }
    mergeParams(httpMethods, path, controllerName, prefixParams, params) {
        params = [...(prefixParams || []), ...(params || [])];
        const referenceObjects = [];
        const paramsInPath = [];
        const paramsNonPath = [];
        params.forEach((p) => {
            if (isReferenceObject(p)) {
                referenceObjects.push(p);
            }
            else {
                if (p.in == 'path') {
                    const paramName = `:${p.name}`;
                    if (path.includes(paramName)) {
                        paramsInPath.push(p);
                    }
                    else {
                        throw new ThrowParamNotFoundInPath(controllerName, paramName, path);
                    }
                }
                else {
                    httpMethods.forEach((httpMethod) => this.bindParams(httpMethod, path, paramsNonPath, p));
                }
            }
        });
        return {
            paramsRefs: getLastReferenceObjects(referenceObjects),
            paramsInPath: getLastParameterObjects(paramsInPath),
            paramsNonPath: getLastParameterObjects(paramsNonPath),
        };
    }
    bindParams(httpMethod, path, paramsNonPath, param) {
        const boundToLastParam = param[BOUND_TO_PATH_PARAM];
        const boundToMethod = param[BOUND_TO_HTTP_METHOD];
        if (boundToLastParam !== undefined && boundToMethod) {
            if (httpMethod == boundToMethod && this.boundToPathOk(path, boundToLastParam)) {
                paramsNonPath.push(param);
            }
        }
        else if (boundToLastParam !== undefined) {
            if (this.boundToPathOk(path, boundToLastParam)) {
                paramsNonPath.push(param);
            }
        }
        else if (boundToMethod) {
            if (httpMethod == boundToMethod) {
                paramsNonPath.push(param);
            }
        }
        else {
            paramsNonPath.push(param);
        }
    }
    boundToPathOk(path, boundToLastParam) {
        const prefixLastPart = path?.split('/').slice(-1)[0];
        return ((prefixLastPart?.charAt(0) == ':' && boundToLastParam) || (prefixLastPart?.charAt(0) != ':' && !boundToLastParam));
    }
    /**
     * Transform from `path/:param` to `path/{param}`.
     */
    transformToOasPath(moduleName, path, paramsInPath) {
        const paramsNames = (paramsInPath || []).map((p) => p.name);
        paramsNames.forEach((name) => {
            if (path.includes(`{${name}}`)) {
                let msg = `Compiling OAS routes failed: ${moduleName} have a route with param: "{${name}}"`;
                msg += `, you must convert this to ":${name}"`;
                throw new CompilingOasRoutesFailed(moduleName, name);
            }
            path = path.replace(`:${name}`, `{${name}}`);
        });
        return path;
    }
};
OpenapiRoutesExtension = __decorate([
    injectable(),
    __metadata("design:paramtypes", [AppOptions,
        MetadataPerMod2])
], OpenapiRoutesExtension);
export { OpenapiRoutesExtension };
//# sourceMappingURL=openapi-routes.extension.js.map