"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.applyModifiersV2 = exports.tweakModelForTsp = void 0;
const extension_base_1 = require("@autorest/extension-base");
const codegen_1 = require("@azure-tools/codegen");
const linq_1 = require("@azure-tools/linq");
const command_operation_1 = require("../utils/command-operation");
// import { CommandOperation } from '@azure-tools/codemodel-v3/dist/code-model/command-operation';
const model_state_1 = require("../utils/model-state");
const resolve_conflicts_1 = require("../utils/resolve-conflicts");
let directives = [];
function hasSpecialChars(str) {
    return !/^[a-zA-Z0-9]+$/.test(str);
}
function addParameters(operations, parameters) {
    var _a;
    for (const parameter of (0, linq_1.values)(parameters)) {
        const vp = {};
        vp.name = parameter.name;
        vp.description = parameter.description;
        if (parameter.default) {
            vp.defaultInfo = {};
            vp.defaultInfo = parameter.default;
            // vp.defaultInfo.script = parameter.default.script;
            // vp.defaultInfo.name = parameter.default.name ?? "";
            // vp.defaultInfo.description = parameter.default.description ?? "";
        }
        if (parameter.completer) {
            vp.completerInfo = {};
            vp.completerInfo = parameter.completer;
            // vp.completerInfo.script = parameter.completer.script;
            // vp.completerInfo.name = parameter.completer.name ?? "";
            // vp.completerInfo.description = parameter.completer.description ?? "";
        }
        vp.required = !!parameter.required;
        vp.type = parameter.type;
        for (const operation of (0, linq_1.values)(operations)) {
            (_a = operation.details.csharp.virtualParameters) === null || _a === void 0 ? void 0 : _a.operation.push(vp);
            //operation.details.csharp.virtualParameters?.operation
        }
    }
}
async function addInputPipelines(state, operations, pipeline) {
    const inputHandlers = await state.getValue('input-handlers', []);
    for (const handler of (0, linq_1.values)(pipeline)) {
        if (!inputHandlers.includes(handler.name)) {
            inputHandlers.push(handler.name);
        }
        for (const operation of (0, linq_1.values)(operations)) {
            operation.extensions.inputPipe = (operation.extensions.inputPipe || []);
            operation.extensions.inputPipe.push(handler);
        }
    }
    await state.setValue('input-handlers', inputHandlers);
}
async function addPipelines(state, operations, pipelines) {
    if (pipelines['input-pipeline']) {
        await addInputPipelines(state, operations, pipelines['input-pipeline']);
    }
}
function getFilterError(whereObject, prohibitedFilters, selectionType) {
    let error = '';
    for (const each of (0, linq_1.values)(prohibitedFilters)) {
        if (whereObject[each] !== undefined) {
            error += `Can't filter by ${each} when selecting command. `;
        }
    }
    return error;
}
function getSetError(setObject, prohibitedSetters, selectionType) {
    let error = '';
    for (const each of (0, linq_1.values)(prohibitedSetters)) {
        if (setObject[each] !== undefined) {
            error += `Can't set ${each} when a ${selectionType} is selected. `;
        }
    }
    return error;
}
function isWhereCommandDirective(it) {
    const directive = it;
    const select = directive.select;
    const where = directive.where;
    const set = directive.set;
    if (directive.remove === undefined && where && (where.verb || where.variant || where['parameter-name'] || where.subject || where['subject-prefix'] || directive.hide || select === 'command' || select === 'parameter' || directive['clear-alias'])) {
        const prohibitedFilters = ['model-name', 'property-name', 'enum-name', 'enum-value-name'];
        let error = getFilterError(where, prohibitedFilters, 'command');
        if (set !== undefined) {
            const prohibitedSetters = ['property-name', 'property-description', ' model-name', 'enum-name', 'enum-value-name'];
            error += getSetError(set, prohibitedSetters, 'command');
        }
        if (error) {
            throw Error(`Incorrect Directive: ${JSON.stringify(it, null, 2)}. Reason: ${error}.`);
        }
        return true;
    }
    return false;
}
function isRemoveCommandDirective(it) {
    const directive = it;
    const where = directive.where;
    const remove = directive.remove;
    if (where && remove && (where.subject || where.verb || where.variant || where['subject-prefix'] || where['parameter-name'] || directive.select === 'command') && directive.select !== 'parameter') {
        return true;
    }
    return false;
}
function isWhereModelDirective(it) {
    const directive = it;
    const where = directive.where;
    const set = directive.set;
    if (where && set && (where['model-name'] || where['model-fullname'] || where['model-namespace'] || where['property-name'] || directive.select === 'model')) {
        const prohibitedFilters = ['enum-name', 'enum-value-name', 'subject', 'subject-prefix', 'verb', 'variant', 'parameter-name'];
        let error = getFilterError(where, prohibitedFilters, 'enum');
        const prohibitedSetters = ['enum-name', 'enum-value-name', 'subject', 'subject-prefix', 'verb', 'variant', 'parameter-name', 'parameter-description', 'command-description', 'completer', 'default'];
        error += getSetError(set, prohibitedSetters, 'enum');
        const modelSelectNameConflict = [];
        let modelSelectNameType = '';
        if (where['model-name']) {
            modelSelectNameType = 'model-name';
            if (where['model-fullname']) {
                modelSelectNameConflict.push('model-fullname');
            }
            if (where['model-namespace']) {
                modelSelectNameConflict.push('model-namespace');
            }
        }
        else if (where['model-fullname']) {
            modelSelectNameType = 'model-fullname';
            if (where['model-name']) {
                modelSelectNameConflict.push('model-name');
            }
            if (where['model-namespace']) {
                modelSelectNameConflict.push('model-namespace');
            }
        }
        else if (where['model-namespace']) {
            modelSelectNameType = 'model-namespace';
            if (where['model-fullname']) {
                modelSelectNameConflict.push('model-fullname');
            }
            if (where['model-name']) {
                modelSelectNameConflict.push('model-name');
            }
        }
        if ((0, linq_1.length)(modelSelectNameConflict) > 0) {
            error += `Can't select ${modelSelectNameType} and ${modelSelectNameConflict} at the same time`;
        }
        if (error) {
            throw Error(`Incorrect Directive: ${JSON.stringify(it, null, 2)}.Reason: ${error}.`);
        }
        return true;
    }
    return false;
}
function isWhereEnumDirective(it) {
    const directive = it;
    const where = directive.where;
    const set = directive.set;
    if (where && set && (where['enum-name'] || where['enum-value-name'] || directive.select === 'enum')) {
        const prohibitedFilters = ['model-name', 'property-name', 'subject', 'subject-prefix', 'verb', 'variant', 'parameter-name'];
        let error = getFilterError(where, prohibitedFilters, 'enum');
        const prohibitedSetters = ['model-name', 'property-name', 'subject', 'subject-prefix', 'verb', 'variant', 'parameter-name', 'parameter-description', 'command-description', 'completer', 'default'];
        error += getSetError(set, prohibitedSetters, 'enum');
        if (error) {
            throw Error(`Incorrect Directive: ${JSON.stringify(it, null, 2)}. Reason: ${error}.`);
        }
        return true;
    }
    return false;
}
async function tweakModelForTsp(state) {
    const allDirectives = await state.service.getValue('directive');
    directives = (0, linq_1.values)(allDirectives)
        .where(directive => isWhereCommandDirective(directive) || isWhereModelDirective(directive) || isWhereEnumDirective(directive) || isRemoveCommandDirective(directive))
        .toArray();
    return await tweakModel(state);
}
exports.tweakModelForTsp = tweakModelForTsp;
async function tweakModel(state) {
    var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
    const isAzure = await state.getValue('azure', false) || await state.getValue('azure-arm', false);
    const removePecAndPlr = !await state.getValue('keep-pec-and-plr', isAzure ? false : true);
    if (removePecAndPlr) {
        const PecOrPlrRegex = '^PrivateEndpointConnection$|^PrivateLinkResource$';
        let operationsToRemoveKeys = new Set((0, linq_1.items)(state.model.commands.operations)
            .select(operation => operation.key)
            .toArray());
        operationsToRemoveKeys = new Set((0, linq_1.items)(state.model.commands.operations)
            .where(operation => !!`${operation.value.details.csharp.subject}`.match(PecOrPlrRegex) && operationsToRemoveKeys.has(operation.key))
            .select(operation => operation.key)
            .toArray());
        for (const key of (0, linq_1.values)(operationsToRemoveKeys)) {
            const operationInfo = state.model.commands.operations[key].details.csharp;
            state.message({
                Channel: extension_base_1.Channel.Debug, Text: `[DIRECTIVE] Removed command ${operationInfo.verb}-${operationInfo.subjectPrefix}${operationInfo.subject}${operationInfo.name ? `_${operationInfo.name}` : ''}`
            });
            delete state.model.commands.operations[key];
        }
    }
    // only look at directives without the `transform` node.
    // dolauli for directives with transform are implemented in autorest core
    for (const directive of directives.filter(each => !each.transform)) {
        const getPatternToMatch = (selector) => {
            return selector ? !hasSpecialChars(selector) ? new RegExp(`^${selector}$`, 'gi') : new RegExp(selector, 'gi') : undefined;
        };
        if (isWhereCommandDirective(directive)) {
            const selectType = directive.select;
            const clearAlias = directive['clear-alias'];
            const subjectRegex = getPatternToMatch(directive.where['subject']);
            const subjectPrefixRegex = getPatternToMatch(directive.where['subject-prefix']);
            const verbRegex = getPatternToMatch(directive.where.verb);
            const variantRegex = getPatternToMatch(directive.where.variant);
            const parameterRegex = getPatternToMatch(directive.where['parameter-name']);
            const SupportsShouldProcess = (directive.set !== undefined) ? directive.set['suppress-should-process'] : undefined;
            const alias = (directive.set !== undefined) ?
                (directive.set.alias !== undefined) ?
                    !Array.isArray(directive.set.alias) ?
                        [directive.set.alias]
                        : directive.set.alias
                    : undefined
                : undefined;
            const breakingChange = (directive.set !== undefined) ? directive.set['breaking-change'] : undefined;
            const previewAnnouncement = (directive.set !== undefined) ? directive.set['preview-announcement'] : undefined;
            const subjectReplacer = (directive.set !== undefined) ? directive.set['subject'] : undefined;
            const subjectPrefixReplacer = (directive.set !== undefined) ? directive.set['subject-prefix'] : undefined;
            const verbReplacer = (directive.set !== undefined) ? directive.set.verb : undefined;
            const variantReplacer = (directive.set !== undefined) ? directive.set.variant : undefined;
            const parameterReplacer = (directive.set !== undefined) ? directive.set['parameter-name'] : undefined;
            const paramDescriptionReplacer = (directive.set !== undefined) ? directive.set['parameter-description'] : undefined;
            const commandDescriptionReplacer = (directive.set !== undefined) ? directive.set['command-description'] : undefined;
            const paramCompleterReplacer = (directive.set !== undefined) ? directive.set['completer'] : undefined;
            const paramDefaultReplacer = (directive.set !== undefined) ? directive.set['default'] : undefined;
            const cliensidePagination = (directive.set !== undefined) ? directive.set['clientside-pagination'] : undefined;
            // select all operations
            let operations = (0, linq_1.values)(state.model.commands.operations).toArray();
            if (subjectRegex) {
                operations = (0, linq_1.values)(operations)
                    .where(operation => !!`${operation.details.csharp.subject}`.match(subjectRegex))
                    .toArray();
            }
            if (subjectPrefixRegex) {
                operations = (0, linq_1.values)(operations)
                    .where(operation => !!`${operation.details.csharp.subjectPrefix}`.match(subjectPrefixRegex))
                    .toArray();
            }
            if (verbRegex) {
                operations = (0, linq_1.values)(operations)
                    .where(operation => !!`${operation.details.csharp.verb}`.match(verbRegex))
                    .toArray();
            }
            if (variantRegex) {
                operations = (0, linq_1.values)(operations)
                    .where(operation => !!`${operation.details.csharp.name}`.match(variantRegex))
                    .toArray();
            }
            if (parameterRegex && selectType === 'command') {
                operations = (0, linq_1.values)(operations)
                    .where(operation => (0, linq_1.values)((0, resolve_conflicts_1.allVirtualParameters)(operation.details.csharp.virtualParameters))
                    .any(parameter => !!`${parameter.name}`.match(parameterRegex)))
                    .toArray();
            }
            if (directive.add !== undefined && directive.add.parameters !== undefined) {
                // we need to handle adding parameters before other parameter related directives, e.g. adding breaking changes
                addParameters(operations, directive.add.parameters);
            }
            if (directive.add !== undefined && directive.add.pipelines !== undefined) {
                // we need to handle adding parameters before other parameter related directives, e.g. adding breaking changes
                await addPipelines(state, operations, directive.add.pipelines);
            }
            if (parameterRegex && (selectType === undefined || selectType === 'parameter')) {
                const parameters = (0, linq_1.values)(operations)
                    .selectMany(operation => (0, resolve_conflicts_1.allVirtualParameters)(operation.details.csharp.virtualParameters))
                    .where(parameter => !!`${parameter.name}`.match(parameterRegex))
                    .toArray();
                for (const p of (0, linq_1.values)(parameters)) {
                    const parameter = p;
                    const prevName = parameter.name;
                    parameter.name = parameterReplacer ? parameterRegex ? parameter.name.replace(parameterRegex, parameterReplacer) : parameterReplacer : parameter.name;
                    parameter.description = paramDescriptionReplacer ? paramDescriptionReplacer : parameter.description;
                    parameter.completerInfo = paramCompleterReplacer ? paramCompleterReplacer : parameter.completerInfo;
                    parameter.defaultInfo = paramDefaultReplacer ? paramDefaultReplacer : parameter.defaultInfo;
                    // handle parameter breaking change for parameter
                    if (breakingChange) {
                        parameter.breakingChange = {};
                        parameter.breakingChange.parameterName = parameter.name;
                        parameter.breakingChange.replacement = (breakingChange && breakingChange['replacement-parameter']) ? breakingChange['replacement-parameter'] : undefined;
                        parameter.breakingChange.isBecomingMandatory = (breakingChange && breakingChange['become-mandatory']) ? breakingChange['become-mandatory'] : undefined;
                        parameter.breakingChange.oldParamaterType = (breakingChange && breakingChange['old-parameter-type']) ? breakingChange['old-parameter-type'] : undefined;
                        parameter.breakingChange.newParameterType = (breakingChange && breakingChange['new-parameter-type']) ? breakingChange['new-parameter-type'] : undefined;
                        parameter.breakingChange.deprecateByVersion = (breakingChange && breakingChange['deprecated-by-version']) ? breakingChange['deprecated-by-version'] : undefined;
                        parameter.breakingChange.deprecateByAzVersion = (breakingChange && breakingChange['deprecated-by-azversion']) ? breakingChange['deprecated-by-azversion'] : undefined;
                        parameter.breakingChange.changeInEfectByDate = (breakingChange && breakingChange['change-effective-date']) ? breakingChange['change-effective-date'] : undefined;
                        parameter.breakingChange.changeDescription = (breakingChange && breakingChange['change-description']) ? breakingChange['change-description'] : undefined;
                    }
                    // handle preview message for parameter
                    if (previewAnnouncement) {
                        parameter.previewAnnouncement = {};
                        parameter.previewAnnouncement.previewMessage = (_a = previewAnnouncement['preview-message']) !== null && _a !== void 0 ? _a : '';
                        parameter.previewAnnouncement.estimatedGaDate = (_b = previewAnnouncement['estimated-ga-date']) !== null && _b !== void 0 ? _b : undefined;
                    }
                    if (clearAlias) {
                        parameter.alias = [];
                        state.message({
                            Channel: extension_base_1.Channel.Debug, Text: `[DIRECTIVE] Cleared aliases from parameter ${parameter.name}.`
                        });
                    }
                    // handle hiding parameters
                    if (directive.hide) {
                        if (p.required && !paramDefaultReplacer) {
                            state.warning(`Unless you will customize the parameter through input-pipeline directive, you probably need to add a default value when hiding the mandatory parameter ${p.name}.
See https://github.com/Azure/autorest.powershell/blob/main/docs/directives.md#default-values`, [], {});
                        }
                        p.required = false;
                        p.hidden = true;
                        state.message({
                            Channel: extension_base_1.Channel.Debug,
                            Text: `[DIRECTIVE] Hide parameter ${p.name}.`,
                        });
                    }
                    if (alias) {
                        const parsedAlias = new Array();
                        for (const each of (0, linq_1.values)(alias)) {
                            parsedAlias.push(hasSpecialChars(each) ? prevName.replace(parameterRegex, each) : each);
                        }
                        parameter.alias = [...new Set((0, linq_1.values)(parameter.alias, parsedAlias).toArray())];
                        state.message({
                            Channel: extension_base_1.Channel.Debug, Text: `[DIRECTIVE] Added alias ${parsedAlias} to parameter ${parameter.name}.`
                        });
                    }
                    if (parameterReplacer) {
                        state.message({
                            Channel: extension_base_1.Channel.Debug, Text: `[DIRECTIVE] Changed parameter-name from ${prevName} to ${parameter.name}.`
                        });
                    }
                    if (paramDescriptionReplacer) {
                        state.message({
                            Channel: extension_base_1.Channel.Debug, Text: `[DIRECTIVE] Set parameter-description from parameter ${parameter.name}.`
                        });
                    }
                }
            }
            else if (operations) {
                for (const operation of (0, linq_1.values)(operations)) {
                    // set suppress-should-process
                    if (!!SupportsShouldProcess && (0, command_operation_1.isWritableCmdlet)(operation)) {
                        operation.details.csharp.supportShouldProcess = SupportsShouldProcess;
                    }
                    const getCmdletName = (verb, subjectPrefix, subject, variantName) => {
                        return `${verb}-${subjectPrefix}${subject}${variantName ? `_${variantName}` : ''}`;
                    };
                    const prevSubject = operation.details.csharp.subject;
                    const prevSubjectPrefix = operation.details.csharp.subjectPrefix;
                    const prevVerb = operation.details.csharp.verb;
                    const prevVariantName = operation.details.csharp.name;
                    const oldCommandName = getCmdletName(prevVerb, prevSubjectPrefix, prevSubject, prevVariantName);
                    // set values
                    operation.details.csharp.subject = subjectReplacer ? subjectRegex ? prevSubject.replace(subjectRegex, subjectReplacer) : subjectReplacer : prevSubject;
                    operation.details.csharp.subjectPrefix = subjectPrefixReplacer !== undefined ? subjectPrefixRegex ? prevSubjectPrefix.replace(subjectPrefixRegex, subjectPrefixReplacer) : subjectPrefixReplacer : prevSubjectPrefix;
                    operation.details.csharp.verb = verbReplacer ? verbRegex ? prevVerb.replace(verbRegex, verbReplacer) : verbReplacer : prevVerb;
                    operation.details.csharp.name = variantReplacer ? variantRegex ? prevVariantName.replace(variantRegex, variantReplacer) : variantReplacer : prevVariantName;
                    operation.details.csharp.hidden = (directive.hide !== undefined) ? !!directive.hide : operation.details.csharp.hidden;
                    operation.details.csharp.description = commandDescriptionReplacer ? commandDescriptionReplacer : operation.details.csharp.description;
                    const newSubject = operation.details.csharp.subject;
                    const newSubjectPrefix = operation.details.csharp.subjectPrefix;
                    const newVerb = operation.details.csharp.verb;
                    const newVariantName = operation.details.csharp.name;
                    const newCommandName = getCmdletName(newVerb, newSubjectPrefix, newSubject, newVariantName);
                    if (cliensidePagination) {
                        operation.details.csharp.clientsidePagination = cliensidePagination;
                    }
                    if (breakingChange) {
                        operation.details.csharp.breakingChange = operation.details.csharp.breakingChange ? operation.details.csharp.breakingChange : {};
                        if (variantRegex) {
                            // handle parameter breaking change for variant
                            operation.details.csharp.breakingChange.variant = {};
                            operation.details.csharp.breakingChange.variant.deprecateByVersion = (breakingChange && breakingChange['deprecated-by-version']) ? breakingChange['deprecated-by-version'] : undefined;
                            operation.details.csharp.breakingChange.variant.deprecateByAzVersion = (breakingChange && breakingChange['deprecated-by-azversion']) ? breakingChange['deprecated-by-azversion'] : undefined;
                            operation.details.csharp.breakingChange.variant.changeInEfectByDate = (breakingChange && breakingChange['change-effective-date']) ? breakingChange['change-effective-date'] : undefined;
                            operation.details.csharp.breakingChange.variant.changeDescription = (breakingChange && breakingChange['change-description']) ? breakingChange['change-description'] : undefined;
                            operation.details.csharp.breakingChange.variant.name = newVariantName;
                        }
                        else {
                            //handle breaking change for output type
                            if (breakingChange['new-output-properties']) {
                                operation.details.csharp.breakingChange.output = {};
                                operation.details.csharp.breakingChange.output.deprecatedCmdLetOutputType = breakingChange['deprecated-cmdlet-output-type'];
                                operation.details.csharp.breakingChange.output.replacement = (breakingChange && breakingChange['replacement-cmdlet-output-type']) ? breakingChange['replacement-cmdlet-output-type'] : undefined;
                                operation.details.csharp.breakingChange.output.deprecatedOutputProperties = (breakingChange && breakingChange['deprecated-output-properties']) ? breakingChange['deprecated-output-properties'] : undefined;
                                operation.details.csharp.breakingChange.output.newOutputProperties = (breakingChange && breakingChange['new-output-properties']) ? breakingChange['new-output-properties'] : undefined;
                                operation.details.csharp.breakingChange.output.deprecateByVersion = (breakingChange && breakingChange['deprecated-by-version']) ? breakingChange['deprecated-by-version'] : undefined;
                                operation.details.csharp.breakingChange.output.deprecateByAzVersion = (breakingChange && breakingChange['deprecated-by-azversion']) ? breakingChange['deprecated-by-azversion'] : undefined;
                                operation.details.csharp.breakingChange.output.changeInEfectByDate = (breakingChange && breakingChange['change-effective-date']) ? breakingChange['change-effective-date'] : undefined;
                                operation.details.csharp.breakingChange.output.changeDescription = (breakingChange && breakingChange['change-description']) ? breakingChange['change-description'] : undefined;
                            }
                            else {
                                // handle parameter breaking change for cmdlet
                                operation.details.csharp.breakingChange.cmdlet = {};
                                operation.details.csharp.breakingChange.cmdlet.replacement = (breakingChange && breakingChange['replacement-cmdlet']) ? breakingChange['replacement-cmdlet'] : undefined;
                                if (operation.details.csharp.breakingChange.cmdlet.replacement && operation.details.csharp.breakingChange.cmdlet.replacement.startsWith('$.')) {
                                    operation.details.csharp.breakingChange.cmdlet.replacement = (0, codegen_1.safeEval)(operation.details.csharp.breakingChange.cmdlet.replacement.replace('$', `"${newCommandName.split('_')[0]}"`));
                                }
                                operation.details.csharp.breakingChange.cmdlet.deprecateByVersion = (breakingChange && breakingChange['deprecated-by-version']) ? breakingChange['deprecated-by-version'] : undefined;
                                operation.details.csharp.breakingChange.cmdlet.deprecateByAzVersion = (breakingChange && breakingChange['deprecated-by-azversion']) ? breakingChange['deprecated-by-azversion'] : undefined;
                                operation.details.csharp.breakingChange.cmdlet.changeInEfectByDate = (breakingChange && breakingChange['change-effective-date']) ? breakingChange['change-effective-date'] : undefined;
                                operation.details.csharp.breakingChange.cmdlet.changeDescription = (breakingChange && breakingChange['change-description']) ? breakingChange['change-description'] : undefined;
                                operation.details.csharp.breakingChange.cmdlet.name = newCommandName.split('_')[0];
                            }
                        }
                    }
                    // handle preview message for cmdlet
                    if (previewAnnouncement) {
                        operation.details.csharp.previewAnnouncement = {};
                        operation.details.csharp.previewAnnouncement.previewMessage = (_c = previewAnnouncement['preview-message']) !== null && _c !== void 0 ? _c : '';
                        operation.details.csharp.previewAnnouncement.estimatedGaDate = (_d = previewAnnouncement['estimated-ga-date']) !== null && _d !== void 0 ? _d : undefined;
                    }
                    // just the subject prefix can be an empty string
                    if (subjectPrefixReplacer !== undefined || subjectReplacer || verbReplacer || variantReplacer) {
                        const modificationMessage = `[DIRECTIVE] Changed command from ${oldCommandName} to ${newCommandName}. `;
                        state.message({
                            Channel: extension_base_1.Channel.Debug, Text: modificationMessage
                        });
                    }
                    if (clearAlias) {
                        operation.details.csharp.alias = [];
                        state.message({
                            Channel: extension_base_1.Channel.Debug, Text: `[DIRECTIVE] Cleared aliases from command ${newCommandName}.`
                        });
                    }
                    if (alias) {
                        const getParsedAlias = (rawAlias) => {
                            return rawAlias.replace('${verb}', operation.details.csharp.verb)
                                .replace('${subject-prefix}', operation.details.csharp.subjectPrefix)
                                .replace('${subject}', operation.details.csharp.subject)
                                .replace('${variant}', operation.details.csharp.name);
                        };
                        const parsedAlias = new Array();
                        for (const each of (0, linq_1.values)(alias)) {
                            parsedAlias.push(getParsedAlias(each));
                        }
                        operation.details.csharp.alias = [...new Set((0, linq_1.values)(operation.details.csharp.alias, parsedAlias).toArray())];
                        state.message({
                            Channel: extension_base_1.Channel.Debug, Text: `[DIRECTIVE] Added alias ${parsedAlias} to command ${newCommandName}.`
                        });
                    }
                }
            }
            continue;
        }
        if (isWhereModelDirective(directive)) {
            const selectType = directive.select;
            const modelNameRegex = getPatternToMatch(directive.where['model-name']);
            const modelFullNameRegex = getPatternToMatch(directive.where['model-fullname']);
            const modelNamespaceRegex = getPatternToMatch(directive.where['model-namespace']);
            const propertyNameRegex = getPatternToMatch(directive.where['property-name']);
            const modelNameReplacer = directive.set['model-name'];
            const propertyNameReplacer = directive.set['property-name'];
            const propertyDescriptionReplacer = directive.set['property-description'];
            const formatTable = directive.set['format-table'];
            const suppressFormat = directive.set['suppress-format'];
            // select all models
            let models = [...(_e = state.model.schemas.objects) !== null && _e !== void 0 ? _e : []];
            // let models = values(state.model.schemas).toArray();
            if (modelNameRegex) {
                models = (0, linq_1.values)(models)
                    .where(model => { var _a; return !!`${(_a = model.language.csharp) === null || _a === void 0 ? void 0 : _a.name}`.match(modelNameRegex); })
                    .toArray();
            }
            if (modelFullNameRegex) {
                models = (0, linq_1.values)(models)
                    .where(model => { var _a; return !!`${(_a = model.language.csharp) === null || _a === void 0 ? void 0 : _a.fullname}`.match(modelFullNameRegex); })
                    .toArray();
            }
            if (modelNamespaceRegex) {
                models = (0, linq_1.values)(models)
                    .where(model => { var _a; return !!`${(_a = model.language.csharp) === null || _a === void 0 ? void 0 : _a.namespace}`.match(modelNamespaceRegex); })
                    .toArray();
            }
            if (propertyNameRegex && selectType === 'model') {
                models = (0, linq_1.values)(models)
                    .where(model => {
                    var _a;
                    return (0, linq_1.values)((0, resolve_conflicts_1.allVirtualProperties)((_a = model.language.csharp) === null || _a === void 0 ? void 0 : _a.virtualProperties))
                        .any(property => !!`${property.name}`.match(propertyNameRegex));
                })
                    .toArray();
            }
            if (propertyNameRegex && (selectType === undefined || selectType === 'property')) {
                const properties = (0, linq_1.values)(models)
                    .selectMany(model => { var _a; return (0, resolve_conflicts_1.allVirtualProperties)((_a = model.language.csharp) === null || _a === void 0 ? void 0 : _a.virtualProperties); })
                    .where(property => !!`${property.name}`.match(propertyNameRegex))
                    .toArray();
                for (const property of (0, linq_1.values)(properties)) {
                    const prevName = property.name;
                    property.name = propertyNameReplacer ? propertyNameRegex ? property.name.replace(propertyNameRegex, propertyNameReplacer) : propertyNameReplacer : property.name;
                    property.description = propertyDescriptionReplacer ? propertyDescriptionReplacer : property.description;
                    if (!property.name) {
                        state.message({ Channel: extension_base_1.Channel.Error, Text: `Directive '${directive.where['model-name'] || directive.where['model-fullname']}/${directive.where['property-name']}' attempted to change '${prevName}' to '' ` });
                    }
                    if (propertyNameRegex) {
                        state.message({
                            Channel: extension_base_1.Channel.Debug, Text: `[DIRECTIVE] Changed property-name from ${prevName} to ${property.name}.`
                        });
                    }
                }
            }
            else if (models) {
                for (const model of (0, linq_1.values)(models)) {
                    if (suppressFormat && model.language.csharp) {
                        model.language.csharp.suppressFormat = true;
                    }
                    if (formatTable !== undefined && !suppressFormat) {
                        const resourceGroupFormat = {};
                        let ResourceGroupNameInclude = false;
                        const resourceGroupName = 'resourcegroupname';
                        const properties = (0, resolve_conflicts_1.allVirtualProperties)((_f = model.language.csharp) === null || _f === void 0 ? void 0 : _f.virtualProperties);
                        const propertiesToExclude = formatTable['exclude-properties'];
                        const propertiesToInclude = formatTable.properties;
                        const labels = formatTable.labels;
                        const widths = formatTable.width;
                        for (const property of (0, linq_1.values)(properties)) {
                            if (property.name.toLowerCase() == resourceGroupName) {
                                ResourceGroupNameInclude = true;
                            }
                        }
                        if (labels) {
                            const parsedLabels = new linq_1.Dictionary();
                            for (const label of (0, linq_1.items)(labels)) {
                                parsedLabels[label.key.toLowerCase()] = label.value;
                            }
                            for (const property of (0, linq_1.values)(properties)) {
                                if (Object.keys(parsedLabels).includes(property.name.toLowerCase())) {
                                    if (property.format === undefined) {
                                        property.format = {};
                                    }
                                    property.format.label = parsedLabels[property.name.toLowerCase()];
                                }
                            }
                            if (!ResourceGroupNameInclude) {
                                resourceGroupFormat.label = parsedLabels[resourceGroupName];
                            }
                        }
                        if (widths) {
                            const parsedWidths = new linq_1.Dictionary();
                            for (const w of (0, linq_1.items)(widths)) {
                                parsedWidths[w.key.toLowerCase()] = w.value;
                            }
                            for (const property of (0, linq_1.values)(properties)) {
                                if (Object.keys(parsedWidths).includes(property.name.toLowerCase())) {
                                    if (property.format === undefined) {
                                        property.format = {};
                                    }
                                    property.format.width = parsedWidths[property.name.toLowerCase()];
                                }
                            }
                            if (!ResourceGroupNameInclude) {
                                resourceGroupFormat.width = parsedWidths[resourceGroupName];
                            }
                        }
                        if (propertiesToInclude) {
                            const indexes = new linq_1.Dictionary();
                            for (const item of (0, linq_1.items)(propertiesToInclude)) {
                                indexes[item.value.toLowerCase()] = item.key;
                            }
                            let ResourceGroupNameInclude = false;
                            for (const property of (0, linq_1.values)(properties)) {
                                if (property.name.toLowerCase() == 'resourcegroupname') {
                                    ResourceGroupNameInclude = true;
                                }
                                if (propertiesToInclude.map(x => x.toLowerCase()).includes(property.name.toLowerCase())) {
                                    if (property.format === undefined) {
                                        property.format = {};
                                    }
                                    property.format.index = indexes[property.name.toLowerCase()];
                                }
                                else {
                                    property.format = { suppressFormat: true };
                                }
                            }
                            if (!ResourceGroupNameInclude) {
                                resourceGroupFormat.index = indexes[resourceGroupName];
                                if (indexes[resourceGroupName] == undefined) {
                                    resourceGroupFormat.suppressFormat = true;
                                }
                            }
                        }
                        if (propertiesToExclude) {
                            for (const property of (0, linq_1.values)(properties)) {
                                if (propertiesToExclude.map(x => x.toLowerCase()).includes(property.name.toLowerCase())) {
                                    property.format = { suppressFormat: true };
                                }
                            }
                            if (!ResourceGroupNameInclude && propertiesToExclude.map(x => x.toLowerCase()).includes(resourceGroupName)) {
                                resourceGroupFormat.suppressFormat = true;
                            }
                        }
                        if (!ResourceGroupNameInclude && await state.getValue('azure', false)) {
                            // Keep the format info for ResourceGroupName and we will need it later if resourcegroup-append is set
                            const formats = await state.getValue('formats', {});
                            formats[`${(_g = model.language.csharp) === null || _g === void 0 ? void 0 : _g.name}`] = resourceGroupFormat;
                            await state.setValue('formats', formats);
                        }
                    }
                    const prevName = (_h = model.language.csharp) === null || _h === void 0 ? void 0 : _h.name;
                    if (model.language.csharp) {
                        model.language.csharp.name = modelNameReplacer ? modelNameRegex ? model.language.csharp.name.replace(modelNameRegex, modelNameReplacer) : modelNameReplacer : model.language.csharp.name;
                    }
                    state.message({
                        Channel: extension_base_1.Channel.Debug, Text: `[DIRECTIVE] Changed model-name from ${prevName} to ${(_j = model.language.csharp) === null || _j === void 0 ? void 0 : _j.name}.`
                    });
                }
            }
            continue;
        }
        if (isWhereEnumDirective(directive)) {
            const enumNameRegex = getPatternToMatch(directive.where['enum-name']);
            const enumValueNameRegex = getPatternToMatch(directive.where['enum-value-name']);
            const enumNameReplacer = directive.set['enum-name'];
            const enumValueNameReplacer = directive.set['enum-value-name'];
            let enums = [...(_k = state.model.schemas.sealedChoices) !== null && _k !== void 0 ? _k : [], ...(_l = state.model.schemas.choices) !== null && _l !== void 0 ? _l : []];
            // let enums = values(state.model.schemas)
            //   .where(each => each.details.csharp.enum !== undefined)
            //   .toArray();
            if (enumNameRegex) {
                enums = (0, linq_1.values)(enums)
                    .where(each => { var _a; return !!`${(_a = each.language.csharp) === null || _a === void 0 ? void 0 : _a.name}`.match(enumNameRegex); })
                    .toArray();
            }
            if (enumValueNameRegex) {
                const enumsValues = (0, linq_1.values)(enums)
                    .selectMany(each => { var _a; return ((_a = each.language.csharp) === null || _a === void 0 ? void 0 : _a.enum) ? each.language.csharp.enum.values : []; })
                    .where(each => !!`${each.name}`.match(enumValueNameRegex))
                    .toArray();
                for (const enumValue of (0, linq_1.values)(enumsValues)) {
                    const prevName = enumValue.name;
                    enumValue.name = enumValueNameReplacer ? enumNameRegex ? enumValue.name.replace(enumValueNameRegex, enumValueNameReplacer) : enumValueNameReplacer : prevName;
                    if (enumValueNameRegex) {
                        const enumNames = (0, linq_1.values)(enums)
                            .select(each => { var _a; return (_a = each.language.csharp) === null || _a === void 0 ? void 0 : _a.name; })
                            .toArray();
                        state.message({
                            Channel: extension_base_1.Channel.Debug, Text: `[DIRECTIVE] Changed enum-value-name from ${prevName} to ${enumValue.name}. Enum: ${JSON.stringify(enumNames, null, 2)}`
                        });
                    }
                }
            }
            else {
                for (const each of (0, linq_1.values)(enums)) {
                    const prevName = (_o = (_m = each.language.csharp) === null || _m === void 0 ? void 0 : _m.name) !== null && _o !== void 0 ? _o : '';
                    if (each.language.csharp) {
                        each.language.csharp.name = enumNameReplacer ? enumNameRegex ? each.language.csharp.name.replace(enumNameRegex, enumNameReplacer) : enumNameReplacer : prevName;
                        state.message({
                            Channel: extension_base_1.Channel.Debug, Text: `[DIRECTIVE] Changed enum-name from ${prevName} to ${(_p = each.language.csharp) === null || _p === void 0 ? void 0 : _p.name}.`
                        });
                    }
                }
            }
            continue;
        }
        if (isRemoveCommandDirective(directive)) {
            const selectType = directive.select;
            const subjectRegex = getPatternToMatch(directive.where.subject);
            const subjectPrefixRegex = getPatternToMatch(directive.where['subject-prefix']);
            const verbRegex = getPatternToMatch(directive.where.verb);
            const variantRegex = getPatternToMatch(directive.where.variant);
            const parameterRegex = getPatternToMatch(directive.where['parameter-name']);
            if (subjectRegex || subjectPrefixRegex || verbRegex || variantRegex || (parameterRegex && selectType === 'command')) {
                // select all operations first then reduce by finding the intersection with selectors
                let operationsToRemoveKeys = new Set((0, linq_1.items)(state.model.commands.operations)
                    .select(operation => operation.key)
                    .toArray());
                if (subjectRegex) {
                    operationsToRemoveKeys = new Set((0, linq_1.items)(state.model.commands.operations)
                        .where(operation => !!`${operation.value.details.csharp.subject}`.match(subjectRegex) && operationsToRemoveKeys.has(operation.key))
                        .select(operation => operation.key)
                        .toArray());
                }
                if (subjectPrefixRegex && operationsToRemoveKeys.size > 0) {
                    operationsToRemoveKeys = new Set((0, linq_1.items)(state.model.commands.operations)
                        .where(operation => !!`${operation.value.details.csharp.subjectPrefix}`.match(subjectPrefixRegex) && operationsToRemoveKeys.has(operation.key))
                        .select(operation => operation.key)
                        .toArray());
                }
                if (verbRegex && operationsToRemoveKeys.size > 0) {
                    operationsToRemoveKeys = new Set((0, linq_1.items)(state.model.commands.operations)
                        .where(operation => !!`${operation.value.details.csharp.verb}`.match(verbRegex) && operationsToRemoveKeys.has(operation.key))
                        .select(operation => operation.key)
                        .toArray());
                }
                if (variantRegex && operationsToRemoveKeys.size > 0) {
                    operationsToRemoveKeys = new Set((0, linq_1.items)(state.model.commands.operations)
                        .where(operation => !!`${operation.value.details.csharp.name}`.match(variantRegex) && operationsToRemoveKeys.has(operation.key))
                        .select(operation => operation.key)
                        .toArray());
                }
                if (parameterRegex && selectType === 'command' && operationsToRemoveKeys.size > 0) {
                    operationsToRemoveKeys = new Set((0, linq_1.items)(state.model.commands.operations)
                        .where(operation => (0, linq_1.values)((0, resolve_conflicts_1.allVirtualParameters)(operation.value.details.csharp.virtualParameters))
                        .any(parameter => !!`${parameter.name}`.match(parameterRegex)))
                        .where(operation => operationsToRemoveKeys.has(operation.key))
                        .select(operation => operation.key)
                        .toArray());
                }
                for (const key of (0, linq_1.values)(operationsToRemoveKeys)) {
                    const operationInfo = state.model.commands.operations[key].details.csharp;
                    state.message({
                        Channel: extension_base_1.Channel.Debug, Text: `[DIRECTIVE] Removed command ${operationInfo.verb}-${operationInfo.subjectPrefix}${operationInfo.subject}${operationInfo.name ? `_${operationInfo.name}` : ''}`
                    });
                    delete state.model.commands.operations[key];
                }
            }
            continue;
        }
    }
    const operationIdentities = new Set();
    for (const operation of (0, linq_1.values)(state.model.commands.operations)) {
        const details = operation.details.csharp;
        let fname = `${details.verb} -${details.subjectPrefix}${details.subject} -${details.name} `;
        let n = 1;
        while (operationIdentities.has(fname)) {
            details.name = (0, codegen_1.pascalCase)(`${details.name.replace(/\d*$/g, '')} ${n++}`);
            fname = (0, codegen_1.pascalCase)(`${details.verb} -${details.subjectPrefix}${details.subject} -${details.name}`);
        }
        operationIdentities.add(fname);
    }
    return state.model;
}
async function applyModifiersV2(service) {
    // dolauli implement directives
    const allDirectives = await service.getValue('directive');
    directives = (0, linq_1.values)(allDirectives)
        // .select(directive => directive)
        .where(directive => isWhereCommandDirective(directive) || isWhereModelDirective(directive) || isWhereEnumDirective(directive) || isRemoveCommandDirective(directive))
        .toArray();
    const state = await new model_state_1.ModelState(service).init();
    const result = await tweakModel(state);
    await service.writeFile({ filename: 'code-model-v4-modifiers-v2.yaml', content: (0, codegen_1.serialize)(result), sourceMap: undefined, artifactType: 'code-model-v4' });
}
exports.applyModifiersV2 = applyModifiersV2;
//# sourceMappingURL=modifiers-v2.js.map