/**
 * 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 { guaranteeNonEmptyString, guaranteeNonNullable, IllegalStateError, isNonNullable, UnsupportedOperationError, } from '@finos/legend-shared';
import { PurePropertyMapping } from '../../../../../../../graph/metamodel/pure/packageableElements/store/modelToModel/mapping/PurePropertyMapping.js';
import { Enum } from '../../../../../../../graph/metamodel/pure/packageableElements/domain/Enum.js';
import { extractLine } from '../../../../../../../graph/metamodel/pure/packageableElements/store/relational/model/RelationalOperationElement.js';
import { FlatDataPropertyMapping } from '../../../../../../../graph/metamodel/pure/packageableElements/store/flatData/mapping/FlatDataPropertyMapping.js';
import { OperationType, } from '../../../../../../../graph/metamodel/pure/packageableElements/mapping/OperationSetImplementation.js';
import { RelationalPropertyMapping } from '../../../../../../../graph/metamodel/pure/packageableElements/store/relational/mapping/RelationalPropertyMapping.js';
import { MappingIncludeMapping } from '../../../../../../../graph/metamodel/pure/packageableElements/mapping/MappingIncludeMapping.js';
import { MappingIncludeDataProduct } from '../../../../../../../graph/metamodel/pure/dataProduct/MappingIncludeDataProduct.js';
import { INTERNAL__UnknownMappingInclude } from '../../../../../../../graph/metamodel/pure/packageableElements/mapping/INTERNAL__UnknownMappingInclude.js';
import { DEPRECATED__ExpectedOutputMappingTestAssert, DEPRECATED__ObjectInputData, DEPRECATED__FlatDataInputData, ObjectInputType, DEPRECATED__RelationalInputData, } from '../../../../../../../graph/metamodel/pure/packageableElements/mapping/DEPRECATED__MappingTest.js';
import { RelationalAssociationImplementation } from '../../../../../../../graph/metamodel/pure/packageableElements/mapping/RelationalAssociationImplementation.js';
import { V1_initPackageableElement, V1_transformElementReferencePointer, V1_transformMultiplicity, } from './V1_CoreTransformerHelper.js';
import { V1_Mapping } from '../../../model/packageableElements/mapping/V1_Mapping.js';
import { V1_EnumValueMapping, V1_EnumValueMappingEnumSourceValue, V1_EnumValueMappingIntegerSourceValue, V1_EnumValueMappingStringSourceValue, } from '../../../model/packageableElements/mapping/V1_EnumValueMapping.js';
import { V1_RelationalAssociationMapping } from '../../../model/packageableElements/store/relational/mapping/V1_RelationalAssociationMapping.js';
import { V1_DEPRECATED__MappingTest, V1_DEPRECATED__ObjectInputData, V1_DEPRECATED__ObjectInputType, V1_DEPRECATED__FlatDataInputData, V1_DEPRECATED__ExpectedOutputMappingTestAssert, V1_DEPRECATED__RelationalInputData, } from '../../../model/packageableElements/mapping/V1_DEPRECATED__MappingTest.js';
import { V1_RawValueSpecificationTransformer } from './V1_RawValueSpecificationTransformer.js';
import { V1_MappingIncludeMapping, } from '../../../model/packageableElements/mapping/V1_MappingInclude.js';
import { V1_MappingIncludeDataProduct } from '../../../model/packageableElements/dataProduct/V1_MappingIncludeDataProduct.js';
import { V1_INTERNAL__UnknownMappingInclude } from '../../../model/packageableElements/mapping/V1_INTERNAL__UnknownMappingInclude.js';
import { V1_EnumerationMapping } from '../../../model/packageableElements/mapping/V1_EnumerationMapping.js';
import { V1_FlatDataPropertyMapping } from '../../../model/packageableElements/store/flatData/mapping/V1_FlatDataPropertyMapping.js';
import { V1_PropertyPointer } from '../../../model/packageableElements/domain/V1_PropertyPointer.js';
import { V1_EmbeddedFlatDataPropertyMapping } from '../../../model/packageableElements/store/flatData/mapping/V1_EmbeddedFlatDataPropertyMapping.js';
import { V1_PurePropertyMapping } from '../../../model/packageableElements/store/modelToModel/mapping/V1_PurePropertyMapping.js';
import { V1_RelationalPropertyMapping } from '../../../model/packageableElements/store/relational/mapping/V1_RelationalPropertyMapping.js';
import { V1_transformRelationalOperationElement, V1_transformTableAliasToTablePointer, } from './V1_DatabaseTransformer.js';
import { V1_EmbeddedRelationalPropertyMapping } from '../../../model/packageableElements/store/relational/mapping/V1_EmbeddedRelationalPropertyMapping.js';
import { V1_InlineEmbeddedPropertyMapping } from '../../../model/packageableElements/store/relational/mapping/V1_InlineEmbeddedPropertyMapping.js';
import { V1_OtherwiseEmbeddedRelationalPropertyMapping } from '../../../model/packageableElements/store/relational/mapping/V1_OtherwiseEmbeddedRelationalPropertyMapping.js';
import { V1_RelationalClassMapping } from '../../../model/packageableElements/store/relational/mapping/V1_RelationalClassMapping.js';
import { V1_MappingOperationType, V1_OperationClassMapping, } from '../../../model/packageableElements/mapping/V1_OperationClassMapping.js';
import { V1_PureInstanceClassMapping } from '../../../model/packageableElements/store/modelToModel/mapping/V1_PureInstanceClassMapping.js';
import { V1_RootFlatDataClassMapping } from '../../../model/packageableElements/store/flatData/mapping/V1_RootFlatDataClassMapping.js';
import { V1_RootRelationalClassMapping } from '../../../model/packageableElements/store/relational/mapping/V1_RootRelationalClassMapping.js';
import { V1_AggregationAwareClassMapping } from '../../../model/packageableElements/store/relational/mapping/aggregationAware/V1_AggregationAwareClassMapping.js';
import { V1_AggregationAwarePropertyMapping } from '../../../model/packageableElements/store/relational/mapping/aggregationAware/V1_AggregationAwarePropertyMapping.js';
import { V1_AggregateSetImplementationContainer } from '../../../model/packageableElements/store/relational/mapping/aggregationAware/V1_AggregateSetImplementationContainer.js';
import { V1_AggregateSpecification } from '../../../model/packageableElements/store/relational/mapping/aggregationAware/V1_AggregateSpecification.js';
import { V1_AggregateFunction } from '../../../model/packageableElements/store/relational/mapping/aggregationAware/V1_AggregateFunction.js';
import { V1_GroupByFunction } from '../../../model/packageableElements/store/relational/mapping/aggregationAware/V1_GroupByFunction.js';
import { V1_XStorePropertyMapping } from '../../../model/packageableElements/mapping/xStore/V1_XStorePropertyMapping.js';
import { XStoreAssociationImplementation } from '../../../../../../../graph/metamodel/pure/packageableElements/mapping/xStore/XStoreAssociationImplementation.js';
import { V1_XStoreAssociationMapping } from '../../../model/packageableElements/mapping/xStore/V1_XStoreAssociationMapping.js';
import { V1_LocalMappingPropertyInfo } from '../../../model/packageableElements/mapping/V1_LocalMappingPropertyInfo.js';
import { V1_FilterMapping } from '../../../model/packageableElements/store/relational/mapping/V1_FilterMapping.js';
import { V1_FilterPointer } from '../../../model/packageableElements/store/relational/mapping/V1_FilterPointer.js';
import { V1_JoinPointer } from '../../../model/packageableElements/store/relational/model/V1_JoinPointer.js';
import { PackageableElementPointerType } from '../../../../../../../graph/MetaModelConst.js';
import { V1_BindingTransformer } from '../../../model/packageableElements/externalFormat/store/V1_DSL_ExternalFormat_BindingTransformer.js';
import { V1_MergeOperationClassMapping } from '../../../model/packageableElements/mapping/V1_MergeOperationClassMapping.js';
import { MergeOperationSetImplementation } from '../../../../../../../graph/metamodel/pure/packageableElements/mapping/MergeOperationSetImplementation.js';
import { isStubbed_EnumValueMapping } from '../../../../../../../graph/helpers/creator/DSL_Mapping_ModelCreatorHelper.js';
import { isStubbed_RawLambda } from '../../../../../../../graph/helpers/creator/RawValueSpecificationCreatorHelper.js';
import { isStubbed_RawRelationalOperationElement } from '../../../../../../../graph/helpers/creator/STO_Relational_ModelCreatorHelper.js';
import { pruneSourceInformation } from '../../../../../../../graph/MetaModelUtils.js';
import { FlatDataAssociationImplementation } from '../../../../../../../graph/metamodel/pure/packageableElements/store/flatData/mapping/FlatDataAssociationImplementation.js';
import { V1_FlatDataAssociationMapping } from '../../../model/packageableElements/store/flatData/mapping/V1_FlatDataAssociationMapping.js';
import { V1_FlatDataAssociationPropertyMapping } from '../../../model/packageableElements/store/flatData/mapping/V1_FlatDataAssociationPropertyMapping.js';
import { V1_MappingTest } from '../../../model/packageableElements/mapping/V1_MappingTest.js';
import { V1_transformAtomicTest, V1_transformTestAssertion, V1_transformTestSuite, } from './V1_TestTransformer.js';
import {} from '../../../../../../../graph/metamodel/pure/packageableElements/mapping/MappingTestSuite.js';
import { V1_MappingTestSuite } from '../../../model/packageableElements/mapping/V1_MappingTestSuite.js';
import { V1_MappingStoreTestData } from '../../../model/packageableElements/mapping/V1_MappingStoreTestData.js';
import { V1_transformEmbeddedData } from './V1_DataElementTransformer.js';
import { INTERNAL__UnknownPropertyMapping } from '../../../../../../../graph/metamodel/pure/packageableElements/mapping/INTERNAL__UnknownPropertyMapping.js';
import { V1_INTERNAL__UnknownPropertyMapping } from '../../../model/packageableElements/mapping/V1_INTERNAL__UnknownPropertyMapping.js';
import { V1_INTERNAL__UnknownClassMapping } from '../../../model/packageableElements/mapping/V1_INTERNAL__UnknownClassMapping.js';
import { V1_PackageableElementPointer } from '../../../model/packageableElements/V1_PackageableElement.js';
import { RelationFunctionPropertyMapping } from '../../../../../../../graph/metamodel/pure/packageableElements/mapping/relationFunction/RelationFunctionPropertyMapping.js';
import { V1_RelationFunctionPropertyMapping } from '../../../model/packageableElements/mapping/V1_RelationFunctionPropertyMapping.js';
import { V1_RelationFunctionClassMapping } from '../../../model/packageableElements/mapping/V1_RelationFunctionClassMapping.js';
import { generateFunctionPrettyName } from '../../../../../../../graph/helpers/PureLanguageHelper.js';
import { isStubbed_RelationColumn } from '../../../../../../../graph/helpers/STO_RelationFunction_Helper.js';
export const V1_transformPropertyReference = (element, options) => {
    const property = new V1_PropertyPointer();
    property.class =
        options?.isTransformingEmbeddedPropertyMapping ||
            options?.isTransformingLocalPropertyMapping
            ? undefined
            : (element.ownerReference.valueForSerialization ?? '');
    property.property = element.value.name;
    return property;
};
const mappingElementIdSerializer = (value) => value.valueForSerialization;
const transformLocalPropertyInfo = (value) => {
    const localPropertyInfo = new V1_LocalMappingPropertyInfo();
    localPropertyInfo.type =
        value.localMappingPropertyType.valueForSerialization ?? '';
    localPropertyInfo.multiplicity = V1_transformMultiplicity(value.localMappingPropertyMultiplicity);
    return localPropertyInfo;
};
const transformEnumValueMapping = (element) => {
    const enumValueMapping = new V1_EnumValueMapping();
    enumValueMapping.enumValue = element.enum.value.name;
    enumValueMapping.sourceValues = element.sourceValues
        .filter(isNonNullable)
        .map((value) => {
        if (typeof value.value === 'string') {
            const _string = new V1_EnumValueMappingStringSourceValue();
            _string.value = value.value;
            return _string;
        }
        else if (typeof value.value === 'number') {
            const _integer = new V1_EnumValueMappingIntegerSourceValue();
            _integer.value = value.value;
            return _integer;
        }
        else if (value.value instanceof Enum) {
            const _enum = new V1_EnumValueMappingEnumSourceValue();
            _enum.value = value.value.name;
            _enum.enumeration = value.value._OWNER.path;
            return _enum;
        }
        throw new UnsupportedOperationError(`Can't transform enum value mapping`, value);
    });
    return enumValueMapping;
};
const transformEnumerationMapping = (element) => {
    const enumerationMapping = new V1_EnumerationMapping();
    enumerationMapping.enumValueMappings = element.enumValueMappings
        .filter((e) => !isStubbed_EnumValueMapping(e))
        .map(transformEnumValueMapping);
    enumerationMapping.enumeration = new V1_PackageableElementPointer(PackageableElementPointerType.ENUMERATION, element.enumeration.valueForSerialization ?? '');
    enumerationMapping.id = mappingElementIdSerializer(element.id);
    return enumerationMapping;
};
// Mapping Test
export const V1_getObjectInputType = (type) => {
    switch (type) {
        case ObjectInputType.JSON:
            return V1_DEPRECATED__ObjectInputType.JSON;
        case ObjectInputType.XML:
            return V1_DEPRECATED__ObjectInputType.XML;
        default:
            throw new UnsupportedOperationError(`Encountered unsupported object input type '${type}'`);
    }
};
const transformObjectInputData = (element) => {
    const inputData = new V1_DEPRECATED__ObjectInputData();
    inputData.data = element.data;
    inputData.inputType = V1_getObjectInputType(element.inputType);
    inputData.sourceClass = element.sourceClass.valueForSerialization ?? '';
    return inputData;
};
const transformFlatDataInputData = (element) => {
    const inputData = new V1_DEPRECATED__FlatDataInputData();
    inputData.data = element.data;
    inputData.sourceFlatData = V1_transformElementReferencePointer(PackageableElementPointerType.STORE, element.sourceFlatData);
    return inputData;
};
const transformRelationalInputData = (element) => {
    const inputData = new V1_DEPRECATED__RelationalInputData();
    inputData.data = element.data;
    inputData.inputType = element.inputType;
    inputData.database = element.database.valueForSerialization ?? '';
    return inputData;
};
const transformExpectedOutputMappingTestAssert = (element) => {
    const assert = new V1_DEPRECATED__ExpectedOutputMappingTestAssert();
    assert.expectedOutput = element.expectedOutput;
    return assert;
};
const transformMappingTestInputData = (inputData) => {
    if (inputData instanceof DEPRECATED__ObjectInputData) {
        return transformObjectInputData(inputData);
    }
    else if (inputData instanceof DEPRECATED__FlatDataInputData) {
        return transformFlatDataInputData(inputData);
    }
    else if (inputData instanceof DEPRECATED__RelationalInputData) {
        return transformRelationalInputData(inputData);
    }
    throw new UnsupportedOperationError(`Can't transform mapping test input data`, inputData);
};
const transformTestAssert = (mappingTestAssert) => {
    if (mappingTestAssert instanceof DEPRECATED__ExpectedOutputMappingTestAssert) {
        return transformExpectedOutputMappingTestAssert(mappingTestAssert);
    }
    throw new UnsupportedOperationError(`Can't transform mapping test assert`, mappingTestAssert);
};
const transformMappingTestLegacy = (element, context) => {
    const test = new V1_DEPRECATED__MappingTest();
    test.assert = transformTestAssert(element.assert);
    test.inputData = element.inputData.map(transformMappingTestInputData);
    test.name = element.name;
    test.query = element.query.accept_RawValueSpecificationVisitor(new V1_RawValueSpecificationTransformer(context));
    return test;
};
const transformMappingStoreTestData = (element, context) => {
    const testData = new V1_MappingStoreTestData();
    testData.data = V1_transformEmbeddedData(element.data, context);
    testData.store = new V1_PackageableElementPointer(PackageableElementPointerType.STORE, element.store.valueForSerialization ?? '');
    return testData;
};
export const V1_transformMappingTest = (element, context) => {
    const mappingTest = new V1_MappingTest();
    mappingTest.id = element.id;
    mappingTest.doc = element.doc;
    mappingTest.assertions = element.assertions.map((assertion) => V1_transformTestAssertion(assertion));
    mappingTest.storeTestData = element.storeTestData.map((testData) => transformMappingStoreTestData(testData, context));
    return mappingTest;
};
export const V1_transformMappingTestSuite = (element, context) => {
    const mappingTestSuite = new V1_MappingTestSuite();
    mappingTestSuite.id = element.id;
    mappingTestSuite.doc = element.doc;
    mappingTestSuite.func = element.func.accept_RawValueSpecificationVisitor(new V1_RawValueSpecificationTransformer(context));
    mappingTestSuite.tests = element.tests.map((test) => V1_transformAtomicTest(test, context));
    return mappingTestSuite;
};
// Include Mapping
const transformMappingInclude = (element, context) => {
    if (element instanceof INTERNAL__UnknownMappingInclude) {
        const protocol = new V1_INTERNAL__UnknownMappingInclude();
        protocol.content = element.content;
        return protocol;
    }
    else if (element instanceof MappingIncludeDataProduct) {
        const mappingInclude = new V1_MappingIncludeDataProduct();
        mappingInclude.includedDataProduct =
            element.includedDataProduct.valueForSerialization ?? '';
        return mappingInclude;
    }
    else if (element instanceof MappingIncludeMapping) {
        const mappingInclude = new V1_MappingIncludeMapping();
        mappingInclude.includedMapping =
            element.included.valueForSerialization ?? '';
        mappingInclude.sourceDatabasePath = element.storeSubstitutions.length
            ? (element.storeSubstitutions[0].original
                .valueForSerialization ?? '')
            : undefined;
        mappingInclude.targetDatabasePath = element.storeSubstitutions.length
            ? (element.storeSubstitutions[0].substitute
                .valueForSerialization ?? '')
            : undefined;
        return mappingInclude;
    }
    const extraIncludeMappingTransformers = context.plugins.flatMap((plugin) => plugin.V1_getExtraMappingIncludeTransformers?.() ?? []);
    for (const transformer of extraIncludeMappingTransformers) {
        const mappingInclude = transformer(element, context);
        if (mappingInclude) {
            return mappingInclude;
        }
    }
    throw new UnsupportedOperationError(`Can't transform mapping include: no compatible transformer available from plugins`, element);
};
// Class Mapping
const transformClassMappingPropertyMappings = (values, isTransformingEmbeddedPropertyMapping, context) => values
    .filter((value) => {
    if (value instanceof INTERNAL__UnknownPropertyMapping) {
        return true;
    }
    else if (value instanceof PurePropertyMapping) {
        return !isStubbed_RawLambda(value.transform);
    }
    else if (value instanceof FlatDataPropertyMapping) {
        return !isStubbed_RawLambda(value.transform);
    }
    else if (value instanceof RelationalPropertyMapping) {
        return !isStubbed_RawRelationalOperationElement(value.relationalOperation);
    }
    else if (value instanceof RelationFunctionPropertyMapping) {
        return !isStubbed_RelationColumn(value.column);
    }
    const checkerResult = context.plugins
        .flatMap((plugin) => plugin.getExtraPropertyMappingTransformationExcludabilityCheckers?.() ??
        [])
        .map((checker) => checker(value))
        .filter(isNonNullable);
    return !checkerResult.length || checkerResult.some(Boolean);
})
    .map((value) => transformProperyMapping(value, isTransformingEmbeddedPropertyMapping, context));
const transformSimpleFlatDataPropertyMapping = (element, context) => {
    const flatDataPropertyMapping = new V1_FlatDataPropertyMapping();
    flatDataPropertyMapping.enumMappingId =
        element.transformer?.valueForSerialization;
    flatDataPropertyMapping.property = V1_transformPropertyReference(element.property);
    flatDataPropertyMapping.source =
        element.sourceSetImplementation.valueForSerialization;
    flatDataPropertyMapping.target =
        element.targetSetImplementation?.valueForSerialization;
    if (!isStubbed_RawLambda(element.transform)) {
        flatDataPropertyMapping.transform =
            element.transform.accept_RawValueSpecificationVisitor(new V1_RawValueSpecificationTransformer(context));
    }
    return flatDataPropertyMapping;
};
const transformFlatDataAssociationPropertyMapping = (element, context) => {
    const flatDataAssociationPropertyMapping = new V1_FlatDataAssociationPropertyMapping();
    flatDataAssociationPropertyMapping.property = V1_transformPropertyReference(element.property);
    flatDataAssociationPropertyMapping.source =
        element.sourceSetImplementation.valueForSerialization;
    flatDataAssociationPropertyMapping.target =
        element.targetSetImplementation?.valueForSerialization;
    flatDataAssociationPropertyMapping.flatData = element.flatData;
    flatDataAssociationPropertyMapping.sectionName = element.sectionName;
    return flatDataAssociationPropertyMapping;
};
const transformEmbeddedFlatDataPropertyMapping = (element, context) => {
    const embedded = new V1_EmbeddedFlatDataPropertyMapping();
    const id = mappingElementIdSerializer(element.id);
    if (id) {
        embedded.id = id;
    }
    embedded.class = element.class.valueForSerialization ?? '';
    embedded.property = V1_transformPropertyReference(element.property); // TODO: we might ned to turn 'isTransformingEmbeddedPropertyMapping' on in the future once we start working on the gramar roundtrip for flat-data
    embedded.propertyMappings = transformClassMappingPropertyMappings(element.propertyMappings, false, // TODO: we might ned to turn this on in the future once we start working on the gramar roundtrip for flat-data
    context);
    embedded.root = false;
    embedded.source = element.sourceSetImplementation.valueForSerialization;
    embedded.target = element.targetSetImplementation?.valueForSerialization;
    return embedded;
};
const transformPurePropertyMapping = (element, context) => {
    const purePropertyMapping = new V1_PurePropertyMapping();
    purePropertyMapping.enumMappingId =
        element.transformer?.valueForSerialization;
    purePropertyMapping.property = V1_transformPropertyReference(element.property, {
        isTransformingLocalPropertyMapping: Boolean(element.localMappingProperty),
    });
    purePropertyMapping.source =
        element.sourceSetImplementation.valueForSerialization;
    purePropertyMapping.target =
        element.targetSetImplementation?.valueForSerialization;
    if (!isStubbed_RawLambda(element.transform)) {
        purePropertyMapping.transform =
            element.transform.accept_RawValueSpecificationVisitor(new V1_RawValueSpecificationTransformer(context));
    }
    if (element.localMappingProperty) {
        purePropertyMapping.localMappingProperty = transformLocalPropertyInfo(element.localMappingProperty);
    }
    purePropertyMapping.explodeProperty = element.explodeProperty;
    return purePropertyMapping;
};
const transformRelationalPropertyMapping = (element, isTransformingEmbeddedPropertyMapping, context) => {
    const propertyMapping = new V1_RelationalPropertyMapping();
    propertyMapping.enumMappingId = element.transformer?.valueForSerialization;
    propertyMapping.property = V1_transformPropertyReference(element.property, {
        isTransformingEmbeddedPropertyMapping: isTransformingEmbeddedPropertyMapping,
        isTransformingLocalPropertyMapping: Boolean(element.localMappingProperty),
    });
    // Prune source information from the operation
    // NOTE: if in the future, source information is stored under different key,
    // e.g. { "classPointerSourceInformation": ... }
    // we need to use the prune source information method from `V1_PureGraphManager`
    propertyMapping.relationalOperation = (context.keepSourceInformation
        ? element.relationalOperation
        : pruneSourceInformation(element.relationalOperation));
    propertyMapping.source =
        element.sourceSetImplementation.valueForSerialization;
    propertyMapping.target =
        element.targetSetImplementation?.valueForSerialization;
    if (element.bindingTransformer?.binding) {
        const bindingTransformer = new V1_BindingTransformer();
        bindingTransformer.binding = guaranteeNonEmptyString(element.bindingTransformer.binding.valueForSerialization);
        propertyMapping.bindingTransformer = bindingTransformer;
    }
    if (element.localMappingProperty) {
        propertyMapping.localMappingProperty = transformLocalPropertyInfo(element.localMappingProperty);
    }
    return propertyMapping;
};
const transformEmbeddedRelationalPropertyMapping = (element, isTransformingEmbeddedPropertyMapping, context) => {
    const embedded = new V1_EmbeddedRelationalPropertyMapping();
    embedded.property = V1_transformPropertyReference(element.property, {
        isTransformingEmbeddedPropertyMapping: isTransformingEmbeddedPropertyMapping,
    });
    embedded.source = element.sourceSetImplementation.valueForSerialization;
    embedded.target = element.targetSetImplementation?.valueForSerialization;
    const classMapping = new V1_RelationalClassMapping();
    classMapping.primaryKey = element.primaryKey.map((pk) => V1_transformRelationalOperationElement(pk, context));
    classMapping.propertyMappings = transformClassMappingPropertyMappings(element.propertyMappings, true, context);
    classMapping.root = element.root.valueForSerialization;
    /**
     * Omit this information during protocol transformation as it can be
     * interpreted while building the graph; and will help grammar-roundtrip
     * tests (involving engine) to pass. Ideally, this requires grammar parser
     * and composer in engine to be more consistent.
     *
     * @discrepancy grammar-roundtrip
     */
    classMapping.class = undefined;
    embedded.classMapping = classMapping;
    if (element.localMappingProperty) {
        embedded.localMappingProperty = transformLocalPropertyInfo(element.localMappingProperty);
    }
    return embedded;
};
const transformInlineEmbeddedRelationalPropertyMapping = (element, isTransformingEmbeddedPropertyMapping) => {
    const embedded = new V1_InlineEmbeddedPropertyMapping();
    embedded.property = V1_transformPropertyReference(element.property, {
        isTransformingEmbeddedPropertyMapping: isTransformingEmbeddedPropertyMapping,
    });
    embedded.source = element.sourceSetImplementation.valueForSerialization;
    embedded.target = element.targetSetImplementation?.valueForSerialization;
    embedded.setImplementationId = element.inlineSetImplementation.id.value;
    if (element.localMappingProperty) {
        embedded.localMappingProperty = transformLocalPropertyInfo(element.localMappingProperty);
    }
    return embedded;
};
const transformOtherwiseEmbeddedRelationalPropertyMapping = (element, isTransformingEmbeddedPropertyMapping, context) => {
    const embedded = new V1_OtherwiseEmbeddedRelationalPropertyMapping();
    embedded.property = V1_transformPropertyReference(element.property, {
        isTransformingEmbeddedPropertyMapping: isTransformingEmbeddedPropertyMapping,
    });
    embedded.source = element.sourceSetImplementation.valueForSerialization;
    embedded.target = element.targetSetImplementation?.valueForSerialization;
    const classMapping = new V1_RelationalClassMapping();
    classMapping.primaryKey = element.primaryKey.map((pk) => V1_transformRelationalOperationElement(pk, context));
    classMapping.propertyMappings = transformClassMappingPropertyMappings(element.propertyMappings, true, context);
    classMapping.root = element.root.valueForSerialization;
    /**
     * Omit this information during protocol transformation as it can be
     * interpreted while building the graph; and will help grammar-roundtrip
     * tests (involving engine) to pass. Ideally, this requires grammar parser
     * and composer in engine to be more consistent.
     *
     * @discrepancy grammar-roundtrip
     */
    classMapping.class = undefined;
    embedded.classMapping = classMapping;
    embedded.otherwisePropertyMapping = transformProperyMapping(element.otherwisePropertyMapping, true, context);
    // use the same property as the parent otherwise
    embedded.otherwisePropertyMapping.property = embedded.property;
    if (element.localMappingProperty) {
        embedded.localMappingProperty = transformLocalPropertyInfo(element.localMappingProperty);
    }
    return embedded;
};
const transformXStorePropertyMapping = (element, context) => {
    const xstore = new V1_XStorePropertyMapping();
    xstore.property = V1_transformPropertyReference(element.property);
    xstore.source = element.sourceSetImplementation.valueForSerialization;
    xstore.target = element.targetSetImplementation?.valueForSerialization;
    if (!isStubbed_RawLambda(element.crossExpression)) {
        xstore.crossExpression =
            element.crossExpression.accept_RawValueSpecificationVisitor(new V1_RawValueSpecificationTransformer(context));
    }
    if (element.localMappingProperty) {
        xstore.localMappingProperty = transformLocalPropertyInfo(element.localMappingProperty);
    }
    return xstore;
};
const transformAggregationAwarePropertyMapping = (element) => {
    const propertyMapping = new V1_AggregationAwarePropertyMapping();
    propertyMapping.property = V1_transformPropertyReference(element.property);
    propertyMapping.source =
        element.sourceSetImplementation.valueForSerialization;
    propertyMapping.target =
        element.targetSetImplementation?.valueForSerialization;
    if (element.localMappingProperty) {
        propertyMapping.localMappingProperty = transformLocalPropertyInfo(element.localMappingProperty);
    }
    return propertyMapping;
};
const transformRelationFunctionPropertyMapping = (element) => {
    const propertyMapping = new V1_RelationFunctionPropertyMapping();
    propertyMapping.column = element.column.name;
    if (element.bindingTransformer?.binding) {
        const bindingTransformer = new V1_BindingTransformer();
        bindingTransformer.binding = guaranteeNonEmptyString(element.bindingTransformer.binding.valueForSerialization);
        propertyMapping.bindingTransformer = bindingTransformer;
    }
    propertyMapping.property = V1_transformPropertyReference(element.property);
    propertyMapping.source =
        element.sourceSetImplementation.valueForSerialization;
    propertyMapping.target =
        element.targetSetImplementation?.valueForSerialization;
    if (element.localMappingProperty) {
        propertyMapping.localMappingProperty = transformLocalPropertyInfo(element.localMappingProperty);
    }
    propertyMapping.column = element.column.name;
    return propertyMapping;
};
class PropertyMappingTransformer {
    isTransformingEmbeddedPropertyMapping = false;
    context;
    constructor(isTransformingEmbeddedPropertyMapping, context) {
        this.isTransformingEmbeddedPropertyMapping =
            isTransformingEmbeddedPropertyMapping;
        this.context = context;
    }
    visit_PropertyMapping(propertyMapping) {
        const extraPropertyMappingTransformers = this.context.plugins.flatMap((plugin) => plugin.V1_getExtraPropertyMappingTransformers?.() ?? []);
        for (const transformer of extraPropertyMappingTransformers) {
            const propertyMappingProtocol = transformer(propertyMapping, this.context);
            if (propertyMappingProtocol) {
                return propertyMappingProtocol;
            }
        }
        throw new UnsupportedOperationError(`Can't transform property mapping: no compatible transformer available from plugins`, propertyMapping);
    }
    visit_INTERNAL__UnknownPropertyMapping(propertyMapping) {
        const protocol = new V1_INTERNAL__UnknownPropertyMapping();
        protocol.content = propertyMapping.content;
        return protocol;
    }
    visit_PurePropertyMapping(propertyMapping) {
        return transformPurePropertyMapping(propertyMapping, this.context);
    }
    visit_FlatDataPropertyMapping(propertyMapping) {
        return transformSimpleFlatDataPropertyMapping(propertyMapping, this.context);
    }
    visit_FlatDataAssociationPropertyMapping(propertyMapping) {
        return transformFlatDataAssociationPropertyMapping(propertyMapping, this.context);
    }
    visit_EmbeddedFlatDataPropertyMapping(propertyMapping) {
        return transformEmbeddedFlatDataPropertyMapping(propertyMapping, this.context);
    }
    visit_RelationalPropertyMapping(propertyMapping) {
        return transformRelationalPropertyMapping(propertyMapping, this.isTransformingEmbeddedPropertyMapping, this.context);
    }
    visit_EmbeddedRelationalPropertyMapping(propertyMapping) {
        return transformEmbeddedRelationalPropertyMapping(propertyMapping, this.isTransformingEmbeddedPropertyMapping, this.context);
    }
    visit_InlineEmbeddedRelationalPropertyMapping(propertyMapping) {
        return transformInlineEmbeddedRelationalPropertyMapping(propertyMapping, this.isTransformingEmbeddedPropertyMapping);
    }
    visit_OtherwiseEmbeddedRelationalPropertyMapping(propertyMapping) {
        return transformOtherwiseEmbeddedRelationalPropertyMapping(propertyMapping, this.isTransformingEmbeddedPropertyMapping, this.context);
    }
    visit_XStorePropertyMapping(propertyMapping) {
        return transformXStorePropertyMapping(propertyMapping, this.context);
    }
    visit_AggregationAwarePropertyMapping(propertyMapping) {
        return transformAggregationAwarePropertyMapping(propertyMapping);
    }
    visit_RelationFunctionPropertyMapping(propertyMapping) {
        return transformRelationFunctionPropertyMapping(propertyMapping);
    }
}
const transformOperationType = (value) => {
    switch (value) {
        case OperationType.ROUTER_UNION:
            return V1_MappingOperationType.ROUTER_UNION;
        case OperationType.STORE_UNION:
            return V1_MappingOperationType.STORE_UNION;
        case OperationType.INHERITANCE:
            return V1_MappingOperationType.INHERITANCE;
        case OperationType.MERGE:
            return V1_MappingOperationType.MERGE;
        default:
            throw new UnsupportedOperationError(`Can't transform operation type`, value);
    }
};
const transformOperationSetImplementation = (element) => {
    const classMapping = new V1_OperationClassMapping();
    classMapping.class = element.class.valueForSerialization ?? '';
    classMapping.id = mappingElementIdSerializer(element.id);
    classMapping.operation = transformOperationType(element.operation);
    classMapping.parameters = element.parameters.map((e) => e.setImplementation.value.id.value);
    classMapping.root = element.root.valueForSerialization;
    classMapping.extendsClassMappingId = element.superSetImplementationId;
    return classMapping;
};
const transformMergeOperationSetImplementation = (element, context) => {
    const classMapping = new V1_MergeOperationClassMapping();
    classMapping.class = element.class.valueForSerialization ?? '';
    classMapping.id = mappingElementIdSerializer(element.id);
    classMapping.operation = transformOperationType(element.operation);
    classMapping.validationFunction =
        element.validationFunction.accept_RawValueSpecificationVisitor(new V1_RawValueSpecificationTransformer(context));
    classMapping.parameters = element.parameters.map((e) => e.setImplementation.value.id.value);
    classMapping.extendsClassMappingId = element.superSetImplementationId;
    classMapping.root = element.root.valueForSerialization;
    return classMapping;
};
const transformPureInstanceSetImplementation = (element, context) => {
    const classMapping = new V1_PureInstanceClassMapping();
    classMapping.class = element.class.valueForSerialization ?? '';
    if (element.filter) {
        classMapping.filter = element.filter.accept_RawValueSpecificationVisitor(new V1_RawValueSpecificationTransformer(context));
    }
    classMapping.id = mappingElementIdSerializer(element.id);
    classMapping.propertyMappings = transformClassMappingPropertyMappings(element.propertyMappings, true, context);
    classMapping.extendsClassMappingId = element.superSetImplementationId;
    classMapping.root = element.root.valueForSerialization;
    classMapping.srcClass = element.srcClass?.valueForSerialization;
    return classMapping;
};
const transformFlatDataInstanceSetImpl = (element, context) => {
    const classMapping = new V1_RootFlatDataClassMapping();
    classMapping.class = element.class.valueForSerialization ?? '';
    if (element.filter) {
        classMapping.filter = element.filter.accept_RawValueSpecificationVisitor(new V1_RawValueSpecificationTransformer(context));
    }
    classMapping.flatData =
        element.sourceRootRecordType.ownerReference.valueForSerialization ?? '';
    classMapping.id = mappingElementIdSerializer(element.id);
    classMapping.propertyMappings = transformClassMappingPropertyMappings(element.propertyMappings, true, context);
    classMapping.root = element.root.valueForSerialization;
    classMapping.extendsClassMappingId = element.superSetImplementationId;
    classMapping.sectionName = element.sourceRootRecordType.value._OWNER.name;
    return classMapping;
};
const transformRootRelationalSetImpl = (element, context) => {
    const classMapping = new V1_RootRelationalClassMapping();
    classMapping.class = element.class.valueForSerialization ?? '';
    classMapping.distinct = element.distinct ?? false;
    classMapping.id = mappingElementIdSerializer(element.id);
    if (element.mainTableAlias) {
        classMapping.mainTable = V1_transformTableAliasToTablePointer(element.mainTableAlias);
    }
    classMapping.extendsClassMappingId = element.superSetImplementationId;
    classMapping.primaryKey = element.primaryKey.map((pk) => V1_transformRelationalOperationElement(pk, context));
    if (element.groupBy) {
        classMapping.groupBy = element.groupBy.columns.map((pk) => V1_transformRelationalOperationElement(pk, context));
    }
    if (element.filter) {
        const filter = new V1_FilterMapping();
        const filterPointer = new V1_FilterPointer();
        filterPointer.db = element.filter.database.path;
        filterPointer.name = element.filter.filterName;
        filter.filter = filterPointer;
        filter.joins = element.filter.joinTreeNode
            ? extractLine(element.filter.joinTreeNode).map((node) => {
                const joinPtr = new V1_JoinPointer();
                joinPtr.db = node.join.ownerReference.valueForSerialization ?? '';
                joinPtr.joinType = node.joinType;
                joinPtr.name = node.join.value.name;
                return joinPtr;
            })
            : [];
        classMapping.filter = filter;
    }
    classMapping.propertyMappings = transformClassMappingPropertyMappings(element.propertyMappings, false, context);
    classMapping.root = element.root.valueForSerialization;
    return classMapping;
};
const transformRelationalInstanceSetImpl = (element, context) => {
    const classMapping = new V1_RelationalClassMapping();
    classMapping.primaryKey = element.primaryKey.map((pk) => V1_transformRelationalOperationElement(pk, context));
    classMapping.extendsClassMappingId = element.superSetImplementationId;
    classMapping.propertyMappings = transformClassMappingPropertyMappings(element.propertyMappings, true, context);
    classMapping.root = element.root.valueForSerialization;
    /**
     * Omit this information during protocol transformation as it can be
     * interpreted while building the graph; and will help grammar-roundtrip
     * tests (involving engine) to pass. Ideally, this requires grammar parser
     * and composer in engine to be more consistent.
     *
     * @discrepancy grammar-roundtrip
     */
    classMapping.class = undefined;
    return classMapping;
};
const transformAggregationFunctionSpecification = (element, context) => {
    const func = new V1_AggregateFunction();
    if (!isStubbed_RawLambda(element.mapFn)) {
        func.mapFn = element.mapFn.accept_RawValueSpecificationVisitor(new V1_RawValueSpecificationTransformer(context));
    }
    if (!isStubbed_RawLambda(element.aggregateFn)) {
        func.aggregateFn = element.aggregateFn.accept_RawValueSpecificationVisitor(new V1_RawValueSpecificationTransformer(context));
    }
    return func;
};
const transformGroupByFunctionSpec = (element, context) => {
    const func = new V1_GroupByFunction();
    if (!isStubbed_RawLambda(element.groupByFn)) {
        func.groupByFn = element.groupByFn.accept_RawValueSpecificationVisitor(new V1_RawValueSpecificationTransformer(context));
    }
    return func;
};
const transformAggregateSpecification = (element, context) => {
    const aggregateSpec = new V1_AggregateSpecification();
    aggregateSpec.canAggregate = element.canAggregate;
    aggregateSpec.groupByFunctions = element.groupByFunctions.map((groupByFunc) => transformGroupByFunctionSpec(groupByFunc, context));
    aggregateSpec.aggregateValues = element.aggregateValues.map((aggregateValue) => transformAggregationFunctionSpecification(aggregateValue, context));
    return aggregateSpec;
};
const transformAggSetImplContainer = (element, context) => {
    const setImplContainer = new V1_AggregateSetImplementationContainer();
    setImplContainer.index = element.index;
    const classMapping = transformSetImplementation(element.setImplementation, context);
    if (classMapping) {
        setImplContainer.setImplementation = classMapping;
    }
    setImplContainer.aggregateSpecification = transformAggregateSpecification(element.aggregateSpecification, context);
    return setImplContainer;
};
const transformAggregationAwareSetImplementation = (element, context) => {
    const classMapping = new V1_AggregationAwareClassMapping();
    classMapping.id = mappingElementIdSerializer(element.id);
    classMapping.class = element.class.valueForSerialization ?? '';
    classMapping.root = element.root.valueForSerialization;
    const mainSetImplementation = transformSetImplementation(element.mainSetImplementation, context);
    if (mainSetImplementation) {
        classMapping.mainSetImplementation = mainSetImplementation;
    }
    classMapping.propertyMappings = transformClassMappingPropertyMappings(element.propertyMappings, false, context);
    classMapping.extendsClassMappingId = element.superSetImplementationId;
    classMapping.aggregateSetImplementations =
        element.aggregateSetImplementations.map((aggregateSetImpl) => transformAggSetImplContainer(aggregateSetImpl, context));
    return classMapping;
};
const transformRelationFunctionInstanceSetImplementation = (element, context) => {
    const functionName = generateFunctionPrettyName(guaranteeNonNullable(element.relationFunction), {
        fullPath: true,
        spacing: false,
        notIncludeParamName: true,
    });
    const classMapping = new V1_RelationFunctionClassMapping();
    classMapping.relationFunction = new V1_PackageableElementPointer(PackageableElementPointerType.FUNCTION, functionName);
    classMapping.id = mappingElementIdSerializer(element.id);
    classMapping.class = element.class.valueForSerialization ?? '';
    classMapping.root = element.root.valueForSerialization;
    classMapping.propertyMappings = transformClassMappingPropertyMappings(element.propertyMappings, false, context);
    classMapping.extendsClassMappingId = element.superSetImplementationId;
    return classMapping;
};
// NOTE: this needs to be a function to avoid error with using before declaration for embedded property mappings due to the hoisting behavior in ES
function transformProperyMapping(propertyMapping, isTransformingEmbeddedPropertyMapping, context) {
    return propertyMapping.accept_PropertyMappingVisitor(new PropertyMappingTransformer(isTransformingEmbeddedPropertyMapping, context));
}
export class V1_SetImplementationTransformer {
    context;
    constructor(context) {
        this.context = context;
    }
    visit_SetImplementation(setImplementation) {
        const extraClassMappingTransformers = this.context.plugins.flatMap((plugin) => plugin.V1_getExtraClassMappingTransformers?.() ?? []);
        for (const transformer of extraClassMappingTransformers) {
            const classMapping = transformer(setImplementation, this.context);
            if (classMapping) {
                return classMapping;
            }
        }
        throw new UnsupportedOperationError(`Can't transform class mapping: no compatible transformer available from plugins`, setImplementation);
    }
    visit_INTERNAL__UnknownSetImplementation(setImplementation) {
        const protocol = new V1_INTERNAL__UnknownClassMapping();
        protocol.class = setImplementation.class.valueForSerialization ?? '';
        protocol.id = mappingElementIdSerializer(setImplementation.id);
        protocol.root = setImplementation.root.valueForSerialization;
        protocol.content = setImplementation.content;
        return protocol;
    }
    visit_OperationSetImplementation(setImplementation) {
        if (setImplementation instanceof MergeOperationSetImplementation) {
            return transformMergeOperationSetImplementation(setImplementation, this.context);
        }
        else {
            return transformOperationSetImplementation(setImplementation);
        }
    }
    visit_MergeOperationSetImplementation(setImplementation) {
        return transformMergeOperationSetImplementation(setImplementation, this.context);
    }
    visit_PureInstanceSetImplementation(setImplementation) {
        return transformPureInstanceSetImplementation(setImplementation, this.context);
    }
    visit_FlatDataInstanceSetImplementation(setImplementation) {
        return transformFlatDataInstanceSetImpl(setImplementation, this.context);
    }
    visit_EmbeddedFlatDataSetImplementation(setImplementation) {
        // NOTE: we currently don't support this and flat-data would probably be deprecated
        // in the future in favor of schema-set/binding
        return undefined;
    }
    visit_RootRelationalInstanceSetImplementation(setImplementation) {
        return transformRootRelationalSetImpl(setImplementation, this.context);
    }
    visit_RelationalInstanceSetImplementation(setImplementation) {
        return transformRelationalInstanceSetImpl(setImplementation, this.context);
    }
    visit_AggregationAwareSetImplementation(setImplementation) {
        return transformAggregationAwareSetImplementation(setImplementation, this.context);
    }
    visit_RelationFunctionInstanceSetImplementation(setImplementation) {
        return transformRelationFunctionInstanceSetImplementation(setImplementation, this.context);
    }
    /**
     * This test is skipped because we want to temporarily relax graph building algorithm
     * to ease Pure -> Legend migration push.
     * See https://github.com/finos/legend-studio/issues/880
     *
     * @discrepancy graph-building
     */
    visit_INTERNAL__UnresolvedSetImplementation(setImplementation) {
        throw new IllegalStateError(`Can't transform unresolved set implementation. This type of set implementation should only show up in references.`);
    }
}
function transformSetImplementation(setImplementation, context) {
    return setImplementation.accept_SetImplementationVisitor(new V1_SetImplementationTransformer(context));
}
// Association V1_Mapping
const transformRelationalAssociationImplementation = (element, context) => {
    const relationalMapping = new V1_RelationalAssociationMapping();
    relationalMapping.stores = element.stores.map((store) => store.valueForSerialization ?? '');
    relationalMapping.association = new V1_PackageableElementPointer(PackageableElementPointerType.ASSOCIATION, element.association.valueForSerialization ?? '');
    relationalMapping.propertyMappings = transformClassMappingPropertyMappings(element.propertyMappings, true, context);
    relationalMapping.id = mappingElementIdSerializer(element.id);
    return relationalMapping;
};
const transformFlatDataAssociationImplementation = (element, context) => {
    const flatDataAssociationMapping = new V1_FlatDataAssociationMapping();
    flatDataAssociationMapping.stores = element.stores.map((store) => store.valueForSerialization ?? '');
    flatDataAssociationMapping.association = new V1_PackageableElementPointer(PackageableElementPointerType.ASSOCIATION, element.association.valueForSerialization ?? '');
    flatDataAssociationMapping.propertyMappings =
        transformClassMappingPropertyMappings(element.propertyMappings, true, context);
    flatDataAssociationMapping.id = mappingElementIdSerializer(element.id);
    return flatDataAssociationMapping;
};
const transformXStorelAssociationImplementation = (element, context) => {
    const xStoreMapping = new V1_XStoreAssociationMapping();
    xStoreMapping.stores = element.stores.map((store) => store.valueForSerialization ?? '');
    xStoreMapping.association = new V1_PackageableElementPointer(PackageableElementPointerType.ASSOCIATION, element.association.valueForSerialization ?? '');
    xStoreMapping.propertyMappings = transformClassMappingPropertyMappings(element.propertyMappings, false, context);
    xStoreMapping.id = mappingElementIdSerializer(element.id);
    return xStoreMapping;
};
const transformAssociationImplementation = (element, context) => {
    if (element instanceof RelationalAssociationImplementation) {
        return transformRelationalAssociationImplementation(element, context);
    }
    else if (element instanceof XStoreAssociationImplementation) {
        return transformXStorelAssociationImplementation(element, context);
    }
    else if (element instanceof FlatDataAssociationImplementation) {
        return transformFlatDataAssociationImplementation(element, context);
    }
    throw new UnsupportedOperationError(`Can't transform association implementation`, element);
};
export const V1_transformMapping = (element, context) => {
    const mapping = new V1_Mapping();
    V1_initPackageableElement(mapping, element);
    mapping.includedMappings = element.includes.map((include) => transformMappingInclude(include, context));
    mapping.enumerationMappings = element.enumerationMappings.map(transformEnumerationMapping);
    mapping.classMappings = element.classMappings
        .map((classMapping) => transformSetImplementation(classMapping, context))
        .filter(isNonNullable);
    mapping.associationMappings = element.associationMappings.map((associationMapping) => transformAssociationImplementation(associationMapping, context));
    mapping.tests = element.test.map((test) => transformMappingTestLegacy(test, context));
    mapping.testSuites = element.tests.map((testSuite) => V1_transformTestSuite(testSuite, context));
    return mapping;
};
//# sourceMappingURL=V1_MappingTransformer.js.map