/**
 * 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 { StoreConnections, PackageableElementExplicitReference, Enumeration, SourceValue, getOwnClassMappingsByClass, observe_SetImplementation, observe_EnumerationMapping, observe_AssociationImplementation, observe_MappingTest_Legacy, observe_SourceValue, observe_MappingTestAssert, observe_SetImplementationContainer, observe_IdentifiedConnection, observe_StoreConnections, observe_PackageableElementReference, observe_PackageableRuntime, observe_PropertyMapping, observe_EnumValueMapping, observe_InputData, observe_RawLambda, observe_PurePropertyMapping, observe_Class, observe_Connection, observe_EngineRuntime, getEnumValueNames, getEnumValue, observe_EnumerationMappingReference, PrimitiveType, observe_MappingTestSuite, observe_EmbeddedData, } from '@finos/legend-graph';
import { addUniqueEntry, assertTrue, changeEntry, deleteEntry, guaranteeNonNullable, } from '@finos/legend-shared';
import { action } from 'mobx';
export const instanceSetImplementation_setPropertyMappings = action((si, pm, observeContext) => {
    si.propertyMappings = pm.map((p) => observe_PropertyMapping(p, observeContext));
});
export const instanceSetImplementation_deletePropertyMapping = action((si, pm) => {
    deleteEntry(si.propertyMappings, pm);
});
export const setImplementation_setRoot = action((owner, val) => {
    owner.root.value = val;
});
export const mapping_addClassMapping = action((mapping, val, observer) => {
    addUniqueEntry(mapping.classMappings, observe_SetImplementation(val, observer));
});
export const mapping_deleteClassMapping = action((mapping, val) => {
    deleteEntry(mapping.classMappings, val);
});
export const mapping_addEnumerationMapping = action((mapping, val) => {
    addUniqueEntry(mapping.enumerationMappings, observe_EnumerationMapping(val));
});
export const mapping_deleteEnumerationMapping = action((mapping, val) => {
    deleteEntry(mapping.enumerationMappings, val);
});
export const mapping_addAssociationMapping = action((mapping, val, observerContext) => {
    addUniqueEntry(mapping.associationMappings, observe_AssociationImplementation(val, observerContext));
});
export const mapping_deleteAssociationMapping = action((mapping, val) => {
    deleteEntry(mapping.associationMappings, val);
});
export const mapping_deleteTest = action((mapping, val) => {
    deleteEntry(mapping.test, val);
});
export const mapping_addDEPRECATEDTest = action((mapping, val, observerContext) => {
    addUniqueEntry(mapping.test, observe_MappingTest_Legacy(val, observerContext));
});
// --------------------------------------------- Testable -------------------------------------
export const mapping_addTestSuite = action((mapping, val, observerContext) => {
    addUniqueEntry(mapping.tests, observe_MappingTestSuite(val, observerContext));
});
export const mapping_deleteTestSuite = action((mapping, val) => {
    deleteEntry(mapping.tests, val);
});
export const mappingTestable_setQuery = action((test, value) => {
    test.func = observe_RawLambda(value);
});
export const mappingTestable_deleteStoreTestData = action((dataHolder, val) => {
    deleteEntry(dataHolder.storeTestData, val);
});
export const mappingTestable_setEmbeddedData = action((store, embeddedData, observerContext) => {
    store.data = observe_EmbeddedData(embeddedData, observerContext);
});
export const mappingTestable_addStoreTestData = action((dataHolder, val) => {
    addUniqueEntry(dataHolder.storeTestData, val);
});
// --------------------------------------------- Enumeration Mapping -------------------------------------
export const enumerationMapping_setId = action((eM, val) => {
    eM.id.value = val;
});
export const enumerationMapping_setSourceType = action((eM, value) => {
    eM.sourceType = value
        ? observe_PackageableElementReference(value)
        : undefined;
});
export const enumerationMapping_setEnumValueMappings = action((eM, value) => {
    eM.enumValueMappings = value.map(observe_EnumValueMapping);
});
export const enumerationMapping_updateSourceType = action((eM, value) => {
    if (eM.sourceType?.value !== value?.value) {
        enumerationMapping_setSourceType(eM, value);
        eM.enumValueMappings = eM.enumValueMappings.map((enumValueMapping) => {
            enumValueMapping.sourceValues = [];
            enumValueMapping.sourceValues.push(observe_SourceValue(new SourceValue(undefined)));
            return enumValueMapping;
        });
    }
});
export const sourceValue_setValue = action((sv, value) => {
    sv.value = value;
});
export const enumValueMapping_addSourceValue = action((enumMapping, sourceValue) => {
    enumMapping.sourceValues.push(observe_SourceValue(new SourceValue(sourceValue)));
});
export const enumValueMapping_setSourceValues = action((enumMapping, value) => {
    enumMapping.sourceValues = value.map(observe_SourceValue);
});
export const enumValueMapping_deleteSourceValue = action((enumMapping, idx) => {
    enumMapping.sourceValues.splice(idx, 1);
});
export const enumValueMapping_updateSourceValue = action((enumMapping, idx, val, sourceType) => {
    const sourceValue = guaranteeNonNullable(enumMapping.sourceValues[idx]);
    // If the source type is an enumeration but the value does NOT match an enum value (most likely user is mid typing an enum value)
    // we move on to update the source value with the string value
    if (sourceType instanceof Enumeration &&
        typeof val === 'string' &&
        getEnumValueNames(sourceType).includes(val)) {
        sourceValue_setValue(sourceValue, getEnumValue(sourceType, val));
    }
    else {
        // Here we update the source values depending on the source type.
        sourceValue_setValue(sourceValue, sourceType === PrimitiveType.INTEGER
            ? parseInt(val, 10)
            : val);
    }
});
// --------------------------------------------- Mapping Test -------------------------------------
export const mappingTest_setName = action((test, value) => {
    test.name = value;
});
export const mappingTest_setInputData = action((test, value, observeContext) => {
    test.inputData = value.map((i) => observe_InputData(i, observeContext));
});
export const DEPRECATED_mappingTest_setQuery = action((test, value) => {
    test.query = observe_RawLambda(value);
});
export const mappingTest_setAssert = action((test, value, observerContext) => {
    test.assert = observe_MappingTestAssert(value, observerContext);
});
export const expectedOutputMappingTestAssert_setExpectedOutput = action((e, val) => {
    e.expectedOutput = val;
});
// --------------------------------------------- Operation Mapping -------------------------------------
export const operationMapping_setOperation = action((oI, value) => {
    oI.operation = value;
});
export const operationMapping_setParameters = action((oI, value) => {
    oI.parameters = value.map(observe_SetImplementationContainer);
});
export const operationMapping_addParameter = action((oI, value) => {
    addUniqueEntry(oI.parameters, observe_SetImplementationContainer(value));
});
export const operationMapping_changeParameter = action((oI, oldValue, newValue) => {
    changeEntry(oI.parameters, oldValue, observe_SetImplementationContainer(newValue));
});
export const operationMapping_deleteParameter = action((oI, value) => {
    deleteEntry(oI.parameters, value);
});
// --------------------------------------------- Root Resolution -------------------------------------
/**
 * If this is the only mapping element for the target class, automatically mark it as root,
 * otherwise, if there is another set implementation make it non-root,
 * otherwise, leave other set implementation root status as-is.
 * NOTE: use get `OWN` class mappings as we are smartly updating the current mapping in the form editor,
 * which does not support `include` mappings as of now.
 */
