/**
 * Copyright (c) 2020-present, Goldman Sachs
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { addDays, assertErrorThrown, formatDate, Randomizer, UnsupportedOperationError, isNonNullable, filterByType, } from '@finos/legend-shared';
import { PrimitiveType, PRIMITIVE_TYPE, Class, getAllClassProperties, DATE_FORMAT, DATE_TIME_FORMAT, classHasCycle, TableAlias, Table, Column, VarChar, Char, VarBinary, TinyInt, Float, Timestamp, Binary, Bit, Other, Numeric, Decimal, Double, Integer, Real, SmallInt, Date as ColumnDate, SemiStructured, Json, MILESTONING_VERSION_PROPERTY_SUFFIX, } from '@finos/legend-graph';
import { CLASS_PROPERTY_TYPE, getClassPropertyType, } from './ModelClassifierUtils.js';
import { DEFAULT_TAB_SIZE } from '@finos/legend-application';
import { XMLBuilder } from 'fast-xml-parser';
import { stringify as YAML_stringify } from 'yaml';
export const createMockPrimitiveProperty = (primitiveType, propertyName) => {
    const randomizer = new Randomizer();
    switch (primitiveType.name) {
        case PRIMITIVE_TYPE.BOOLEAN:
            return randomizer.getRandomItemInCollection([true, false]) ?? true;
        case PRIMITIVE_TYPE.FLOAT:
            return randomizer.getRandomFloat();
        case PRIMITIVE_TYPE.DECIMAL:
            return randomizer.getRandomDouble();
        case PRIMITIVE_TYPE.NUMBER:
        case PRIMITIVE_TYPE.INTEGER:
            return randomizer.getRandomWholeNumber(100);
        // NOTE that `Date` is the umbrella type that comprises `StrictDate` and `DateTime`, but for simplicity, we will generate `Date` as `StrictDate`
        case PRIMITIVE_TYPE.DATE:
        case PRIMITIVE_TYPE.STRICTDATE:
            return formatDate(randomizer.getRandomDate(new Date(Date.now()), addDays(Date.now(), 100)), DATE_FORMAT);
        case PRIMITIVE_TYPE.DATETIME:
            return formatDate(randomizer.getRandomDate(new Date(Date.now()), addDays(Date.now(), 100)), DATE_TIME_FORMAT);
        case PRIMITIVE_TYPE.STRING:
        default:
            return `${propertyName} ${randomizer.getRandomWholeNumber(100)}`;
    }
};
export const createMockEnumerationProperty = (enumeration) => new Randomizer().getRandomItemInCollection(enumeration.values)?.name ?? '';
export const createMockClassInstance = (_class, traverseNonRequiredProperties = false, depth = 0) => {
    const properties = traverseNonRequiredProperties
        ? getAllClassProperties(_class, true)
        : getAllClassProperties(_class, true).filter((p) => p.multiplicity.lowerBound);
    const propertyNames = properties.map((property) => property.name);
    const filteredProperties = properties.filter((prop) => prop.name.endsWith(MILESTONING_VERSION_PROPERTY_SUFFIX.ALL_VERSIONS) ||
        (!propertyNames.includes(prop.name + MILESTONING_VERSION_PROPERTY_SUFFIX.ALL_VERSIONS) &&
            !prop.name.endsWith(MILESTONING_VERSION_PROPERTY_SUFFIX.ALL_VERSIONS_IN_RANGE)));
    const mockData = {};
    filteredProperties.forEach((property) => {
        const propertyType = property.genericType.value.rawType;
        let propertyMockData;
        switch (getClassPropertyType(propertyType)) {
            case CLASS_PROPERTY_TYPE.PRIMITIVE:
                propertyMockData = createMockPrimitiveProperty(propertyType, property.name);
                break;
            case CLASS_PROPERTY_TYPE.ENUMERATION:
                propertyMockData = createMockEnumerationProperty(propertyType);
                break;
            case CLASS_PROPERTY_TYPE.CLASS:
                if (depth > 0) {
                    propertyMockData = createMockClassInstance(propertyType, traverseNonRequiredProperties, depth - 1);
                }
                break;
            default:
                break;
        }
        if (propertyMockData) {
            mockData[property.name] =
                property.multiplicity.upperBound === undefined ||
                    property.multiplicity.upperBound >= 2
                    ? [propertyMockData]
                    : propertyMockData;
        }
    });
    return mockData;
};
export const createMockDataForClass = (element, maxDepth = 100, depthForCycle = 3) => {
    const depth = classHasCycle(element, {
        traverseNonRequiredProperties: true,
    })
        ? Math.max(depthForCycle, 0)
        : Math.max(maxDepth, 0);
    return createMockClassInstance(element, true, depth);
};
export var CLASS_MOCK_DATA_GENERATION_FORMAT;
(function (CLASS_MOCK_DATA_GENERATION_FORMAT) {
    CLASS_MOCK_DATA_GENERATION_FORMAT["JSON"] = "JSON";
    CLASS_MOCK_DATA_GENERATION_FORMAT["XML"] = "XML";
    CLASS_MOCK_DATA_GENERATION_FORMAT["YAML"] = "YAML";
})(CLASS_MOCK_DATA_GENERATION_FORMAT || (CLASS_MOCK_DATA_GENERATION_FORMAT = {}));
export const createMockDataForClassWithFormat = (element, format, maxDepth = 100, depthForCycle = 3) => {
    const depth = classHasCycle(element, {
        traverseNonRequiredProperties: true,
    })
        ? Math.max(depthForCycle, 0)
        : Math.max(maxDepth, 0);
    const obj = createMockClassInstance(element, true, depth);
    switch (format) {
        case CLASS_MOCK_DATA_GENERATION_FORMAT.JSON:
            return JSON.stringify(obj, undefined, DEFAULT_TAB_SIZE);
        case CLASS_MOCK_DATA_GENERATION_FORMAT.XML: {
            return new XMLBuilder({
                indentBy: ' '.repeat(DEFAULT_TAB_SIZE),
                format: true,
            }).build(obj);
        }
        case CLASS_MOCK_DATA_GENERATION_FORMAT.YAML:
            return YAML_stringify(obj, { indent: DEFAULT_TAB_SIZE });
        default:
            throw new UnsupportedOperationError(`Can't create mock data for class with format '${format}'`);
    }
};
export const getPrimitiveTypeFromRelationalType = (type) => {
    if (type instanceof VarChar ||
        type instanceof Char ||
        type instanceof VarBinary ||
        type instanceof Binary ||
        type instanceof Bit ||
        type instanceof Other) {
        return PrimitiveType.STRING;
    }
    else if (type instanceof Numeric) {
        return PrimitiveType.NUMBER;
    }
    else if (type instanceof Decimal) {
        return PrimitiveType.DECIMAL;
    }
    else if (type instanceof Double ||
        type instanceof Integer ||
        type instanceof Real ||
        type instanceof SmallInt ||
        type instanceof TinyInt) {
        return PrimitiveType.INTEGER;
    }
    else if (type instanceof Float) {
        return PrimitiveType.FLOAT;
    }
    else if (type instanceof ColumnDate) {
        return PrimitiveType.DATE;
    }
    else if (type instanceof Timestamp) {
        return PrimitiveType.DATETIME;
    }
    return undefined;
};
export const createMockDataForColumn = (col, isPrimaryKey, idx) => {
    const type = col.type;
    if ((type instanceof Double ||
        type instanceof Integer ||
        type instanceof Real ||
        type instanceof SmallInt ||
        type instanceof TinyInt) &&
        isPrimaryKey &&
        idx) {
        return idx.toString();
    }
    const primitive = getPrimitiveTypeFromRelationalType(type);
    if (primitive) {
        return createMockPrimitiveProperty(primitive, col.name).toString();
    }
    else if (type instanceof Json || type instanceof SemiStructured) {
        return '{}';
    }
    return undefined;
};
export const createMockDataForTable = (table, ITERATIONS = 1) => {
    const targetedCols = table.columns.filter(filterByType(Column));
    const colNames = targetedCols.map((e) => e.name).join(',');
    const vals = Array.from(Array(ITERATIONS).keys())
        .map((idx) => targetedCols
        .map((col) => createMockDataForColumn(col, table.primaryKey.includes(col), idx))
        .filter(isNonNullable)
        .join(','))
        .join('\n');
    return `${colNames}\n${vals}`;
};
export const createMockDataForMappingElementSource = (srcElement, editorStore) => {
    if (srcElement instanceof Class) {
        try {
            return JSON.stringify(createMockDataForClass(srcElement), undefined, DEFAULT_TAB_SIZE);
        }
        catch (error) {
            assertErrorThrown(error);
            editorStore.applicationStore.notificationService.notifyWarning(`Can't generate test data for class '${srcElement}'. Error:\n${error.message}`);
            return '';
        }
    }
    else if (srcElement instanceof TableAlias &&
        srcElement.relation instanceof Table) {
        return createMockDataForTable(srcElement.relation);
    }
    else if (srcElement instanceof Table) {
        return createMockDataForTable(srcElement);
    }
    editorStore.applicationStore.notificationService.notifyWarning(new UnsupportedOperationError(`Can't generate test data for mapping source`, srcElement));
    return '';
};
//# sourceMappingURL=MockDataUtils.js.map