/**
 * 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 { DataElement, RelationalCSVData, DataElementReference, ExternalFormatData, ModelStoreData, ModelEmbeddedData, RelationElementsData, RelationRowTestData, observe_RelationRowTestData, observe_RelationElement, } from '@finos/legend-graph';
import { ContentType, csvDecodeValue, csvEncodeValue, csvStringify, parseCSVContent, guaranteeNonEmptyString, tryToFormatLosslessJSONString, UnsupportedOperationError, uuid, } from '@finos/legend-shared';
import { action, makeObservable, observable } from 'mobx';
import { dataElementReference_setDataElement, externalFormatData_setContentType, externalFormatData_setData, relationalData_addTable, relationalData_deleteData, relationalData_setTableValues, } from '../../../../graph-modifier/DSL_Data_GraphModifierHelper.js';
import { EmbeddedDataType } from '../../ExternalFormatState.js';
import { TEMPORARY__createRelationalDataFromCSV } from '../../../utils/TestableUtils.js';
export const createEmbeddedData = (type, editorStore) => {
    if (type === EmbeddedDataType.EXTERNAL_FORMAT_DATA) {
        const externalFormatData = new ExternalFormatData();
        externalFormatData_setData(externalFormatData, '');
        externalFormatData_setContentType(externalFormatData, guaranteeNonEmptyString(editorStore.graphState.graphGenerationState.externalFormatState
            .formatContentTypes[0]));
        return externalFormatData;
    }
    else if (type === EmbeddedDataType.RELATIONAL_CSV) {
        const relational = new RelationalCSVData();
        return relational;
    }
    else if (type === EmbeddedDataType.RELATION_ELEMENTS_DATA) {
        const testData = new RelationElementsData();
        return testData;
    }
    else if (type === EmbeddedDataType.MODEL_STORE_DATA) {
        const modelStoreData = new ModelStoreData();
        return modelStoreData;
    }
    else {
        const extraEmbeddedDataCreator = editorStore.pluginManager
            .getApplicationPlugins()
            .flatMap((plugin) => plugin.getExtraEmbeddedDataCreators?.() ?? []);
        for (const creator of extraEmbeddedDataCreator) {
            const embeddedData = creator(type);
            if (embeddedData) {
                return embeddedData;
            }
        }
        throw new UnsupportedOperationError(`Can't create embedded data: no compatible creators available from plugins`, type);
    }
};
export class EmbeddedDataState {
    editorStore;
    embeddedData;
    constructor(editorStore, embeddedData) {
        this.editorStore = editorStore;
        this.embeddedData = embeddedData;
    }
}
export class ExternalFormatDataState extends EmbeddedDataState {
    embeddedData;
    canEditContentType = true;
    constructor(editorStore, embeddedData) {
        super(editorStore, embeddedData);
        makeObservable(this, {
            format: action,
            canEditContentType: observable,
        });
        this.embeddedData = embeddedData;
    }
    label() {
        return 'External Format Data';
    }
    setCanEditoContentType(val) {
        this.canEditContentType = val;
    }
    get supportsFormatting() {
        return this.embeddedData.contentType === ContentType.APPLICATION_JSON;
    }
    format() {
        externalFormatData_setData(this.embeddedData, tryToFormatLosslessJSONString(this.embeddedData.data));
    }
}
export class ModelDataState {
    uuid = uuid();
    modelStoreDataState;
    modelData;
    constructor(modelData, modelStoreDataState) {
        this.modelStoreDataState = modelStoreDataState;
        this.modelData = modelData;
    }
}
export class ModelEmbeddedDataState extends ModelDataState {
    modelData;
    embeddedDataState;
    constructor(modelData, modelStoreDataState) {
        super(modelData, modelStoreDataState);
        this.modelData = modelData;
        this.embeddedDataState = buildEmbeddedDataEditorState(this.modelData.data, this.modelStoreDataState.editorStore);
    }
}
export class UnsupportedModelDataState extends ModelDataState {
}
export class ModelStoreDataState extends EmbeddedDataState {
    embeddedData;
    modelDataStates = [];
    hideClass = false;
    constructor(editorStore, embeddedData, hideClass) {
        super(editorStore, embeddedData);
        makeObservable(this, {
            hideClass: observable,
            modelDataStates: observable,
            buildStates: action,
        });
        this.embeddedData = embeddedData;
        this.modelDataStates = this.buildStates();
        this.hideClass = Boolean(hideClass);
    }
    label() {
        return 'Model Store Data';
    }
    buildStates() {
        return (this.embeddedData.modelData?.map((modelData) => {
            if (modelData instanceof ModelEmbeddedData) {
                return new ModelEmbeddedDataState(modelData, this);
            }
            return new UnsupportedModelDataState(modelData, this);
        }) ?? []);
    }
}
export class RelationElementState {
    relationElement;
    supportsColumnEditing;
    constructor(relationElement, options) {
        makeObservable(this, {
            relationElement: observable,
            addColumn: action,
            removeColumn: action,
            updateColumn: action,
            addRow: action,
            removeRow: action,
            updateRow: action,
            clearAllData: action,
            importCSV: action,
        });
        this.supportsColumnEditing = options?.supportsColumnEditing ?? true;
        this.relationElement = relationElement;
        this.relationElement = observe_RelationElement(relationElement);
    }
    addColumn(name) {
        this.relationElement.columns.push(name);
        this.relationElement.rows.forEach((row) => {
            row.values.push('');
        });
    }
    removeColumn(index) {
        const columnToRemove = this.relationElement.columns[index];
        if (columnToRemove) {
            this.relationElement.columns.splice(index, 1);
            this.relationElement.rows.forEach((row) => {
                row.values.splice(index, 1);
            });
        }
    }
    updateColumn(index, name) {
        const oldName = this.relationElement.columns[index];
        if (oldName && oldName !== name) {
            this.relationElement.columns[index] = name;
        }
    }
    addRow() {
        const row = new RelationRowTestData();
        row.values = [];
        const newRow = observe_RelationRowTestData(row);
        this.relationElement.columns.forEach((col) => {
            newRow.values.push('');
        });
        this.relationElement.rows.push(newRow);
    }
    removeRow(index) {
        this.relationElement.rows.splice(index, 1);
    }
    updateRow(rowIndex, columnIndex, value) {
        if (this.relationElement.rows[rowIndex]) {
            this.relationElement.rows[rowIndex].values[columnIndex] =
                csvEncodeValue(value);
        }
    }
    getDisplayValue(rowIndex, columnIndex) {
        const value = this.relationElement.rows[rowIndex]?.values[columnIndex];
        return value !== undefined ? csvDecodeValue(value) : '';
    }
    clearAllData() {
        this.relationElement.rows.splice(0);
    }
    exportJSON() {
        return JSON.stringify({
            columns: this.relationElement.columns,
            data: this.relationElement.rows.map((row) => ({
                values: row.values.map((v) => csvDecodeValue(v)),
            })),
        }, null, 2);
    }
    exportSQL() {
        if (this.relationElement.columns.length === 0 ||
            this.relationElement.rows.length === 0) {
            return '';
        }
        const tableName = 'test_data';
        const defaultDataType = 'VARCHAR(1000)';
        const columnDefs = this.relationElement.columns
            .map((col) => `${col} ${defaultDataType}`)
            .join(', ');
        const createTable = `CREATE TABLE ${tableName} (${columnDefs});`;
        const insertStatements = this.relationElement.rows.map((row) => {
            const values = this.relationElement.columns
                .map((col, colIndex) => {
                const value = csvDecodeValue(row.values[colIndex] ?? '');
                if (value !== '') {
                    return `'${value.replace(/'/g, "''")}'`;
                }
                return 'NULL';
            })
                .join(', ');
            return `INSERT INTO ${tableName} VALUES (${values});`;
        });
        return [createTable, '', ...insertStatements].join('\n');
    }
    exportCSV() {
        // decode so that csvStringify does not double encode
        const data = this.relationElement.rows.map((row) => row.values.map((v) => csvDecodeValue(v)));
        return csvStringify([this.relationElement.columns, ...data]);
    }
    importCSV(csvContent) {
        const parsed = parseCSVContent(csvContent);
        if (parsed.length === 0) {
            return;
        }
        const headers = parsed[0];
        if (!headers) {
            return;
        }
        this.relationElement.columns = headers;
        this.relationElement.rows = parsed.slice(1).map((values) => {
            const row = new RelationRowTestData();
            row.values = headers.map((_, index) => csvEncodeValue(values[index] ?? ''));
            return observe_RelationRowTestData(row);
        });
    }
}
export class RelationElementsDataState extends EmbeddedDataState {
    embeddedData;
    showImportCSVModal = false;
    showNewRelationElementModal = false;
    activeRelationElement;
    relationElementStates;
    accessorOptions;
    accessorTypeLabel;
    refreshAccessorOptions;
    constructor(editorStore, embeddedData) {
        super(editorStore, embeddedData);
        makeObservable(this, {
            embeddedData: observable,
            showImportCSVModal: observable,
            showNewRelationElementModal: observable,
            activeRelationElement: observable,
            relationElementStates: observable,
            accessorOptions: observable,
            accessorTypeLabel: observable,
            setActiveRelationElement: action,
            setShowImportCSVModal: action,
            setShowNewRelationElementModal: action,
            addRelationElement: action,
            deleteRelationElement: action,
            setAccessorOptions: action,
        });
        this.embeddedData = embeddedData;
        this.relationElementStates = embeddedData.relationElements.map((relationElement) => new RelationElementState(relationElement));
        this.activeRelationElement = this.relationElementStates[0];
    }
    label() {
        return 'Relation Elements Test Data';
    }
    setActiveRelationElement(val) {
        this.activeRelationElement = val;
    }
    addRelationElement(relationElement) {
        const newElementState = new RelationElementState(relationElement);
        this.relationElementStates.push(newElementState);
        this.embeddedData.relationElements.push(relationElement);
        this.setActiveRelationElement(newElementState);
    }
    deleteRelationElement(relationElementState) {
        const idx = this.relationElementStates.indexOf(relationElementState);
        if (idx === -1) {
            return;
        }
        this.relationElementStates.splice(idx, 1);
        this.embeddedData.relationElements.splice(idx, 1);
        if (this.activeRelationElement === relationElementState) {
            this.setActiveRelationElement(this.relationElementStates[0]);
        }
    }
    setShowImportCSVModal(show) {
        this.showImportCSVModal = show;
    }
    setShowNewRelationElementModal(show) {
        this.showNewRelationElementModal = show;
    }
    setAccessorOptions(options, typeLabel) {
        this.accessorOptions = options;
        this.accessorTypeLabel = typeLabel;
    }
    setRefreshAccessorOptions(fn) {
        this.refreshAccessorOptions = fn;
    }
    get availableAccessorOptions() {
        if (!this.accessorOptions) {
            return [];
        }
        const existingPaths = new Set(this.relationElementStates.map((s) => s.relationElement.paths.join('.')));
        return this.accessorOptions.filter((opt) => !existingPaths.has(opt.value));
    }
}
export class RelationalCSVDataTableState {
    editorStore;
    table;
    constructor(table, editorStore) {
        this.table = table;
        this.editorStore = editorStore;
        makeObservable(this, {
            table: observable,
            updateTableValues: action,
        });
    }
    updateTableValues(val) {
        relationalData_setTableValues(this.table, val);
    }
}
export class RelationalCSVDataState extends EmbeddedDataState {
    embeddedData;
    selectedTable;
    showImportCSVModal = false;
    database;
    //
    showTableIdentifierModal = false;
    tableToEdit;
    constructor(editorStore, embeddedData) {
        super(editorStore, embeddedData);
        makeObservable(this, {
            selectedTable: observable,
            showTableIdentifierModal: observable,
            deleteTable: observable,
            showImportCSVModal: observable,
            database: observable,
            resetSelectedTable: action,
            changeSelectedTable: action,
            setDatabase: action,
            closeModal: action,
            openIdentifierModal: action,
            setShowImportCsvModal: action,
            closeCSVModal: action,
            importCSV: action,
        });
        this.embeddedData = embeddedData;
        this.resetSelectedTable();
    }
    setShowImportCsvModal(val) {
        this.showImportCSVModal = val;
    }
    setDatabase(val) {
        this.database = val;
    }
    openIdentifierModal(renameTable) {
        this.showTableIdentifierModal = true;
        this.tableToEdit = renameTable;
    }
    closeCSVModal() {
        this.showImportCSVModal = false;
    }
    closeModal() {
        this.showTableIdentifierModal = false;
        this.tableToEdit = undefined;
    }
    importCSV(val) {
        const generated = TEMPORARY__createRelationalDataFromCSV(val);
        generated.tables.forEach((t) => relationalData_addTable(this.embeddedData, t));
        this.resetSelectedTable();
    }
    resetSelectedTable() {
        const table = this.embeddedData.tables[0];
        if (table) {
            this.selectedTable = new RelationalCSVDataTableState(table, this.editorStore);
        }
        else {
            this.selectedTable = undefined;
        }
    }
    deleteTable(val) {
        relationalData_deleteData(this.embeddedData, val);
        if (this.selectedTable?.table === val) {
            this.resetSelectedTable();
        }
    }
    changeSelectedTable(val) {
        this.selectedTable = new RelationalCSVDataTableState(val, this.editorStore);
    }
    label() {
        return 'Relational Data';
    }
}
export class UnsupportedDataState extends EmbeddedDataState {
    label() {
        return 'Unsupported embedded data';
    }
}
export class DataElementReferenceState extends EmbeddedDataState {
    embeddedData;
    embeddedDataValueState;
    options;
    constructor(editorStore, embeddedData, options) {
        super(editorStore, embeddedData);
        this.embeddedData = embeddedData;
        this.options = options;
        this.embeddedDataValueState = this.buildValueState();
    }
    label() {
        return 'Data Element Reference';
    }
    setDataElement(dataElement) {
        dataElementReference_setDataElement(this.embeddedData, dataElement, this.editorStore.changeDetectionState.observerContext);
        this.embeddedDataValueState = this.buildValueState();
    }
    buildValueState(options) {
        const packagableEl = this.embeddedData.dataElement.value;
        if (packagableEl instanceof DataElement) {
            return buildEmbeddedDataEditorState(packagableEl.data, this.editorStore, this.options);
        }
        return new UnsupportedDataState(this.editorStore, this.embeddedData);
    }
}
export function buildEmbeddedDataEditorState(_embeddedData, editorStore, options) {
    const embeddedData = _embeddedData;
    if (embeddedData instanceof ExternalFormatData) {
        return new ExternalFormatDataState(editorStore, embeddedData);
    }
    else if (embeddedData instanceof ModelStoreData) {
        return new ModelStoreDataState(editorStore, embeddedData, options?.hideSource);
    }
    else if (embeddedData instanceof RelationalCSVData) {
        return new RelationalCSVDataState(editorStore, embeddedData);
    }
    else if (embeddedData instanceof RelationElementsData) {
        return new RelationElementsDataState(editorStore, embeddedData);
    }
    else if (embeddedData instanceof DataElementReference) {
        return new DataElementReferenceState(editorStore, embeddedData, options);
    }
    else {
        const extraEmbeddedDataEditorStateBuilders = editorStore.pluginManager
            .getApplicationPlugins()
            .flatMap((plugin) => plugin.getExtraEmbeddedDataEditorStateBuilders?.() ?? []);
        for (const stateBuilder of extraEmbeddedDataEditorStateBuilders) {
            const state = stateBuilder(editorStore, embeddedData);
            if (state) {
                return state;
            }
        }
        return new UnsupportedDataState(editorStore, embeddedData);
    }
}
//# sourceMappingURL=EmbeddedDataState.js.map