"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.tweakM4ModelPlugin = exports.tweakModelForTsp = exports.directives = void 0;
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
const codemodel_1 = require("@autorest/codemodel");
const codegen_1 = require("@azure-tools/codegen");
const model_state_1 = require("../utils/model-state");
const http_definitions_1 = require("../utils/http-definitions");
const linq_1 = require("@azure-tools/linq");
const sort_parameters_1 = require("../utils/sort-parameters");
exports.directives = [];
async function tweakModelForTsp(state) {
    const allDirectives = await state.service.getValue('directive');
    exports.directives = (0, linq_1.values)(allDirectives).toArray();
    return await tweakModel(state);
}
exports.tweakModelForTsp = tweakModelForTsp;
async function tweakModel(state) {
    const model = state.model;
    sortParameters(model);
    addResponseHeaderSchema(model);
    addDictionaryApiVersion(model);
    removeM4DefaultDescription(model);
    removeExceptionResponse(model);
    handleNoinlineDirective(state);
    return model;
}
//remove error responses except default
function removeExceptionResponse(model) {
    model.operationGroups.forEach(group => {
        var _a;
        (_a = group.operations) === null || _a === void 0 ? void 0 : _a.forEach(operation => {
            var _a;
            operation.exceptions = (_a = operation.exceptions) === null || _a === void 0 ? void 0 : _a.filter(exception => { var _a; return ((_a = exception.protocol.http) === null || _a === void 0 ? void 0 : _a.statusCodes[0]) === 'default'; });
        });
    });
}
//sort path parameters to follow the order in path for each operation
function sortParameters(model) {
    model.operationGroups.forEach(group => {
        var _a;
        (_a = group.operations) === null || _a === void 0 ? void 0 : _a.forEach(operation => {
            var _a, _b;
            operation.parameters = (0, sort_parameters_1.sortPathParameters)((_b = (_a = operation.requests) === null || _a === void 0 ? void 0 : _a[0].protocol.http) === null || _b === void 0 ? void 0 : _b.path, operation.parameters);
        });
    });
}
function handleNoinlineDirective(state) {
    let inlineModels = [];
    for (const directive of exports.directives.filter(each => each['no-inline'])) {
        inlineModels = inlineModels.concat((0, linq_1.values)(directive['no-inline']).toArray());
    }
    for (const model of state.model.schemas.objects || []) {
        if (inlineModels.includes(model.language.default.name)) {
            model.language.default['skip-inline'] = true;
        }
    }
}
function addResponseHeaderSchema(model) {
    // In remodeler, each operations response headers will has its own scheam. Each header will be schema's property. 
    // But in m4, if 'schema' is not explicitly defined, even 'headers' is specified, there won't be a schema for headers.
    // To keep backward compatiable, we will create headers schema here
    model.operationGroups.forEach((group) => {
        var _a;
        (_a = group.operations) === null || _a === void 0 ? void 0 : _a.forEach((op) => {
            if (!op.responses) {
                return;
            }
            op.responses.forEach((resp) => {
                var _a, _b, _c, _d;
                if (resp.schema) {
                    return;
                }
                const headers = (_a = resp.protocol.http) === null || _a === void 0 ? void 0 : _a.headers;
                if (headers === undefined) {
                    return;
                }
                const responseCode = (_c = (_b = resp.protocol.http) === null || _b === void 0 ? void 0 : _b.statusCodes) === null || _c === void 0 ? void 0 : _c[0];
                if (responseCode === undefined) {
                    return;
                }
                // Follow naming pattern in m3
                const code = (http_definitions_1.StatusCodes[responseCode] || '') || responseCode;
                const schemaName = `${group.language.default.name}_${op.language.default.name} ${code} ResponseHeaders`;
                const newSchema = ((_d = model.schemas.objects) === null || _d === void 0 ? void 0 : _d.find((schema) => schema.language.default.name === schemaName)) ||
                    new codemodel_1.ObjectSchema(schemaName, '');
                newSchema.language.default.isHeaderModel = true;
                if (!model.schemas.objects) {
                    model.schemas.objects = [];
                }
                model.schemas.objects.push(newSchema);
                headers.forEach((head) => {
                    // We lost description and x-ms-client-name in m4. So newProp's description is empty and use header as serializedName 
                    const newProp = new codemodel_1.Property(head.header, '', head.schema, {
                        readOnly: false,
                        required: false,
                        serializedName: head.header
                    });
                    newProp.language.default.HeaderProperty = 'Header';
                    if (!newSchema.properties) {
                        newSchema.properties = [];
                    }
                    newSchema.properties.push(newProp);
                });
                // Set response header use new schema
                resp.language.default.headerSchema = newSchema;
            });
        });
    });
}
function addDictionaryApiVersion(model) {
    var _a, _b;
    (_a = model.schemas.dictionaries) === null || _a === void 0 ? void 0 : _a.forEach((schema) => {
        if (schema.apiVersions) {
            return;
        }
        if (schema.elementType && schema.elementType.apiVersions) {
            schema.apiVersions = JSON.parse(JSON.stringify(schema.elementType.apiVersions));
        }
    });
    // If we cannot find api version from element type, try to get it from object schema who refers the dict or any.
    (_b = model.schemas.objects) === null || _b === void 0 ? void 0 : _b.forEach((schema) => {
        var _a;
        if (!schema.apiVersions) {
            return;
        }
        for (const prop of (0, codemodel_1.getAllProperties)(schema)) {
            if (prop.schema.type !== codemodel_1.SchemaType.Dictionary || prop.schema.apiVersions) {
                continue;
            }
            prop.schema.apiVersions = JSON.parse(JSON.stringify(schema.apiVersions));
        }
        if (schema.parents) {
            for (const parent of (((_a = schema.parents) === null || _a === void 0 ? void 0 : _a.all) || [])) {
                if (parent.type !== codemodel_1.SchemaType.Dictionary || parent.apiVersions) {
                    continue;
                }
                // for object which both contains properties and additional properties, 
                // we need to skip the model generation for redundant dict.
                parent.language.default.skip = true;
            }
        }
    });
}
function removeM4DefaultDescription(model) {
    // For dictionary and arrya schema and property, if there is no description assigned, m4 will set a default description like: Dictionary of <type> or Array of <type>
    // To keep same action as m3, we will set it to empty string
    var _a, _b, _c;
    const visited = new Set();
    [...(_a = model.schemas.objects) !== null && _a !== void 0 ? _a : [], ...(_b = model.schemas.dictionaries) !== null && _b !== void 0 ? _b : [], ...(_c = model.schemas.arrays) !== null && _c !== void 0 ? _c : []].forEach((schema) => {
        recursiveRemoveM4DefaultDescription(schema, visited);
    });
}
function recursiveRemoveM4DefaultDescription(schema, visited) {
    if (visited.has(schema) || (schema.type !== codemodel_1.SchemaType.Object && schema.type !== codemodel_1.SchemaType.Dictionary && schema.type !== codemodel_1.SchemaType.Array)) {
        return;
    }
    // Default description pattern in m4
    const defaultDictDescPattern = /Dictionary of <.?>$/;
    const defaultArrayDescPattern = /Array of .?$/;
    visited.add(schema);
    if (schema.type === codemodel_1.SchemaType.Dictionary) {
        const dictSchema = schema;
        recursiveRemoveM4DefaultDescription(dictSchema.elementType, visited);
        if (defaultDictDescPattern.test(dictSchema.language.default.description)) {
            dictSchema.language.default.description = '';
        }
    }
    else if (schema.type === codemodel_1.SchemaType.Array) {
        const arrSchema = schema;
        recursiveRemoveM4DefaultDescription(arrSchema.elementType, visited);
        if (defaultArrayDescPattern.test(schema.language.default.description)) {
            schema.language.default.description = '';
        }
    }
    else if (schema.type === codemodel_1.SchemaType.Object) {
        const objSchema = schema;
        for (const prop of (0, codemodel_1.getAllProperties)(objSchema)) {
            recursiveRemoveM4DefaultDescription(prop.schema, visited);
            if (prop.schema.type === codemodel_1.SchemaType.Dictionary && (defaultDictDescPattern.test(prop.language.default.description) || defaultArrayDescPattern.test(prop.language.default.description))) {
                prop.language.default.description = '';
            }
        }
    }
}
async function tweakM4ModelPlugin(service) {
    const allDirectives = await service.getValue('directive');
    exports.directives = (0, linq_1.values)(allDirectives).toArray();
    const state = await new model_state_1.ModelState(service).init();
    service.writeFile({ filename: 'code-model-v4-tweakm4codemodel.yaml', content: (0, codegen_1.serialize)(await tweakModel(state)), sourceMap: undefined, artifactType: 'code-model-v4' });
}
exports.tweakM4ModelPlugin = tweakM4ModelPlugin;
//# sourceMappingURL=plugin-tweak-m4-model.js.map