export const setImpl_updateRootOnCreate = action((setImp) => {
    const classMappingsWithSimilarTarget = getOwnClassMappingsByClass(setImp._PARENT, setImp.class.value).filter((si) => si !== setImp);
    if (classMappingsWithSimilarTarget.length) {
        setImplementation_setRoot(setImp, false);
        if (classMappingsWithSimilarTarget.length === 1) {
            setImplementation_setRoot(classMappingsWithSimilarTarget[0], false);
        }
    }
    else {
        setImplementation_setRoot(setImp, true);
    }
});
/**
 * If only one set implementation remained, it will be nominated as the new root
 * NOTE: use get `OWN` class mappings as we are smartly updating the current mapping in the form editor,
 * which does not support `include` mappings as of now.
 */
export const setImpl_updateRootOnDelete = action((setImp) => {
    const classMappingsWithSimilarTarget = getOwnClassMappingsByClass(setImp._PARENT, setImp.class.value).filter((si) => si !== setImp);
    if (classMappingsWithSimilarTarget.length === 1) {
        setImplementation_setRoot(classMappingsWithSimilarTarget[0], false);
    }
});
/**
 * Make the nominated set implementation root and flip the root flag of all other
 * set implementations with the same target
 * NOTE: use get `OWN` class mappings as we are smartly updating the current mapping in the form editor,
 * which does not support `include` mappings as of now.
 */
