/*!
 * Copyright (C) Microsoft Corporation. All rights reserved.
 */
import { VariableDeclarationKind } from 'ts-morph';
import { formatEnumLabel, generateLookupValuePropertyName, generateODataBindPropertyName, generateOptionsetEnumName, getDataverseLookupColumns, getDataverseMultiSelectPicklistFields, hasOptionsetData, iterateDataverseProperties, } from '../../shared/dataverseUtils.js';
import { determineTypeScriptType } from '../../shared/modelUtils.js';
import { convertToValidIdentifier, formatPropertyName, normalizePropertyName, sanitizeName, } from '../../shared/nameUtils.js';
import { getPropertiesNode, getRequiredFields, isJsonObject } from '../../shared/schemaUtils.js';
export async function generateDataverseModel({ parentContext, schema, fileColumnNames, imageColumnNames, modelFilePath, }) {
    const { modelName, project, logger } = parentContext;
    const sourceFile = await project.createFile('model', modelFilePath, '');
    const propertiesNode = getPropertiesNode(schema);
    const lookupColumns = getDataverseLookupColumns(propertiesNode);
    const hasLookupColumns = lookupColumns.size > 0;
    const multiSelectPicklistFields = getDataverseMultiSelectPicklistFields(propertiesNode);
    addOptionsetDeclarations(sourceFile, modelName, propertiesNode);
    if (!hasLookupColumns) {
        addFullInterface(sourceFile, modelName, schema, propertiesNode);
    }
    else {
        const baseInterfaceName = `${modelName}Base`;
        addBaseInterface(sourceFile, baseInterfaceName, modelName, schema, lookupColumns, propertiesNode, logger);
        addExtendedInterface(sourceFile, modelName, baseInterfaceName, schema, lookupColumns, propertiesNode);
    }
    addUploadColumnNameType(sourceFile, modelName, fileColumnNames, imageColumnNames);
    return { hasLookupColumns, multiSelectPicklistFields };
}
// ─── Upload Column Name Type ──────────────────────────────────────────────────
function addUploadColumnNameType(sourceFile, modelName, fileColumnNames, imageColumnNames) {
    const id = convertToValidIdentifier(modelName);
    if (fileColumnNames.length > 0) {
        sourceFile.addTypeAlias({
            name: `${id}FileColumnName`,
            isExported: true,
            type: fileColumnNames.map((col) => `'${col}'`).join(' | '),
        });
    }
    if (imageColumnNames.length > 0) {
        sourceFile.addTypeAlias({
            name: `${id}ImageColumnName`,
            isExported: true,
            type: imageColumnNames.map((col) => `'${col}'`).join(' | '),
        });
    }
    if (fileColumnNames.length === 0 && imageColumnNames.length === 0) {
        return;
    }
    const uploadType = fileColumnNames.length > 0 && imageColumnNames.length > 0
        ? `${id}FileColumnName | ${id}ImageColumnName`
        : fileColumnNames.length > 0
            ? `${id}FileColumnName`
            : `${id}ImageColumnName`;
    sourceFile.addTypeAlias({
        name: `${id}UploadColumnName`,
        isExported: true,
        type: uploadType,
    });
}
// ─── Optionset Declarations ───────────────────────────────────────────────────
function addOptionsetDeclarations(sourceFile, modelName, propertiesNode) {
    for (const [propName, propValue] of Object.entries(propertiesNode)) {
        if (!isJsonObject(propValue) || !hasOptionsetData(propValue)) {
            continue;
        }
        const enumName = generateOptionsetEnumName(modelName, propName);
        const enumValues = propValue['x-ms-enum-values'];
        const enumLabels = propValue.enum;
        if (!Array.isArray(enumValues)) {
            // No x-ms-enum-values: emit a string union type alias from the enum labels.
            sourceFile.addTypeAlias({
                name: enumName,
                isExported: true,
                type: enumLabels.map(formatEnumLabel).join(' | '),
            });
            continue;
        }
        if (!enumValues.every((val) => typeof val === 'number')) {
            throw new Error(`Unsupported Dataverse optionset format: x-ms-enum-values must be an array of numbers. Property: ${propName} in model ${modelName}`);
        }
        // Dataverse format: x-ms-enum-values contains numeric values.
        // Emit a const object mapping numeric keys to display names, plus a keyof type alias.
        const entries = enumLabels.map((label, i) => {
            const sanitizedLabel = !label || (typeof label === 'string' && label.trim() === '')
                ? `EmptyOption${i}`
                : sanitizeName(label);
            return `  ${enumValues[i]}: '${sanitizedLabel}'`;
        });
        sourceFile.addVariableStatement({
            declarationKind: VariableDeclarationKind.Const,
            isExported: true,
            declarations: [
                {
                    name: enumName,
                    initializer: `{\n${entries.join(',\n')}\n} as const`,
                },
            ],
        });
        sourceFile.addTypeAlias({
            name: enumName,
            isExported: true,
            type: `keyof typeof ${enumName}`,
        });
    }
}
/**
 * Adds a single interface for entities with no lookup columns.
 * All properties (including read-only) are included.
 */
