"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.namerV2 = exports.tweakModel = exports.getDeduplicatedNoun = void 0;
const codemodel_1 = require("@autorest/codemodel");
const extension_base_1 = require("@autorest/extension-base");
//import { allVirtualParameters, allVirtualProperties, resolveParameterNames, resolvePropertyNames } from '@azure-tools/codemodel-v3';
const codegen_1 = require("@azure-tools/codegen");
const linq_1 = require("@azure-tools/linq");
const linq = require("@azure-tools/linq");
const name_inferrer_1 = require("../internal/name-inferrer");
const model_state_1 = require("../utils/model-state");
const resolve_conflicts_1 = require("../utils/resolve-conflicts");
function getCmdletName(verb, subjectPrefix, subject) {
    return `${verb}-${subjectPrefix}${subject}`;
}
function getDeduplicatedNoun(subjectPrefix, subject) {
    // dedup parts
    const dedupedPrefix = [...(0, codegen_1.removeSequentialDuplicates)((0, codegen_1.deconstruct)(subjectPrefix))];
    const dedupedSubject = [...(0, codegen_1.removeSequentialDuplicates)((0, codegen_1.deconstruct)(subject))];
    // dedup the noun
    const dedupedMerge = [...(0, codegen_1.removeSequentialDuplicates)([...dedupedPrefix, ...dedupedSubject])];
    // figure out what belongs to the subject
    const reversedFinalSubject = new Array();
    for (let mCount = (0, linq_1.length)(dedupedMerge) - 1, sCount = (0, linq_1.length)(dedupedSubject) - 1; sCount >= 0 && mCount >= 0; mCount--, sCount--) {
        if (dedupedMerge[mCount] !== dedupedSubject[sCount]) {
            break;
        }
        reversedFinalSubject.push(dedupedMerge.pop());
    }
    // what's left belongs to the prefix
    const finalPrefix = new Array();
    for (const each of dedupedMerge) {
        finalPrefix.push(each);
    }
    return { subjectPrefix: (0, codegen_1.pascalCase)(finalPrefix), subject: (0, codegen_1.pascalCase)(reversedFinalSubject.reverse()) };
}
exports.getDeduplicatedNoun = getDeduplicatedNoun;
async function tweakModel(state) {
    var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
    // get the value 
    const isAzure = await state.getValue('azure', false);
    // without setting snitize-names, isAzure is applied
    const shouldSanitize = await state.getValue('sanitize-names', isAzure);
    // make sure recursively that every details field has csharp
    for (const { index, instance } of linq.visitor(state.model)) {
        if ((index === 'details' || index === 'language') && instance && instance.default && !instance.csharp) {
            instance.csharp = linq.clone(instance.default, false, undefined, undefined, ['schema', 'origin']);
        }
    }
    // dolauli sanitize name
    if (shouldSanitize) {
        for (const operation of (0, linq_1.values)(state.model.commands.operations)) {
            // clean the noun (i.e. subjectPrefix + subject)
            const prevSubjectPrefix = operation.details.csharp.subjectPrefix;
            const prevSubject = operation.details.csharp.subject;
            const dedupedNounParts = getDeduplicatedNoun(operation.details.csharp.subjectPrefix, operation.details.csharp.subject);
            if (prevSubjectPrefix !== dedupedNounParts.subjectPrefix || prevSubject !== dedupedNounParts.subject) {
                const verb = operation.details.csharp.verb;
                const variantName = operation.details.csharp.name;
                const prevCmdletName = getCmdletName(verb, prevSubjectPrefix, prevSubject);
                operation.details.csharp.subjectPrefix = dedupedNounParts.subjectPrefix;
                operation.details.csharp.subject = dedupedNounParts.subject;
                const newCmdletName = getCmdletName(verb, operation.details.csharp.subjectPrefix, operation.details.csharp.subject);
                state.message({
                    Channel: extension_base_1.Channel.Debug,
                    Text: `Sanitized cmdlet-name -> Changed cmdlet-name from ${prevCmdletName} to ${newCmdletName}: {subjectPrefix: ${operation.details.csharp.subjectPrefix}, subject: ${operation.details.csharp.subject}${variantName ? `, variant: ${variantName}}` : '}'}`
                });
            }
            const virtualParameters = [...(0, resolve_conflicts_1.allVirtualParameters)(operation.details.csharp.virtualParameters)];
            for (const parameter of virtualParameters) {
                let prevName = parameter.name;
                const otherParametersNames = (0, linq_1.values)(virtualParameters)
                    .select(each => each.name)
                    .where(name => name !== parameter.name)
                    .toArray();
                // first try to singularize the parameter
                const singularName = (0, name_inferrer_1.singularize)(parameter.name);
                if (prevName != singularName) {
                    parameter.name = singularName;
                    state.message({ Channel: extension_base_1.Channel.Debug, Text: `Sanitized parameter-name -> Changed parameter-name from ${prevName} to singular ${parameter.name} from command ${operation.verb}-${operation.details.csharp.subjectPrefix}${operation.details.csharp.subject}` });
                }
                // save the name again to compare in case it was modified
                prevName = parameter.name;
                // now remove the subject from the beginning of the parameter
                // to reduce naming redundancy, but just for path parameters
                // e.g. get-vm -vmname ---> get-vm -name
                if (((_b = (_a = parameter.origin.protocol) === null || _a === void 0 ? void 0 : _a.http) === null || _b === void 0 ? void 0 : _b.in) === codemodel_1.ParameterLocation.Path) {
                    const sanitizedName = (0, codegen_1.removeProhibitedPrefix)(parameter.name, operation.details.csharp.subject, otherParametersNames);
                    if (prevName !== sanitizedName) {
                        if (parameter.alias === undefined) {
                            parameter.alias = [];
                        }
                        // saved the prev name as alias
                        parameter.alias.push(parameter.name);
                        // change name
                        parameter.name = sanitizedName;
                        state.message({ Channel: extension_base_1.Channel.Debug, Text: `Sanitized parameter-name -> Changed parameter-name from ${prevName} to ${parameter.name} from command ${operation.verb}-${operation.details.csharp.subjectPrefix}${operation.details.csharp.subject}` });
                        state.message({ Channel: extension_base_1.Channel.Debug, Text: `                         -> And, added alias '${prevName}'` });
                    }
                }
            }
        }
        for (const schemaGroup of (0, linq_1.values)(state.model.schemas)) {
            for (const schema of schemaGroup) {
                const virtualProperties = [...(0, resolve_conflicts_1.allVirtualProperties)((_c = schema.language.csharp) === null || _c === void 0 ? void 0 : _c.virtualProperties)];
                for (const property of virtualProperties) {
                    let prevName = property.name;
                    const otherPropertiesNames = (0, linq_1.values)(virtualProperties)
                        .select(each => each.name)
                        .where(name => name !== property.name)
                        .toArray();
                    // first try to singularize the property
                    const singularName = (0, name_inferrer_1.singularize)(property.name);
                    if (prevName != singularName && !otherPropertiesNames.includes(singularName)) {
                        property.name = singularName;
                        state.message({ Channel: extension_base_1.Channel.Debug, Text: `Sanitized property-name -> Changed property-name from ${prevName} to singular ${property.name} from model ${(_d = schema.language.csharp) === null || _d === void 0 ? void 0 : _d.name}` });
                    }
                    // save the name again to compare in case it was modified
                    prevName = property.name;
                    // now remove the model=name from the beginning of the property-name
                    // to reduce naming redundancy
                    const sanitizedName = (0, codegen_1.removeProhibitedPrefix)(property.name, ((_e = schema.language.csharp) === null || _e === void 0 ? void 0 : _e.name) ? (_f = schema.language.csharp) === null || _f === void 0 ? void 0 : _f.name : '', otherPropertiesNames);
                    if (prevName !== sanitizedName) {
                        property.alias = property.alias || [];
                        // saved the prev name as alias
                        property.alias.push(property.name);
                        // change name
                        property.name = sanitizedName;
                        state.message({ Channel: extension_base_1.Channel.Debug, Text: `Sanitized property-name -> Changed property-name from ${prevName} to ${property.name} from model ${(_g = schema.language.csharp) === null || _g === void 0 ? void 0 : _g.name}` });
                        state.message({ Channel: extension_base_1.Channel.Debug, Text: `                        -> And, added alias '${prevName}'` });
                        // update shared properties too
                        if (property.sharedWith) {
                            for (const sharedProperty of property.sharedWith) {
                                if (sharedProperty.name !== sanitizedName) {
                                    state.message({ Channel: extension_base_1.Channel.Debug, Text: `Changing shared property ${sharedProperty.name} to ${sanitizedName}` });
                                    sharedProperty.alias = sharedProperty.alias || [];
                                    sharedProperty.alias.push(sharedProperty.name);
                                    sharedProperty.name = sanitizedName;
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    // do collision detection work.
    for (const command of (0, linq_1.values)(state.model.commands.operations)) {
        const vp = (_h = command.details.csharp) === null || _h === void 0 ? void 0 : _h.virtualParameters;
        if (vp) {
            (0, resolve_conflicts_1.resolveParameterNames)([], vp);
        }
    }
    for (const schemaGroup of (0, linq_1.values)(state.model.schemas)) {
        for (const schema of schemaGroup) {
            const vp = (_j = schema.language.csharp) === null || _j === void 0 ? void 0 : _j.virtualProperties;
            if (vp) {
                (0, resolve_conflicts_1.resolvePropertyNames)(((_k = schema.language.csharp) === null || _k === void 0 ? void 0 : _k.name) ? [(_l = schema.language.csharp) === null || _l === void 0 ? void 0 : _l.name] : [], vp);
            }
        }
    }
    return state.model;
}
exports.tweakModel = tweakModel;
async function namerV2(service) {
    // dolauli add csharp for cmdlets in the command->operation node
    //return processCodeModel(tweakModel, service, 'psnamer');
    //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-psnamer-v2.yaml', content: (0, codegen_1.serialize)(await tweakModel(state)), sourceMap: undefined, artifactType: 'code-model-v4' });
}
exports.namerV2 = namerV2;
//# sourceMappingURL=ps-namer-v2.js.map