"use strict";
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.tweakModelAzurePluginV2 = exports.tweakModel = void 0;
const linq_1 = require("@azure-tools/linq");
const model_state_1 = require("../utils/model-state");
const codemodel_1 = require("@autorest/codemodel");
const codegen_1 = require("@azure-tools/codegen");
const xmsPageable = 'x-ms-pageable';
async function tweakModel(state) {
    var _a, _b, _c, _d, _e, _f, _g, _h, _j;
    const model = state.model;
    // service.message{ Channel: Channel.Debug, Text: "THIS IS THE AZURE TWEAKER" });
    // TODO:
    // look at models, and extract out any case that has an IRESOURCE, IPROXYRESOURCE, etc.
    // and use the common versions of those models.
    // Is the result marked x-ms-pagable?
    // identify the next link (null means just get the results as an array)
    // if nextLinkName is null, then it won't actually page, but we'd like to unroll the contents anyway.
    for (const group of (0, linq_1.values)(model.operationGroups)) {
        for (const operation of (0, linq_1.values)(group.operations)) {
            if (operation.extensions && operation.extensions[xmsPageable]) {
                // it's marked pagable.
                operation.language.default.pageable = {
                    responseType: 'pageable',
                    nextLinkName: operation.extensions[xmsPageable].nextLinkName || undefined,
                    itemName: operation.extensions[xmsPageable].itemName || 'value',
                    operationName: operation.extensions[xmsPageable].operationName || `${operation.language.default.name}Next`,
                };
                continue;
            }
            // let's just check to see if it looks like it's supposed to be a collection
            for (const response of (0, linq_1.values)(operation.responses)) {
                // does the response have a schema?
                // TODO: check schema
                if (getSchema(response)) {
                    const schema = getSchema(response);
                    // is this just an array response?
                    if (schema.type === codemodel_1.SchemaType.Array) {
                        operation.language.default.pageable = {
                            responseType: 'array',
                        };
                        continue;
                    }
                    // if it returns an object, let's see what's inside...
                    if (schema.type === codemodel_1.SchemaType.Object) {
                        const objSchema = schema;
                        // does it have  a single member that is an array (ie, value :  [...])
                        if ((0, linq_1.length)(objSchema.properties) === 1 && !objSchema.parents) {
                            const property = (_a = objSchema.properties) === null || _a === void 0 ? void 0 : _a[0];
                            if (property) {
                                if (property.schema.type === codemodel_1.SchemaType.Array) {
                                    // nested array!
                                    operation.language.default.pageable = {
                                        responseType: 'nested-array',
                                        itemName: property.serializedName,
                                    };
                                }
                                continue;
                            }
                        }
                        // xichen: If response schema has only 2 properties and one of it is nextLink, the other is array
                        // does it kinda look like a x-ms-pagable (value/nextlink?)
                        if ((0, linq_1.length)(objSchema.properties) === 2 && !objSchema.parents) {
                            const hasNextLink = (_b = objSchema.properties) === null || _b === void 0 ? void 0 : _b.some((prop) => prop.serializedName === 'nextLink');
                            if (hasNextLink) {
                                const property = (_c = objSchema.properties) === null || _c === void 0 ? void 0 : _c.find((prop) => prop.serializedName !== 'nextLink');
                                if (property) {
                                    if (property.schema.type === codemodel_1.SchemaType.Array) {
                                        // nested array!
                                        operation.language.default.pageable = {
                                            responseType: 'nested-array',
                                            itemName: property.serializedName,
                                            nextLinkName: 'nextLink'
                                        };
                                    }
                                    continue;
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    // make sure that all operations with lro have an options block.
    for (const group of (0, linq_1.values)(model.operationGroups)) {
        for (const operation of (0, linq_1.values)(group.operations)) {
            if (operation.extensions && operation.extensions['x-ms-long-running-operation']) {
                operation.language.default.asjob = true;
                operation.language.default.lro = operation.extensions['x-ms-long-running-operation-options'] || {
                    'final-state-via': 'default'
                };
                // LRO 201 and 202 responses are handled internally, so remove any 201/202 responses in the operation
                operation.responses = (operation.responses).filter(each => { var _a, _b; return ((_a = each.protocol.http) === null || _a === void 0 ? void 0 : _a.statusCodes[0]) !== '201' && ((_b = each.protocol.http) === null || _b === void 0 ? void 0 : _b.statusCodes[0]) !== '202'; });
                //delete operation.responses['201'];
                //delete operation.responses['202'];
                // for lro deletion, we need to add the 200 response if it's not already there.
                if (operation.requests && ((_d = operation.requests[0].protocol.http) === null || _d === void 0 ? void 0 : _d.method) === 'delete') {
                    if (!operation.responses.find(each => { var _a; return ((_a = each.protocol.http) === null || _a === void 0 ? void 0 : _a.statusCodes[0]) === '200'; })) {
                        const response = new codemodel_1.Response();
                        response.protocol.http = (_e = response.protocol.http) !== null && _e !== void 0 ? _e : new codemodel_1.Protocol();
                        response.protocol.http.statusCodes = ['200'];
                        operation.responses.push(response);
                    }
                }
            }
        }
    }
    // xichen: Cannot find 'x-ms-metadata' from swagger repo. Are we still using it?
    // Api Version parameter handling for Azure.
    // if there is only a single api-version for the operation, let's just make it a constant
    // otherwise, we need to make it selectable, but default to the 'latest' version there is.
    for (const group of (0, linq_1.values)(model.operationGroups)) {
        for (const operation of (0, linq_1.values)(group.operations)) {
            const apiVersions = operation.apiVersions;
            for (const parameter of (0, linq_1.values)(operation.parameters)) {
                if (parameter.language.default.serializedName === 'api-version') {
                    // only set it if it hasn't been set yet.
                    // if (parameter.details.default.constantValue) {
                    //continue;
                    //}
                    if (apiVersions) {
                        // set the constant value to the first one
                        if ((0, linq_1.length)(apiVersions) === 1) {
                            parameter.language.default.constantValue = apiVersions[0].version;
                            continue;
                        }
                        // otherwise, the parameter can't have a constant value
                        parameter.language.default.constantValue = undefined;
                        // mark it so that we can add profile support in the method generation
                        parameter.language.default.apiversion = true;
                    }
                }
            }
        }
    }
    // when make-sub-resources-byreference is specified, mark models with a writable id as byref.
    if (await state.getValue('azure', false) && await state.getValue('make-sub-resources-byreference', false)) {
        for (const schema of (0, linq_1.values)((_f = model.schemas.objects) !== null && _f !== void 0 ? _f : [])) {
            // find schemas that have an 'id' and are not readonly
            if ((0, linq_1.values)((0, codemodel_1.getAllProperties)(schema)).any(prop => prop.serializedName === 'id' && !prop.language.default.readOnly)) {
                // look thru the operations, and the PUT methods 
                for (const group of model.operationGroups) {
                    for (const op of (0, linq_1.values)(group.operations)) {
                        for (const request of (_g = op.requests) !== null && _g !== void 0 ? _g : []) {
                            if (((_h = request.protocol.http) === null || _h === void 0 ? void 0 : _h.method) === 'put') {
                                for (const response of (_j = op.responses) !== null && _j !== void 0 ? _j : []) {
                                    // see if any of the responses have the same schema as we are looking for 
                                    if (getSchema(response) === schema) {
                                        // tell it not to inline that 
                                        schema.language.default.byReference = true;
                                        break;
                                    }
                                }
                                break;
                            }
                        }
                    }
                }
            }
        }
    }
    return model;
}
exports.tweakModel = tweakModel;
function getSchema(response) {
    return response.schema;
}
// Azure version -
// Additional tweaks the code model to adjust things so that the code will generate better.
async function tweakModelAzurePluginV2(service) {
    const state = await new model_state_1.ModelState(service).init();
    await service.writeFile({ filename: 'code-model-v4-tweakcodemodelazure-v2.yaml', content: (0, codegen_1.serialize)(await tweakModel(state)), sourceMap: undefined, artifactType: 'code-model-v4' });
}
exports.tweakModelAzurePluginV2 = tweakModelAzurePluginV2;
//# sourceMappingURL=plugin-tweak-model-azure-v2.js.map