"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DataImportWizard = void 0;
const tslib_1 = require("tslib");
const React = tslib_1.__importStar(require("react"));
const HelpBlock_1 = tslib_1.__importDefault(require("../../../components/HelpBlock"));
const Tag_1 = require("../../../components/Tag");
const AdaptableContext_1 = require("../../AdaptableContext");
const OnePageAdaptableWizard_1 = require("../../Wizard/OnePageAdaptableWizard");
const systemFileHandlers_1 = require("../systemFileHandlers");
const ColumnsSection_1 = require("./sections/ColumnsSection");
const UploadSection_1 = require("./sections/UploadSection");
const ValidationSection_1 = require("./sections/ValidationSection");
const DataImportWizard = (props) => {
    const adaptable = (0, AdaptableContext_1.useAdaptable)();
    const adaptableApi = adaptable.api;
    const dataImportOptions = adaptableApi.optionsApi.getDataImportOptions();
    const module = adaptableApi.internalApi.getModuleService().getModuleById('DataImport');
    const [rowData, setRowData] = React.useState(null);
    const [file, setFile] = React.useState(null);
    const [text, setText] = React.useState('');
    // This needs to be called only when:
    // - columnsMap changes
    // - new data is loaded
    const [importType, setImportType] = React.useState('file');
    const primaryKey = adaptableApi.optionsApi.getPrimaryKey();
    const hasDynamicallyAddedPrimaryKey = typeof dataImportOptions._getPrimaryKeyValue === 'function';
    const getPrimaryKeyValue = (rowData) => {
        if (hasDynamicallyAddedPrimaryKey) {
            return dataImportOptions._getPrimaryKeyValue({
                ...adaptableApi.internalApi.buildBaseContext(),
                rowData,
            });
        }
        return rowData[primaryKey];
    };
    const preprocessRowData = (rowData) => {
        if (typeof dataImportOptions?._preprocessRowData === 'function') {
            return dataImportOptions._preprocessRowData({
                ...adaptableApi.internalApi.buildBaseContext(),
                rowData,
            });
        }
        // by default just trim the keys
        let processedRowData = {};
        if (rowData) {
            processedRowData = Object.keys(rowData).reduce((acc, key) => {
                acc[key.trim()] = rowData[key];
                return acc;
            }, processedRowData);
        }
        return processedRowData;
    };
    // ---- COLUMNS ----
    const [columnsMap, setColumnsMap] = React.useState(null);
    const mappedRowDataToColumns = React.useMemo(() => {
        return (rowData ?? []).map((row) => {
            // pick only included columns, and need to map to abColumn.field
            return columnsMap?.reduce((acc, colMap) => {
                if (colMap.include && colMap.abColumn && colMap.abColumn.field) {
                    acc[colMap.abColumn.field] = row[colMap.field];
                }
                return acc;
            }, {});
        });
    }, [rowData, columnsMap]);
    const columnsErrors = React.useMemo(() => {
        return columnsMap
            ? columnsMap.reduce((acc, colMap) => {
                if (colMap.include && !colMap.abColumn) {
                    acc[colMap.field] = `Field ${colMap.field} does not have a coresponding column.`;
                }
                return acc;
            }, {})
            : null;
    }, [columnsMap]);
    const handleNewRowData = React.useCallback((data) => {
        if (Array.isArray(data)) {
            const processedData = data.map((rowData) => preprocessRowData(rowData));
            setRowData(processedData);
            const fields = new Set();
            processedData.forEach((row) => {
                Object.keys(row).forEach((field) => {
                    fields.add(field);
                });
            });
            const allAbColumns = adaptableApi.columnApi.getColumns();
            const columnsMap = Array.from(fields).map((field) => {
                const abColumn = allAbColumns.find((c) => {
                    // exact field match
                    if (c.field === field) {
                        return true;
                    }
                    // based on friendly name
                    const friendlyName = c.friendlyName;
                    if (typeof friendlyName !== 'string') {
                        return false;
                    }
                    // exact match
                    if (friendlyName === field) {
                        return true;
                    }
                    // match without case
                    if (friendlyName.toLowerCase() === field.toLowerCase()) {
                        return true;
                    }
                    // match without spaces and case, and special characters: ' ', '-', '_'
                    if (friendlyName.replace(/\s|-|_/g, '').toLowerCase() === field.toLowerCase()) {
                        return true;
                    }
                    return false;
                });
                return { field, abColumn, include: true };
            });
            setColumnsMap(columnsMap);
        }
        else {
            setRowData(null);
            setColumnsMap(null);
        }
    }, []);
    const handleRowDataChange = React.useCallback((data) => {
        if (Array.isArray(data)) {
            setRowData(data);
        }
        else {
            setRowData(null);
        }
    }, []);
    const handleChangeImportType = React.useCallback((importType) => {
        setImportType(importType);
        // clear data
        handleNewRowData(null);
        setFile(null);
        setText('');
    }, []);
    // ---- FILE HANDLERS ----
    const fileHandlers = React.useMemo(() => {
        const userDefinedHandlers = adaptableApi.optionsApi.getDataImportOptions()?.fileHandlers ?? [];
        return [...userDefinedHandlers, ...systemFileHandlers_1.systemFileHandlers];
    }, []);
    const supportedFileFormats = React.useMemo(() => {
        return [...new Set(fileHandlers.map((h) => h.fileExtension)).values()].join(', ');
    }, [fileHandlers]);
    const readFile = React.useCallback(async (file) => {
        const userDefinedFileHandlers = adaptableApi.optionsApi.getDataImportOptions()?.fileHandlers ?? [];
        const fileExtension = `.${file.name.split('.').pop()}`;
        const handler = [...userDefinedFileHandlers, ...systemFileHandlers_1.systemFileHandlers].find((fh) => fh.fileExtension === fileExtension);
        if (!handler) {
            adaptableApi.logError(`No file handler found for file extension ${fileExtension}. Please provide a file handler in Data Import Options.`);
            return;
        }
        try {
            const data = await handler.handleFile(file);
            setFile(file);
            handleNewRowData(data);
            return data;
        }
        catch {
            adaptableApi.logError(`Error reading file ${file.name}. Please check the file is valid.`);
        }
    }, [adaptable]);
    const fileMessage = file ? (React.createElement(HelpBlock_1.default, { className: "twa:mb-2" },
        "File ",
        React.createElement(Tag_1.Tag, null, file.name),
        " is ready to be imported.")) : (React.createElement(HelpBlock_1.default, { className: "twa:mb-2" },
        "Supported file types: ",
        supportedFileFormats));
    // ---- TEXT IMPORT ----
    const [textError, setTextError] = React.useState(null);
    const handleTextChange = React.useCallback(async (text) => {
        //
        let textHandler = adaptableApi.optionsApi.getDataImportOptions()?.textHandler;
        if (!textHandler) {
            // regex to test for json
            const jsonRegex = /^\s*[\[{]/;
            if (text && jsonRegex.test(text)) {
                textHandler = JSON.parse;
            }
            else {
                textHandler = systemFileHandlers_1.parseCSV;
            }
        }
        setText(text);
        let textRawData = null;
        if (!text || text === '') {
            setTextError(null);
        }
        else {
            try {
                textRawData = await textHandler(text);
                if (Array.isArray(textRawData)) {
                    handleNewRowData(textRawData);
                    setTextError('');
                }
                else {
                    throw 'Error parsing text';
                }
            }
            catch {
                handleNewRowData(null);
                setTextError('Error parsing text');
            }
        }
    }, []);
    const textMessage = textError ?? (!rowData ? 'Paste your data here' : '');
    // ---- VALIDATION ----
    const errors = React.useMemo(() => {
        if (!dataImportOptions?.validate) {
            return null;
        }
        if (!rowData) {
            return null;
        }
        return rowData?.reduce?.((acc, rowData) => {
            const error = dataImportOptions.validate({
                rowData,
                ...adaptableApi.internalApi.buildBaseContext(),
            });
            const primaryKeyValue = getPrimaryKeyValue(rowData);
            if (error && primaryKeyValue && error.length) {
                acc[primaryKeyValue] = error;
            }
            return acc;
        }, {});
    }, [rowData]) ?? {};
    const [skipInvalidRows, setSkipInvalidRows] = React.useState(false);
    const handleFinish = () => {
        const validData = mappedRowDataToColumns.filter((row) => {
            const rowErrors = errors[getPrimaryKeyValue(row)];
            return !rowErrors || rowErrors.length === 0;
        });
        adaptableApi.dataImportApi.internalApi.importData(validData);
    };
    return (React.createElement(OnePageAdaptableWizard_1.OnePageAdaptableWizard, { data: null, moduleInfo: module.moduleInfo, finishText: "Import", onFinish: () => {
            handleFinish();
            props.onClose();
        }, onHide: () => {
            props.onClose();
        }, sections: [
            {
                title: 'Upload',
                isValid: () => {
                    return rowData ? true : 'No valid data uploaded';
                },
                details: 'Upload the data',
                render: () => {
                    return (React.createElement(UploadSection_1.UploadSection, { importType: importType, onImportTypeChange: handleChangeImportType, 
                        // FILE
                        readFile: readFile, supportedFileFormats: supportedFileFormats, fileMessage: fileMessage, 
                        // TEXT
                        text: text, onTextChange: handleTextChange, textMessage: textMessage }));
                },
            },
            {
                title: 'Columns',
                isValid: () => {
                    if (columnsErrors && Object.keys(columnsErrors).length > 0) {
                        return 'Some fields do not have a corresponding column.';
                    }
                    if (!hasDynamicallyAddedPrimaryKey &&
                        !columnsMap?.some((map) => map?.abColumn?.field === primaryKey)) {
                        return 'You need to select a column for the primary key.';
                    }
                    return true;
                },
                render: () => {
                    return (React.createElement(ColumnsSection_1.ColumnsSection, { columnsMap: columnsMap, onColumnsChange: (columnsMap) => {
                            setColumnsMap(columnsMap);
                        } }));
                },
            },
            {
                title: 'Validation',
                details: 'Check the Data is Valid',
                isValid: () => {
                    if (errors && Object.keys(errors).length > 0) {
                        if (!skipInvalidRows)
                            return 'There are errors in the data.';
                    }
                    return true;
                },
                render: () => {
                    return (React.createElement(ValidationSection_1.ValidationSection, { errors: errors, data: rowData, columnsMap: columnsMap, onDataChange: handleRowDataChange, skipInvalidRows: skipInvalidRows, onSkipInvalidRowsChange: setSkipInvalidRows }));
                },
            },
        ] }));
};
exports.DataImportWizard = DataImportWizard;
