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);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
import { ExtensionsManager, ExtensionsMetaPerApp, PerAppService, injectable, Injector, optional, reflector, } from '@ditsmod/core';
import { stringify } from 'yaml';
import { RoutesExtension } from '@ditsmod/rest';
import { DEFAULT_OAS_OBJECT, defaultForNonOasGuard } from '#constants';
import { OasConfigFiles, OasExtensionConfig } from '#types/oas-extension-options.js';
import { OpenapiLogMediator } from '#services/openapi-log-mediator.js';
import { OasRouteMetaNotFound } from '#errors';
let OpenapiCompilerExtension = class OpenapiCompilerExtension {
    perAppService;
    injectorPerMod;
    extensionsManager;
    log;
    extensionsMetaPerApp;
    oasObject;
    stage1ExtensionMeta;
    constructor(perAppService, injectorPerMod, extensionsManager, log, extensionsMetaPerApp) {
        this.perAppService = perAppService;
        this.injectorPerMod = injectorPerMod;
        this.extensionsManager = extensionsManager;
        this.log = log;
        this.extensionsMetaPerApp = extensionsMetaPerApp;
    }
    async stage1() {
        const stage1ExtensionMeta = await this.extensionsManager.stage1(RoutesExtension, this, true);
        if (stage1ExtensionMeta.delay) {
            this.log.dataAccumulation(this);
            return false;
        }
        this.stage1ExtensionMeta = stage1ExtensionMeta;
        return this.oasObject;
    }
    async stage2() {
        if (this.stage1ExtensionMeta) {
            this.log.applyingAccumulatedData(this);
            await this.compileOasObject(this.stage1ExtensionMeta.groupDataPerApp);
            const json = JSON.stringify(this.oasObject);
            const oasOptions = this.extensionsMetaPerApp?.oasOptions;
            const yaml = stringify(this.oasObject, oasOptions?.yamlSchemaOptions);
            this.perAppService.reinitInjector([{ token: OasConfigFiles, useValue: { json, yaml } }]);
        }
    }
    async compileOasObject(groupDataPerApp) {
        const paths = {};
        this.initOasObject();
        for (const stage1ExtensionMetaPerApp of groupDataPerApp) {
            for (const metadataPerMod3 of stage1ExtensionMetaPerApp.groupData) {
                metadataPerMod3.aControllerMetadata.forEach(({ httpMethods, fullPath, routeMeta, guards }) => {
                    httpMethods.forEach((method) => {
                        const { oasPath, resolvedGuards, operationObject } = routeMeta;
                        if (operationObject) {
                            const clonedOperationObject = { ...operationObject };
                            this.setSecurityInfo(clonedOperationObject, guards);
                            const pathItemObject = { [method.toLowerCase()]: clonedOperationObject };
                            paths[`/${oasPath}`] = { ...(paths[`/${oasPath}`] || {}), ...pathItemObject };
                        }
                        else {
                            if (!method) {
                                throw new OasRouteMetaNotFound(metadataPerMod3.baseMeta.name);
                            }
                            this.applyNonOasRoute(fullPath, paths, method, guards);
                        }
                    });
                });
            }
        }
        this.oasObject.paths = paths;
        return this.oasObject;
    }
    initOasObject() {
        let oasExtensionConfig = this.injectorPerMod.get(OasExtensionConfig, undefined, null);
        if (!oasExtensionConfig) {
            oasExtensionConfig = {};
            this.log.oasObjectNotFound(this);
        }
        else {
            this.log.foundOasObject(this);
        }
        this.oasObject = Object.assign({}, DEFAULT_OAS_OBJECT, oasExtensionConfig.oasObject);
        this.oasObject.components = { ...(this.oasObject.components || {}) };
    }
    setSecurityInfo(operationObject, normalizedGuards) {
        const security = [];
        const tags = [];
        const responses = {};
        normalizedGuards.forEach((normalizedGuard) => {
            const guardName = normalizedGuard.guard.name;
            const numberOfDecorators = reflector.getDecorators(normalizedGuard.guard)?.length || 0;
            reflector.getDecorators(normalizedGuard.guard)?.forEach((decor, index) => {
                let securityName = numberOfDecorators > 1 ? `${guardName}_${index}` : guardName;
                securityName = securityName.charAt(0).toLowerCase() + securityName.slice(1);
                this.oasObject.components.securitySchemes = { ...(this.oasObject.components.securitySchemes || {}) };
                this.oasObject.components.securitySchemes[securityName] =
                    decor.value?.securitySchemeObject || defaultForNonOasGuard.securitySchemeObject;
                let scopes = normalizedGuard.params || [];
                if (!scopes.some((scope) => typeof scope == 'string')) {
                    scopes = [];
                }
                security.push({ [securityName]: scopes });
                tags.push(...(decor.value?.tags || defaultForNonOasGuard.tags || []));
                Object.assign(responses, decor.value?.responses || defaultForNonOasGuard.responses);
            });
        });
        this.mergeOperationObjects(operationObject, security, tags, responses);
    }
    mergeOperationObjects(operationObject, newSecurities, newTags, responses) {
        operationObject.tags = [...(operationObject.tags || [])];
        newTags.forEach((newTag) => {
            if (!operationObject.tags?.includes(newTag)) {
                operationObject.tags?.push(newTag);
            }
        });
        operationObject.security = [...(operationObject.security || [])];
        newSecurities.forEach((newSecurities) => {
            const newSecurityName = Object.keys(newSecurities)[0];
            if (!operationObject.security?.find((securities) => Object.keys(securities)[0] == newSecurityName)) {
                operationObject.security?.push(newSecurities);
            }
        });
        operationObject.responses = { ...(operationObject.responses || {}), ...responses };
    }
    applyNonOasRoute(path, paths, httpMethod, normalizedGuards) {
        httpMethod = httpMethod.toLowerCase();
        const parameters = [];
        path = `/${path}`.replace(/:([^/]+)/g, (_, name) => {
            parameters.push({ in: 'path', name, required: true });
            return `{${name}}`;
        });
        const operationObject = { tags: ['NonOasRoutes'], parameters, responses: {} };
        this.setSecurityInfo(operationObject, normalizedGuards);
        if (['POST', 'PATCH', 'PUT'].includes(httpMethod)) {
            operationObject.requestBody = {
                description: 'It is default content field for non-oasRoute',
                content: { ['application/json']: {} },
            };
        }
        if (paths[path]) {
            paths[path][httpMethod] = operationObject;
        }
        else {
            paths[path] = { [httpMethod]: operationObject };
        }
    }
};
OpenapiCompilerExtension = __decorate([
    injectable(),
    __param(4, optional()),
    __metadata("design:paramtypes", [PerAppService,
        Injector,
        ExtensionsManager,
        OpenapiLogMediator,
        ExtensionsMetaPerApp])
], OpenapiCompilerExtension);
export { OpenapiCompilerExtension };
//# sourceMappingURL=openapi-compiler.extension.js.map