export const setImpl_nominateRoot = action((setImp) => {
    const classMappingsWithSimilarTarget = getOwnClassMappingsByClass(setImp._PARENT, setImp.class.value);
    classMappingsWithSimilarTarget.forEach((si) => {
        if (si !== setImp) {
            setImplementation_setRoot(si, false);
        }
    });
    setImplementation_setRoot(setImp, true);
});
// --------------------------------------------- M2M -------------------------------------
export const objectInputData_setData = action((o, val) => {
    o.data = val;
});
export const pureInstanceSetImpl_setPropertyMappings = action((val, value, observeContext) => {
    val.propertyMappings = value.map((pm) => observe_PurePropertyMapping(pm, observeContext));
});
export const pureInstanceSetImpl_setSrcClass = action((val, value) => {
    val.srcClass = value
        ? observe_PackageableElementReference(value)
        : undefined;
});
export const pureInstanceSetImpl_setMappingFilter = action((val, value) => {
    val.filter = value ? observe_RawLambda(value) : undefined;
});
export const purePropertyMapping_setTransformer = action((val, value) => {
    val.transformer = value
        ? observe_EnumerationMappingReference(value)
        : undefined;
});
// --------------------------------------------- Connection -------------------------------------
export const connection_setStore = action((con, val) => {
    con.store = observe_PackageableElementReference(val);
});
export const modelConnection_setClass = action((val, value) => {
    val.class.value = observe_Class(value);
});
export const modelConnection_setUrl = action((val, value) => {
    val.url = value;
});
export const packageableConnection_setConnectionValue = action((pc, connection, observeContext) => {
    pc.connectionValue = observe_Connection(connection, observeContext);
});
// --------------------------------------------- Runtime -------------------------------------
export const packageableRuntime_setRuntimeValue = action((pr, value, observeContext) => {
    pr.runtimeValue = observe_EngineRuntime(value, observeContext);
});
export const runtime_addIdentifiedConnection = action((eR, value, observerContext) => {
    observe_IdentifiedConnection(value, observerContext);
    const store = value.connection.store;
    if (store) {
        const storeConnections = eR.connections.find((sc) => sc.store.value === store.value) ??
            observe_StoreConnections(new StoreConnections(store), observerContext);
        addUniqueEntry(eR.connections, storeConnections);
        assertTrue(!storeConnections.storeConnections
            .map((connection) => connection.id)
            .includes(value.id), `Can't add identified connection as a connection with the same ID '${value.id} already existed'`);
        addUniqueEntry(storeConnections.storeConnections, value);
    }
});
export const runtime_deleteIdentifiedConnection = action((eR, value) => {
    const storeConnections = eR.connections.find((sc) => sc.store.value === value.connection.store?.value);
    if (storeConnections) {
        deleteEntry(storeConnections.storeConnections, value);
    }
});
export const runtime_addUniqueStoreConnectionsForStore = action((eR, value, observerContext) => {
    if (!eR.connections.find((sc) => sc.store.value === value)) {
        eR.connections.push(observe_StoreConnections(new StoreConnections(PackageableElementExplicitReference.create(value)), observerContext));
    }
});
export const runtime_setMappings = action((eR, value) => {
    eR.mappings = value.map(observe_PackageableElementReference);
});
export const runtime_addMapping = action((eR, value) => {
    addUniqueEntry(eR.mappings, observe_PackageableElementReference(value));
});
export const runtime_deleteMapping = action((eR, value) => {
    deleteEntry(eR.mappings, value);
});
export const setPackageableRuntime = action((rP, value, context) => {
    rP.packageableRuntime.value = observe_PackageableRuntime(value, context);
});
//# sourceMappingURL=DSL_Mapping_GraphModifierHelper.js.map