/**
 * 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 { ExternalFormatData, RelationalCSVData, ConnectionTestData, EqualToJson, DEFAULT_TEST_ASSERTION_PREFIX, RelationalCSVDataTable, getAllIdentifiedConnectionsFromRuntime, ModelStoreData, ModelEmbeddedData, PackageableElementExplicitReference, TestExecuted, TestExecutionStatus, isStubbed_RawLambda, SimpleFunctionExpression, InstanceValue, CollectionInstanceValue, matchFunctionName, PackageableElementImplicitReference, VariableExpression, PrimitiveInstanceValue, PrimitiveType, LambdaFunctionInstanceValue, DataElement, RelationElementsData, RelationElement, RelationRowTestData, } from '@finos/legend-graph';
import { assertTrue, ContentType, generateEnumerableNameFromToken, guaranteeNonEmptyString, guaranteeType, isNonNullable, LogEvent, returnUndefOnError, UnsupportedOperationError, uuid, } from '@finos/legend-shared';
import { EmbeddedDataType } from '../editor-state/ExternalFormatState.js';
import { createMockDataForMappingElementSource } from './MockDataUtils.js';
import { QUERY_BUILDER_SUPPORTED_FUNCTIONS } from '@finos/legend-query-builder';
import { LEGEND_STUDIO_APP_EVENT } from '../../../__lib__/LegendStudioEvent.js';
export const DEFAULT_TEST_ASSERTION_ID = 'assertion_1';
export const DEFAULT_TEST_ID = 'test_1';
export const validateTestableId = (id, possibleIds) => {
    // undefined in this case means user has yet to write anything so we shouldnt show any error message
    if (id === undefined) {
        return undefined;
    }
    if (!id) {
        return 'ID is required';
    }
    else if (id.includes(' ')) {
        return `ID can't contain spaces`;
    }
    else if (possibleIds?.includes(id)) {
        return `ID '${id}' already exists`;
    }
    return undefined;
};
export const createBareExternalFormat = (contentType, content) => {
    const data = new ExternalFormatData();
    data.contentType = contentType ?? ContentType.APPLICATION_JSON;
    data.data = content ?? '';
    return data;
};
export const createDefaultEqualToJSONTestAssertion = (id) => {
    const xt = createBareExternalFormat(undefined, '{}');
    const assertion = new EqualToJson();
    assertion.expected = xt;
    assertion.id = id ?? uuid();
    return assertion;
};
export const createEmbeddedDataFromClass = (_class, editorStore) => {
    const _json = createMockDataForMappingElementSource(_class, editorStore);
    const data = createBareExternalFormat();
    data.data = _json;
    return data;
};
export const createBareModelStoreData = (_class, editorStore) => {
    const embeddedData = createEmbeddedDataFromClass(_class, editorStore);
    const modelStoreData = new ModelStoreData();
    const modelData = new ModelEmbeddedData();
    modelData.data = embeddedData;
    modelData.model = PackageableElementExplicitReference.create(_class);
    modelStoreData.modelData = [modelData];
    return modelStoreData;
};
export class EmbeddedDataCreatorFromEmbeddedData {
    editorStore;
    constructor(editorStore) {
        this.editorStore = editorStore;
    }
    visit_EmbeddedData(data) {
        const extraEmbeddedDataCloners = this.editorStore.pluginManager
            .getApplicationPlugins()
            .flatMap((plugin) => plugin.getExtraEmbeddedDataCloners?.() ?? []);
        for (const creator of extraEmbeddedDataCloners) {
            const embeddedData = creator(data);
            if (embeddedData) {
                return embeddedData;
            }
        }
        throw new UnsupportedOperationError(`Unsupported embedded data${data.toString()}`);
    }
    visit_INTERNAL__UnknownEmbeddedData(data) {
        throw new Error('Method not implemented.');
    }
    visit_ExternalFormatData(data) {
        const val = new ExternalFormatData();
        val.contentType = data.contentType;
        val.data = data.data;
        return val;
    }
    visit_RelationElementsData(data) {
        const val = new RelationElementsData();
        val.relationElements = data.relationElements.map((relationElement) => {
            const v = new RelationElement();
            v.columns = relationElement.columns;
            v.paths = relationElement.paths;
            v.rows = relationElement.rows.map((row) => {
                const r = new RelationRowTestData();
                r.values = row.values;
                return r;
            });
            return v;
        });
        return val;
    }
    visit_ModelStoreData(data) {
        const val = new ModelStoreData();
        val.modelData = data.modelData
            ?.map((e) => {
            if (e instanceof ModelEmbeddedData) {
                const v = new ModelEmbeddedData();
                v.model = PackageableElementExplicitReference.create(e.model.value);
                v.data = e.data.accept_EmbeddedDataVisitor(new EmbeddedDataCreatorFromEmbeddedData(this.editorStore));
                return v;
            }
            return undefined;
        })
            .filter(isNonNullable);
        return val;
    }
    visit_DataElementReference(data) {
        const datElement = guaranteeType(data.dataElement.value, DataElement);
        return datElement.data.accept_EmbeddedDataVisitor(new EmbeddedDataCreatorFromEmbeddedData(this.editorStore));
    }
    visit_RelationalCSVData(data) {
        const val = new RelationalCSVData();
        return val;
    }
}
// NOTE: this will all move to `engine` once engine support generating test data for all connections
// Throws if unable to generate test data
export class TEMPORARY__EmbeddedDataConnectionVisitor {
    editorStore;
    constructor(editorStore) {
        this.editorStore = editorStore;
    }
    visit_Connection(connection) {
        throw new UnsupportedOperationError();
    }
    visit_INTERNAL__UnknownConnection(connection) {
        throw new UnsupportedOperationError();
    }
    visit_ConnectionPointer(connection) {
        const packageableConnection = connection.packageableConnection.value.connectionValue;
        return packageableConnection.accept_ConnectionVisitor(this);
    }
    visit_ModelChainConnection(connection) {
        throw new UnsupportedOperationError();
    }
    visit_JsonModelConnection(connection) {
        return createEmbeddedDataFromClass(connection.class.value, this.editorStore);
    }
    visit_XmlModelConnection(connection) {
        throw new UnsupportedOperationError();
    }
    visit_FlatDataConnection(connection) {
        throw new UnsupportedOperationError();
    }
    visit_RelationalDatabaseConnection(connection) {
        throw new UnsupportedOperationError();
    }
}
export class EmbeddedDataConnectionTypeVisitor {
    editorStore;
    constructor(editorStore) {
        this.editorStore = editorStore;
    }
    visit_Connection(connection) {
        const extraEmbeddedDataCreator = this.editorStore.pluginManager
            .getApplicationPlugins()
            .flatMap((plugin) => plugin.getExtraEmbeddedDataTypeFromConnectionMatchers?.() ?? []);
        for (const creator of extraEmbeddedDataCreator) {
            const embeddedData = creator(connection);
            if (embeddedData) {
                return embeddedData;
            }
        }
        return EmbeddedDataType.EXTERNAL_FORMAT_DATA;
    }
    visit_INTERNAL__UnknownConnection(connection) {
        return EmbeddedDataType.EXTERNAL_FORMAT_DATA;
    }
    visit_ConnectionPointer(connection) {
        const packageableConnection = connection.packageableConnection.value.connectionValue;
        return packageableConnection.accept_ConnectionVisitor(this);
    }
    visit_ModelChainConnection(connection) {
        return EmbeddedDataType.EXTERNAL_FORMAT_DATA;
    }
    visit_JsonModelConnection(connection) {
        return EmbeddedDataType.EXTERNAL_FORMAT_DATA;
    }
    visit_XmlModelConnection(connection) {
        return EmbeddedDataType.EXTERNAL_FORMAT_DATA;
    }
    visit_FlatDataConnection(connection) {
        return EmbeddedDataType.EXTERNAL_FORMAT_DATA;
    }
    visit_RelationalDatabaseConnection(connection) {
        return EmbeddedDataType.RELATIONAL_CSV;
    }
}
export const initializeConnectionDataFromRuntime = (runtime, editorStore) => {
    const identifiedConnections = getAllIdentifiedConnectionsFromRuntime(runtime);
    return identifiedConnections
        .map((identifiedConnection) => {
        const connection = identifiedConnection.connection;
        const embeddedData = returnUndefOnError(() => connection.accept_ConnectionVisitor(new TEMPORARY__EmbeddedDataConnectionVisitor(editorStore)));
        if (embeddedData) {
            const connectionTestData = new ConnectionTestData();
            connectionTestData.connectionId = identifiedConnection.id;
            connectionTestData.testData = embeddedData;
            return connectionTestData;
        }
        return undefined;
    })
        .filter(isNonNullable);
};
export const createEmptyEqualToJsonAssertion = (test) => {
    const assert = new EqualToJson();
    assert.id = generateEnumerableNameFromToken(test.assertions.map((a) => a.id), DEFAULT_TEST_ASSERTION_PREFIX);
    assert.expected = createBareExternalFormat();
    assert.parentTest = test;
    return assert;
};
// Temproary as engine should return an embedded data type
export const TEMPORARY__createRelationalDataFromCSV = (val) => {
    const NEW_LINE = '\n';
    const data = new RelationalCSVData();
    const separator = `${NEW_LINE}-----${NEW_LINE}`;
    const lineBreak = /\r?\n/;
    const tables = val
        .split(separator)
        .filter((e) => !(e === NEW_LINE || e === '\r' || e === ''));
    tables.forEach((tableData) => {
        const tableInfo = tableData.split(lineBreak);
        assertTrue(tableInfo.length >= 2, 'Table and Schema Name required from test data');
        const table = new RelationalCSVDataTable();
        table.schema = guaranteeNonEmptyString(tableInfo.shift());
        table.table = guaranteeNonEmptyString(tableInfo.shift());
        table.values = tableInfo.join(NEW_LINE) + NEW_LINE;
        data.tables.push(table);
    });
    return data;
};
// test result
export const isTestPassing = (testResult) => testResult instanceof TestExecuted &&
    testResult.testExecutionStatus === TestExecutionStatus.PASS;
export const getContentTypeWithParamRecursively = (expression) => {
    let currentExpression = expression;
    const res = [];
    // use if statement to safely scan service query without breaking the app
    while (currentExpression instanceof SimpleFunctionExpression) {
        if (matchFunctionName(currentExpression.functionName, QUERY_BUILDER_SUPPORTED_FUNCTIONS.INTERNALIZE) ||
            matchFunctionName(currentExpression.functionName, QUERY_BUILDER_SUPPORTED_FUNCTIONS.GET_RUNTIME_WITH_MODEL_QUERY_CONNECTION)) {
            if (currentExpression.parametersValues[1] instanceof InstanceValue) {
                if (currentExpression.parametersValues[1].values[0] instanceof
                    PackageableElementImplicitReference &&
                    currentExpression.parametersValues[2] instanceof VariableExpression) {
                    res.push({
                        contentType: currentExpression.parametersValues[1].values[0].value.contentType,
                        param: currentExpression.parametersValues[2].name,
                    });
                }
                else if (matchFunctionName(currentExpression.functionName, QUERY_BUILDER_SUPPORTED_FUNCTIONS.GET_RUNTIME_WITH_MODEL_QUERY_CONNECTION) &&
                    currentExpression.parametersValues[1] instanceof
                        PrimitiveInstanceValue &&
                    currentExpression.parametersValues[1].genericType.value.rawType ===
                        PrimitiveType.STRING &&
                    currentExpression.parametersValues[2] instanceof VariableExpression) {
                    res.push({
                        contentType: currentExpression.parametersValues[1]
                            .values[0],
                        param: currentExpression.parametersValues[2].name,
                    });
                }
            }
            currentExpression = currentExpression.parametersValues[1];
        }
        else if (matchFunctionName(currentExpression.functionName, QUERY_BUILDER_SUPPORTED_FUNCTIONS.FROM)) {
            currentExpression = currentExpression.parametersValues[2];
        }
        else if (matchFunctionName(currentExpression.functionName, QUERY_BUILDER_SUPPORTED_FUNCTIONS.MERGERUNTIMES)) {
            const collection = currentExpression.parametersValues[0];
            if (collection instanceof CollectionInstanceValue) {
                collection.values
                    .map((v) => getContentTypeWithParamRecursively(v))
                    .flat()
                    .map((p) => res.push(p));
            }
            currentExpression = collection;
        }
        else {
            currentExpression = currentExpression.parametersValues[0];
        }
    }
    return res;
};
export const getContentTypeWithParamFromQuery = (query, editorStore) => {
    if (query && !isStubbed_RawLambda(query)) {
        // safely pass unsupported funtions when building ValueSpecification from Rawlambda
        try {
            const valueSpec = editorStore.graphManagerState.graphManager.buildValueSpecification(editorStore.graphManagerState.graphManager.serializeRawValueSpecification(query), editorStore.graphManagerState.graph);
            if (valueSpec instanceof LambdaFunctionInstanceValue) {
                return getContentTypeWithParamRecursively(valueSpec.values[0]?.expressionSequence.find((exp) => exp instanceof SimpleFunctionExpression &&
                    (matchFunctionName(exp.functionName, QUERY_BUILDER_SUPPORTED_FUNCTIONS.SERIALIZE) ||
                        matchFunctionName(exp.functionName, QUERY_BUILDER_SUPPORTED_FUNCTIONS.EXTERNALIZE))));
            }
        }
        catch (error) {
            editorStore.applicationStore.logService.error(LogEvent.create(LEGEND_STUDIO_APP_EVENT.SERVICE_TEST_SETUP_FAILURE), error);
        }
    }
    return [];
};
//# sourceMappingURL=TestableUtils.js.map