function addFullInterface(sourceFile, modelName, schema, //DataverseEntitySchema,
propertiesNode //DataverseEntitySchema['properties']
) {
    const requiredFields = getRequiredFields(schema);
    const properties = Object.entries(propertiesNode).map(([propName, propValue]) => {
        const propValueAsObject = isJsonObject(propValue) ? propValue : undefined;
        const formattedName = normalizePropertyName(formatPropertyName(convertToValidIdentifier(propName)));
        const description = typeof propValueAsObject?.description === 'string'
            ? propValueAsObject.description
            : undefined;
        return {
            name: formattedName,
            type: determineTypeScriptType(modelName, propValueAsObject, '', propName),
            hasQuestionToken: !requiredFields.has(propName),
            docs: description ? [{ description }] : undefined,
        };
    });
    sourceFile.addInterface({
        name: convertToValidIdentifier(modelName),
        isExported: true,
        properties,
    });
}
/**
 * Adds the base interface for PATCH/POST operations on entities with lookup columns.
 * - Skips read-only properties (they belong in the extended GET interface)
 * - Replaces lookup columns with @odata.bind string properties
 * - Skips lookup-related name properties (e.g., owneridname)
 */
function addBaseInterface(sourceFile, interfaceName, originalModelName, schema, lookupColumns, propertiesNode, logger) {
    const properties = [];
    const requiredFields = getRequiredFields(schema);
    for (const prop of iterateDataverseProperties(propertiesNode, lookupColumns)) {
        const { type, readOnly, propName, propValue } = prop;
        // lookupRelated properties (e.g., owneridname) should always be read-only.
        // Log a warning if an anomaly is detected, but it will be skipped regardless.
        if (logger && type === 'lookupRelated' && !readOnly) {
            logger.trackActivityEvent('CodegenV2.generateDataverseModel.addBaseInterface.warning', {
                message: 'Anomaly: lookupRelated property is not read-only',
                modelName: originalModelName,
                columnName: propName,
            });
        }
        // Skip read-only properties and lookup-related properties (e.g., owneridname)
        // They are handled in the extended interface, and cannot be written to.
        if (readOnly || type === 'lookupRelated') {
            continue;
        }
        // Replace the lookup columns with an @odata.bind property for write operations.
        if (type === 'lookup') {
            properties.push({
                name: formatPropertyName(generateODataBindPropertyName(prop.schemaName)),
                type: 'string',
                hasQuestionToken: !requiredFields.has(propName),
            });
            continue;
        }
        const formattedName = normalizePropertyName(formatPropertyName(convertToValidIdentifier(propName)));
        const description = typeof propValue.description === 'string' ? propValue.description : undefined;
        properties.push({
            name: formattedName,
            type: determineTypeScriptType(originalModelName, propValue, '', propName),
            hasQuestionToken: !requiredFields.has(propName),
            docs: description ? [{ description }] : undefined,
        });
    }
    sourceFile.addInterface({
        name: convertToValidIdentifier(interfaceName),
        isExported: true,
        properties,
    });
}
/**
 * Adds the extended interface for GET operations on entities with lookup columns.
 * Extends the base interface and adds:
 * - Read-only properties (excluding lookup columns, which are handled separately)
 * - Lookup object properties (e.g., ownerid?: object)
 * - Lookup value properties (e.g., _ownerid_value?: string)
 */
function addExtendedInterface(sourceFile, modelName, baseInterfaceName, schema, lookupColumns, propertiesNode) {
    const requiredFields = getRequiredFields(schema);
    const properties = [];
    for (const { type, readOnly, propName, propValue } of iterateDataverseProperties(propertiesNode, lookupColumns)) {
        // Exclude non-readonly properties (they are handled in the base interface)
        // Exclude lookup properties, they are handled separately below
        if (!readOnly || type === 'lookup') {
            continue;
        }
        const formattedName = normalizePropertyName(formatPropertyName(convertToValidIdentifier(propName)));
        const description = typeof propValue.description === 'string' ? propValue.description : undefined;
        properties.push({
            name: formattedName,
            type: determineTypeScriptType(modelName, propValue, '', propName),
            hasQuestionToken: !requiredFields.has(propName),
            docs: description ? [{ description }] : undefined,
        });
    }
    // Lookup-specific properties: the lookup object and its extracted GUID value.
    for (const lookupPropName of lookupColumns.keys()) {
        properties.push({
            name: formatPropertyName(convertToValidIdentifier(lookupPropName)),
            type: 'object',
            hasQuestionToken: true,
        });
        properties.push({
            name: formatPropertyName(generateLookupValuePropertyName(lookupPropName)),
            type: 'string',
            hasQuestionToken: true,
        });
    }
    sourceFile.addInterface({
        name: convertToValidIdentifier(modelName),
        isExported: true,
        extends: [convertToValidIdentifier(baseInterfaceName)],
        properties,
    });
}
export default generateDataverseModel;
//# sourceMappingURL=generateDataverseModel.js.map