/**
 * 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 { FlatDataInputData, MappingTestSuite, MappingTest, EqualToJson, StoreTestData, DEPRECATED__ObjectInputData, PackageableElementExplicitReference, ModelStore, ModelStoreData, ModelEmbeddedData, ExternalFormatData, ObjectInputType, DEPRECATED__ExpectedOutputMappingTestAssert, RelationalInputData, } from '@finos/legend-graph';
import { ContentType, guaranteeNonNullable, uniq, guaranteeType, ActionState, assertErrorThrown, } from '@finos/legend-shared';
import { action, computed, flow, makeObservable, observable } from 'mobx';
import { mapping_addDEPRECATEDTest, mapping_addTestSuite, mapping_deleteTest, mapping_deleteTestSuite, } from '../../../../../graph-modifier/DSL_Mapping_GraphModifierHelper.js';
var UNSUPPORTED_REASON;
(function (UNSUPPORTED_REASON) {
    UNSUPPORTED_REASON["FLAT_DATA"] = "FLAT_DATA";
    UNSUPPORTED_REASON["MULTI_INPUT_DATA"] = "MULTI_INPUT_DATA";
    UNSUPPORTED_REASON["RELATIONAL"] = "RELATIONAL";
})(UNSUPPORTED_REASON || (UNSUPPORTED_REASON = {}));
class SupportedMigrationTestState {
    test;
    inputData;
    constructor(test, inputData) {
        this.test = test;
        this.inputData = inputData;
    }
}
export var MIGRATE_PHASE;
(function (MIGRATE_PHASE) {
    MIGRATE_PHASE["OVERVIEW"] = "OVERVIEW";
    MIGRATE_PHASE["CONFIRM"] = "CONFIRM";
})(MIGRATE_PHASE || (MIGRATE_PHASE = {}));
export class MappingTestConfirmationState {
    editorStore;
    migrationState;
    before;
    after;
    // diffs
    suitesToAdd;
    testsToDelete;
    migrated = false;
    calculatingDiffs = ActionState.create();
    constructor(migrationState) {
        makeObservable(this, {
            init: flow,
            migrate: action,
            reverse: action,
            before: observable,
            after: observable,
            suitesToAdd: observable,
            testsToDelete: observable,
        });
        this.migrationState = migrationState;
        this.editorStore = migrationState.editorStore;
        this.suitesToAdd = migrationState.migrate();
        this.testsToDelete = this.migrationState.migrateableTests;
        this.init();
    }
    *init() {
        try {
            this.calculatingDiffs.inProgress();
            // before
            const beforeEntity = this.migrationState.editorStore.graphManagerState.graphManager.elementToEntity(this.migrationState.mapping);
            this.migrate();
            // after
            const after = this.migrationState.editorStore.graphManagerState.graphManager.elementToEntity(this.migrationState.mapping);
            const beforeP = this.editorStore.graphManagerState.graphManager.entitiesToPureCode([
                beforeEntity,
            ]);
            const afterP = this.editorStore.graphManagerState.graphManager.entitiesToPureCode([
                after,
            ]);
            const [beforeGrammar, afterGrammar] = (yield Promise.all([
                beforeP,
                afterP,
            ]));
            // done
            this.before = beforeGrammar;
            this.after = afterGrammar;
            this.reverse();
        }
        catch (error) {
            assertErrorThrown(error);
            this.editorStore.applicationStore.notificationService.notifyError(`Error calcuating mapping diffs: ${error.message}`);
        }
        finally {
            this.calculatingDiffs.complete();
        }
    }
    migrate() {
        // add
        this.suitesToAdd.forEach((suite) => mapping_addTestSuite(this.migrationState.mapping, suite, this.editorStore.changeDetectionState.observerContext));
        // delete
        this.testsToDelete.forEach((test) => mapping_deleteTest(this.migrationState.mapping, test));
        this.migrated = true;
    }
    reverse() {
        // add
        this.suitesToAdd.forEach((suite) => mapping_deleteTestSuite(this.migrationState.mapping, suite));
        // delete
        this.testsToDelete.forEach((test) => mapping_addDEPRECATEDTest(this.migrationState.mapping, test, this.editorStore.changeDetectionState.observerContext));
        this.migrated = false;
    }
}
export class MappingTestMigrationState {
    editorStore;
    mappingEditorState;
    unsupported;
    queryToSuiteMap;
    suitesAdded;
    // phases
    steps = [
        MIGRATE_PHASE.OVERVIEW,
        MIGRATE_PHASE.CONFIRM,
    ];
    currentStep = MIGRATE_PHASE.OVERVIEW;
    confirmationState;
    constructor(editorStore, mappingEditorState, unsupported, supported) {
        makeObservable(this, {
            suitesAdded: observable,
            currentStep: observable,
            confirmationState: observable,
            handleNext: action,
            activeStep: computed,
            disableBack: computed,
            disableNext: computed,
            handleBack: action,
        });
        this.editorStore = editorStore;
        this.mappingEditorState = mappingEditorState;
        this.unsupported = unsupported;
        this.queryToSuiteMap = supported;
    }
    get activeStep() {
        return this.steps.findIndex((e) => e === this.currentStep);
    }
    get disableBack() {
        if (this.currentStep === MIGRATE_PHASE.OVERVIEW) {
            return true;
        }
        return false;
    }
    get disableNext() {
        return !this.migrateableTests.length;
    }
    get nextText() {
        if (this.currentStep === MIGRATE_PHASE.CONFIRM) {
            return 'Confirm';
        }
        return 'Next';
    }
    handleBack() {
        if (this.currentStep === MIGRATE_PHASE.CONFIRM) {
            this.confirmationState?.reverse();
            this.confirmationState = undefined;
            this.currentStep = MIGRATE_PHASE.OVERVIEW;
        }
    }
    handleNext() {
        if (this.currentStep === MIGRATE_PHASE.OVERVIEW) {
            this.confirmationState = new MappingTestConfirmationState(this);
            this.currentStep = MIGRATE_PHASE.CONFIRM;
        }
        else {
            this.confirmationState?.migrate();
            this.mappingEditorState.closeMigrationTool();
            this.mappingEditorState.DEPRECATED_mappingTestStates =
                this.mappingEditorState.buildLegacyTestsStates();
        }
    }
    static build(editorStore, mappingEditorState) {
        const unsupportedTests = new Map();
        const supported = new Map();
        mappingEditorState.mapping.test.forEach((test) => {
            let testUnsupportedReason = undefined;
            const nullableInputData = test.inputData[0];
            if (test.inputData.length !== 1 || !nullableInputData) {
                testUnsupportedReason = UNSUPPORTED_REASON.MULTI_INPUT_DATA;
            }
            else if (nullableInputData instanceof FlatDataInputData) {
                testUnsupportedReason = UNSUPPORTED_REASON.FLAT_DATA;
            }
            else if (nullableInputData instanceof RelationalInputData) {
                testUnsupportedReason = UNSUPPORTED_REASON.FLAT_DATA;
            }
            if (testUnsupportedReason) {
                const unsupportedTest = unsupportedTests.get(testUnsupportedReason) ?? [];
                unsupportedTest.push(test);
                unsupportedTests.set(testUnsupportedReason, unsupportedTest);
                return;
            }
            const inputData = guaranteeNonNullable(nullableInputData);
            const testQuery = test.query;
            // we will use hash code to see if the same query exists
            const suites = supported.get(testQuery.hashCode) ?? [];
            suites.push(new SupportedMigrationTestState(test, inputData));
            supported.set(testQuery.hashCode, suites);
        });
        return new MappingTestMigrationState(editorStore, mappingEditorState, unsupportedTests, supported);
    }
    migrate() {
        const suites = [];
        this.suiteValuesToMigrate.forEach((testsWithQuery) => {
            if (!testsWithQuery.length) {
                return;
            }
            const test = guaranteeNonNullable(testsWithQuery[0]);
            const query = test.test.query;
            const suite = new MappingTestSuite();
            suite.func = query;
            testsWithQuery.forEach((legacyTestState) => {
                const legacyTest = legacyTestState.test;
                const legacyAssertion = guaranteeType(legacyTest.assert, DEPRECATED__ExpectedOutputMappingTestAssert);
                const mappingTest = new MappingTest();
                const assertion = new EqualToJson();
                mappingTest.id = legacyTest.name;
                assertion.id = legacyTest.name;
                mappingTest.assertions = [assertion];
                assertion.parentTest = mappingTest;
                assertion.expected = new ExternalFormatData();
                assertion.expected.contentType = ContentType.APPLICATION_JSON;
                assertion.expected.data = legacyAssertion.expectedOutput;
                const inputData = legacyTestState.inputData;
                const storeTestData = new StoreTestData();
                if (inputData instanceof DEPRECATED__ObjectInputData) {
                    storeTestData.store = PackageableElementExplicitReference.create(ModelStore.INSTANCE);
                    const modelStoreData = new ModelStoreData();
                    const modelEmbeddedData = new ModelEmbeddedData();
                    modelEmbeddedData.model = PackageableElementExplicitReference.create(inputData.sourceClass.value);
                    const externalFormatData = new ExternalFormatData();
                    if (inputData.inputType === ObjectInputType.XML) {
                        externalFormatData.contentType = ContentType.APPLICATION_XML;
                    }
                    else {
                        externalFormatData.contentType = ContentType.APPLICATION_JSON;
                    }
                    externalFormatData.data = inputData.data;
                    modelEmbeddedData.data = externalFormatData;
                    modelStoreData.modelData = [modelEmbeddedData];
                    storeTestData.data = modelStoreData;
                }
                else if (inputData instanceof RelationalInputData) {
                    storeTestData.store = PackageableElementExplicitReference.create(inputData.database.value);
                }
                mappingTest.storeTestData = [storeTestData];
                mappingTest.__parent = suite;
                suite.tests.push(mappingTest);
            });
            // calculate ID
            let id = 'suite';
            if (suite.tests.length === 1) {
                id = `${guaranteeNonNullable(suite.tests[0]).id}_${id}`;
            }
            else {
                id = `suite_${suites.length}`;
            }
            suite.id = id;
            suites.push(suite);
        });
        return suites;
    }
    get suiteValuesToMigrate() {
        return Array.from(this.queryToSuiteMap.values());
    }
    get unSupportedTestsToMigrate() {
        return uniq(Array.from(this.unsupported.values()).flat());
    }
    get mapping() {
        return this.mappingEditorState.mapping;
    }
    get migrateableTests() {
        return this.mapping.test.filter((t) => !this.unSupportedTestsToMigrate.includes(t));
    }
}
//# sourceMappingURL=MappingTestMigrationState.js.map