"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.csnamerV2 = exports.nameStuffRight = void 0;
const codemodel_1 = require("@autorest/codemodel");
const codegen_1 = require("@azure-tools/codegen");
const linq_1 = require("@azure-tools/linq");
const codegen_csharp_1 = require("@azure-tools/codegen-csharp");
const schema_resolver_1 = require("../llcsharp/schema/schema-resolver");
const model_state_1 = require("../utils/model-state");
const schema_1 = require("../utils/schema");
function setPropertyNames(schema) {
    var _a, _b, _c;
    // name each property in this schema
    // skip-for-time-being
    if (!(0, codemodel_1.isObjectSchema)(schema)) {
        return;
    }
    for (const propertySchema of (0, linq_1.values)(schema.properties)) {
        const propertyDetails = propertySchema.language.default;
        const mutability = (0, schema_1.getMutability)(propertySchema);
        propertyDetails.required = (_a = propertySchema.required) !== null && _a !== void 0 ? _a : false;
        propertyDetails.readOnly = (_b = propertySchema.readOnly) !== null && _b !== void 0 ? _b : false;
        propertyDetails.read = mutability.read;
        propertyDetails.update = mutability.update && !propertyDetails.readOnly;
        propertyDetails.create = mutability.create && !propertyDetails.readOnly;
        const className = (_c = schema.language.csharp) === null || _c === void 0 ? void 0 : _c.name;
        let pname = (0, codegen_1.getPascalIdentifier)(propertyDetails.name);
        if (pname === className) {
            pname = `${pname}Property`;
        }
        if (pname === 'default') {
            pname = '@default';
        }
        propertySchema.language.csharp = {
            ...propertyDetails,
            name: pname // and so are the propertyNmaes
        };
        if (propertyDetails.isNamedStream) {
            propertySchema.language.csharp.namedStreamPropertyName = (0, codegen_1.pascalCase)((0, codegen_1.fixLeadingNumber)([...(0, codegen_1.deconstruct)(propertyDetails.name), 'filename']));
        }
    }
}
function setSchemaNames(schemaGroups, azure, serviceNamespace, keepNames, addAPIVersion = false) {
    var _a, _b;
    const baseNamespace = new Set();
    const subNamespace = new Map();
    // dolauli need to notice this -- schemas in the namespace of the lowest supported api version
    // in Azure Mode, we want to always put schemas into the namespace of the lowest supported apiversion.
    // otherwise, we just want to differentiate with a simple incremental numbering scheme.
    for (const group of (0, linq_1.values)(schemaGroups)) {
        for (const schema of group) {
            if (schema.language.default.skip) {
                continue;
            }
            let thisNamespace = baseNamespace;
            let thisApiversion = '';
            // create the namespace if required
            if (azure) {
                const versions = [...(0, linq_1.values)(schema.apiVersions).select(v => v.version)];
                if (((_a = schema.language.default) === null || _a === void 0 ? void 0 : _a.uid) !== 'universal-parameter-type') {
                    if (addAPIVersion && versions && (0, linq_1.length)(versions) > 0) {
                        thisApiversion = (0, codegen_1.minimum)(versions);
                        thisNamespace = subNamespace.get(thisApiversion) || new Set();
                        subNamespace.set(thisApiversion, thisNamespace);
                    }
                }
            }
            // for each schema, we're going to set the name
            // to the suggested name, unless we have collisions
            // at which point, we're going to add a number (for now?)
            const details = schema.language.default;
            let schemaName = (0, codegen_1.getPascalIdentifier)(details.name);
            const apiName = (!thisApiversion) ? '' : (0, codegen_1.getPascalIdentifier)(`Api ${thisApiversion}`);
            const ns = addAPIVersion && !!thisApiversion ? ['.', apiName] : [];
            let n = 1;
            while (thisNamespace.has(schemaName) ||
                (keepNames.includes(schemaName) && ((_b = schema.language.default) === null || _b === void 0 ? void 0 : _b.uid) !== 'universal-parameter-type')) {
                schemaName = (0, codegen_1.getPascalIdentifier)(`${details.name}_${n++}`);
            }
            thisNamespace.add(schemaName);
            // object types.
            if (schema.type === codemodel_1.SchemaType.Object || schema.type === codemodel_1.SchemaType.Dictionary || schema.type === codemodel_1.SchemaType.Any) {
                schema.language.csharp = {
                    ...details,
                    apiversion: thisApiversion,
                    apiname: apiName,
                    interfaceName: 'I' + (0, codegen_1.pascalCase)((0, codegen_1.fixLeadingNumber)([...(0, codegen_1.deconstruct)(schemaName)])),
                    internalInterfaceName: 'I' + (0, codegen_1.pascalCase)((0, codegen_1.fixLeadingNumber)([...(0, codegen_1.deconstruct)(schemaName), 'Internal'])),
                    fullInternalInterfaceName: `${(0, codegen_1.pascalCase)([serviceNamespace, '.', 'Models', ...ns])}.${'I' + (0, codegen_1.pascalCase)((0, codegen_1.fixLeadingNumber)([...(0, codegen_1.deconstruct)(schemaName), 'Internal']))}`,
                    name: (0, codegen_1.getPascalIdentifier)(schemaName),
                    namespace: (0, codegen_1.pascalCase)([serviceNamespace, '.', 'Models', ...ns]),
                    fullname: `${(0, codegen_1.pascalCase)([serviceNamespace, '.', 'Models', ...ns])}.${(0, codegen_1.getPascalIdentifier)(schemaName)}`,
                };
            }
            else if (schema.type === codemodel_1.SchemaType.Choice || schema.type === codemodel_1.SchemaType.SealedChoice) {
                // oh, it's an enum type
                const choiceSchema = schema;
                schema.language.csharp = {
                    ...details,
                    interfaceName: 'I' + (0, codegen_1.pascalCase)((0, codegen_1.fixLeadingNumber)([...(0, codegen_1.deconstruct)(schemaName)])),
                    name: (0, codegen_1.getPascalIdentifier)(schemaName),
                    namespace: (0, codegen_1.pascalCase)([serviceNamespace, '.', 'Support']),
                    fullname: `${(0, codegen_1.pascalCase)([serviceNamespace, '.', 'Support'])}.${(0, codegen_1.getPascalIdentifier)(schemaName)}`,
                    enum: {
                        ...schema.language.default.enum,
                        name: (0, codegen_1.getPascalIdentifier)(schema.language.default.name),
                        values: choiceSchema.choices.map(each => {
                            return {
                                ...each,
                                name: (0, codegen_1.getPascalIdentifier)(each.language.default.name),
                                description: each.language.default.description
                            };
                        })
                    }
                };
            }
            else {
                schema.language.csharp = {
                    ...details,
                    interfaceName: '<INVALID_INTERFACE>',
                    internalInterfaceName: '<INVALID_INTERFACE>',
                    name: schemaName,
                    namespace: '<INVALID_NAMESPACE>',
                    fullname: '<INVALID_FULLNAME>'
                };
                // xichen: for invalid namespace case, we won't create model class. So we do not need consider dup case
                thisNamespace.delete(schemaName);
            }
            // name each property in this schema
            setPropertyNames(schema);
            // fix enum names
            if (schema.type === codemodel_1.SchemaType.Choice || schema.type === codemodel_1.SchemaType.SealedChoice) {
                schema.language.csharp.enum.name = (0, codegen_1.getPascalIdentifier)(schema.language.default.name);
                // and the value names themselves
                for (const value of (0, linq_1.values)(schema.language.csharp.enum.values)) {
                    // In m3, enum.name and enum.value are same. But in m4, enum.name is named by m4.
                    // To keep same action as m3, use enum.value here
                    value.name = (0, codegen_1.getPascalIdentifier)(value.value);
                }
            }
        }
    }
}
async function setOperationNames(state, resolver) {
    var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
    // keep a list of operation names that we've assigned.
    const operationNames = new Set();
    for (const operationGroup of (0, linq_1.values)(state.model.operationGroups)) {
        for (const operation of (0, linq_1.values)(operationGroup.operations)) {
            const details = operation.language.default;
            // come up with a name
            const oName = (0, codegen_1.getPascalIdentifier)(operationGroup.$key + '_' + details.name);
            let i = 1;
            let operationName = oName;
            while (operationNames.has(operationName)) {
                // if we have used that name, try again.
                operationName = `${oName}${i++}`;
            }
            operationNames.add(operationName);
            operation.language.csharp = {
                ...details,
                name: operationName,
            };
            // parameters are camelCased.
            for (const parameter of (0, linq_1.values)(operation.parameters)) {
                const parameterDetails = parameter.language.default;
                let propName = (0, codegen_1.camelCase)((0, codegen_1.fixLeadingNumber)((0, codegen_1.deconstruct)(parameterDetails.serializedName)));
                if (propName === 'default') {
                    propName = '@default';
                }
                parameter.language.csharp = {
                    ...parameterDetails,
                    name: propName
                };
            }
            const responses = [...(0, linq_1.values)(operation.responses), ...(0, linq_1.values)(operation.exceptions)];
            for (const rsp of responses) {
                // per responseCode
                const response = rsp;
                const responseTypeDefinition = response.schema ? resolver.resolveTypeDeclaration(response.schema, true, state, await state.getValue('fixed-array', false)) : undefined;
                const headerSchema = response.language.default.headerSchema;
                const headerTypeDefinition = headerSchema ? resolver.resolveTypeDeclaration(headerSchema, true, state.path('schemas', headerSchema.language.default.name), await state.getValue('fixed-array', false)) : undefined;
                let code = (codegen_csharp_1.System.Net.HttpStatusCode[(_a = response.protocol.http) === null || _a === void 0 ? void 0 : _a.statusCodes[0]] ? codegen_csharp_1.System.Net.HttpStatusCode[(_b = response.protocol.http) === null || _b === void 0 ? void 0 : _b.statusCodes[0]].value : (((_c = response.protocol.http) === null || _c === void 0 ? void 0 : _c.statusCodes[0]) || '')).replace('global::System.Net.HttpStatusCode', '');
                let rawValue = code.replace(/\./, '');
                if (((_d = response.protocol.http) === null || _d === void 0 ? void 0 : _d.statusCodes[0]) === 'default' || rawValue === 'default' || '') {
                    rawValue = 'any response code not handled elsewhere';
                    code = 'default';
                    response.language.default.isErrorResponse = true;
                }
                response.language.csharp = {
                    ...response.language.default,
                    responseType: responseTypeDefinition ? responseTypeDefinition.declaration : '',
                    headerType: headerTypeDefinition ? headerTypeDefinition.declaration : '',
                    name: ((0, linq_1.length)((_e = response.protocol.http) === null || _e === void 0 ? void 0 : _e.mimeTypes) <= 1) ?
                        (0, codegen_1.camelCase)((0, codegen_1.fixLeadingNumber)((0, codegen_1.deconstruct)(`on ${code}`))) : // the common type (or the only one.)
                        (0, codegen_1.camelCase)((0, codegen_1.fixLeadingNumber)((0, codegen_1.deconstruct)(`on ${code} ${(_f = response.protocol.http) === null || _f === void 0 ? void 0 : _f.mimeTypes[0]}`))),
                    description: ((0, linq_1.length)((_g = response.protocol.http) === null || _g === void 0 ? void 0 : _g.mimeTypes) <= 1) ?
                        `a delegate that is called when the remote service returns ${(_h = response.protocol.http) === null || _h === void 0 ? void 0 : _h.statusCodes[0]} (${rawValue}).` :
                        `a delegate that is called when the remote service returns ${(_j = response.protocol.http) === null || _j === void 0 ? void 0 : _j.statusCodes[0]} (${rawValue}) with a Content-Type matching ${(_k = response.protocol.http) === null || _k === void 0 ? void 0 : _k.mimeTypes.join(',')}.`
                };
            }
        }
    }
}
async function nameStuffRight(state) {
    const resolver = new schema_resolver_1.SchemaDefinitionResolver(await state.getValue('fixed-array', false));
    const model = state.model;
    // set the namespace for the service
    const serviceNamespace = await state.getValue('namespace', 'Sample.API');
    const azure = await state.getValue('azure', false) || await state.getValue('azure-arm', false);
    const clientName = (0, codegen_1.getPascalIdentifier)(model.language.default.name);
    const addAPIVersion = await state.getValue('add-api-version-in-model-namespace', false);
    // dolauli see model.details.csharp for c# related staff
    // set c# client details (name)
    model.language.csharp = {
        ...model.language.default,
        name: clientName,
        namespace: serviceNamespace,
        fullname: `${serviceNamespace}.${clientName}`
    };
    const universalIdName = `${await state.getValue('service-name')}Identity`;
    setSchemaNames(model.schemas, azure, serviceNamespace, [universalIdName], addAPIVersion);
    await setOperationNames(state, resolver);
    return model;
}
exports.nameStuffRight = nameStuffRight;
async function csnamerV2(service) {
    // dolauli add names for http operations and schemas
    //return processCodeModel(nameStuffRight, service, 'csnamer');
    //const session = await startSession<PwshModel>(service, {}, codeModelSchema);
    //const result = tweakModelV2(session);
    const state = await new model_state_1.ModelState(service).init();
    await service.writeFile({ filename: 'code-model-v4-csnamer-v2.yaml', content: (0, codegen_1.serialize)(await nameStuffRight(state)), sourceMap: undefined, artifactType: 'code-model-v4' });
}
exports.csnamerV2 = csnamerV2;
//# sourceMappingURL=cs-namer-v2.js.map