var __awaiter = this && this.__awaiter || function(thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))((function(resolve, reject) {
        function fulfilled(value) {
            try {
                step(generator.next(value));
            } catch (e) {
                reject(e);
            }
        }
        function rejected(value) {
            try {
                step(generator.throw(value));
            } catch (e) {
                reject(e);
            }
        }
        function step(result) {
            var value;
            result.done ? resolve(result.value) : (value = result.value, value instanceof P ? value : new P((function(resolve) {
                resolve(value);
            }))).then(fulfilled, rejected);
        }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    }));
};

import { TABLE_EVENT_TYPE } from "@visactor/vtable";

import ExcelJS from "exceljs";

import { importExcelMultipleSheets, importCsvFile } from "./excel-import/excel";

import { applyImportToVTableSheet } from "./excel-import/vtable-sheet";

export class ExcelImportPlugin {
    constructor(options) {
        this.id = "excel-import-plugin", this.name = "ExcelImportPlugin", this.runTime = [ TABLE_EVENT_TYPE.INITIALIZED ], 
        this.table = null, this.options = Object.assign({
            autoTable: !0,
            autoColumns: !0,
            delimiter: ",",
            exportData: !1,
            batchSize: 1e3,
            enableBatchProcessing: !0,
            asyncDelay: 5,
            importAllSheets: !1,
            clearExisting: !0
        }, options), (null == options ? void 0 : options.id) && (this.id = options.id);
    }
    run(...args) {
        if (args[1] === TABLE_EVENT_TYPE.INITIALIZED && (this.table = args[2], this.table.importFile = () => {
            this.import("file");
        }, this.table.__vtableSheet)) {
            const vtableSheet = this.table.__vtableSheet;
            vtableSheet._importFile || (vtableSheet._importFile = options => __awaiter(this, void 0, void 0, (function*() {
                const mergedOptions = Object.assign(Object.assign({}, this.options), options);
                return this._importFileFromDialogForVTableSheet(vtableSheet, mergedOptions);
            })));
        }
    }
    _importFile(options) {
        return __awaiter(this, void 0, void 0, (function*() {
            if (!this.table) throw new Error("表格实例不存在或已销毁，无法导入数据！");
            const mergedOptions = Object.assign(Object.assign({}, this.options), options);
            if (this._detectVTableSheet(this.table)) return this._importMultipleSheetsFromFileDialog(Object.assign(Object.assign({}, mergedOptions), {
                importAllSheets: !0
            }));
            throw new Error("ListTable 单 sheet 导入功能需要进一步重构");
        }));
    }
    _selectFile(accept = ".xlsx,.xls,.csv,.txt,.json,.html,.htm") {
        return new Promise(((resolve, reject) => {
            const input = document.createElement("input");
            input.type = "file", input.accept = accept, input.style.display = "none", document.body.appendChild(input), 
            input.addEventListener("change", (() => {
                var _a;
                try {
                    const file = null === (_a = input.files) || void 0 === _a ? void 0 : _a[0];
                    if (!file) return document.body.removeChild(input), void reject(new Error("未选择文件"));
                    input.value = "", document.body.removeChild(input), resolve(file);
                } catch (error) {
                    document.body.removeChild(input), reject(error);
                }
            })), input.click();
        }));
    }
    _importFileFromDialogForVTableSheet(vtableSheetInstance, options) {
        return __awaiter(this, void 0, void 0, (function*() {
            try {
                const file = yield this._selectFile(".xlsx,.xls,.csv,.txt"), fileExtension = this._getFileExtension(file.name).toLowerCase();
                let result;
                if ("csv" === fileExtension || "txt" === fileExtension) result = yield importCsvFile(file, options); else {
                    if ("xlsx" !== fileExtension && "xls" !== fileExtension) throw new Error("不支持的文件类型，仅支持 Excel (.xlsx, .xls) 和 CSV (.csv, .txt)");
                    result = yield importExcelMultipleSheets(file, options);
                }
                return !1 !== options.autoTable && result.sheets.length > 0 && applyImportToVTableSheet(vtableSheetInstance, result, options), 
                result;
            } catch (error) {
                throw error;
            }
        }));
    }
    _importMultipleSheetsFromFileDialog(options) {
        return __awaiter(this, void 0, void 0, (function*() {
            try {
                const file = yield this._selectFile(".xlsx,.xls,.csv,.txt"), fileExtension = this._getFileExtension(file.name).toLowerCase();
                let result;
                if ("csv" === fileExtension || "txt" === fileExtension) result = yield importCsvFile(file, options); else {
                    if ("xlsx" !== fileExtension && "xls" !== fileExtension) throw new Error("不支持的文件类型，仅支持 Excel (.xlsx, .xls) 和 CSV (.csv, .txt)");
                    result = yield importExcelMultipleSheets(file, options);
                }
                if (!1 !== options.autoTable && this.table && this._detectVTableSheet(this.table)) {
                    const vtableSheet = this.table.__vtableSheet;
                    vtableSheet && applyImportToVTableSheet(vtableSheet, result, options);
                }
                return result;
            } catch (error) {
                throw error;
            }
        }));
    }
    _detectVTableSheet(instance) {
        const inst = instance;
        return !!(inst && "function" == typeof inst.updateOption && inst.options && "object" == typeof inst.options && null !== inst.options && Array.isArray(inst.options.sheets));
    }
    _getFileExtension(filename) {
        const parts = filename.split(".");
        return parts.length > 1 ? parts[parts.length - 1] : "";
    }
    release() {
        this.table && (delete this.table.importFile, this.table = null);
    }
    import(type, source, options) {
        return __awaiter(this, void 0, void 0, (function*() {
            if (!this.table) throw new Error("表格实例不存在或已销毁，无法导入数据！");
            const mergedOptions = Object.assign(Object.assign({}, this.options), options);
            if (this._detectVTableSheet(this.table) && "file" === type) return this._importMultipleSheetsFromFileDialog(Object.assign(Object.assign({}, mergedOptions), {
                importAllSheets: !0
            }));
            if ("file" === type) return this._importFromFileDialog(mergedOptions);
            if ("string" == typeof source) return this._importFromString(type, source, mergedOptions);
            if (Array.isArray(source) || "object" == typeof source) {
                if ("json" !== type) throw new Error("只有JSON格式支持从对象导入");
                return this._importFromData("json", source, mergedOptions);
            }
            throw new Error("Invalid import source");
        }));
    }
    _importFromFileDialog(options) {
        return __awaiter(this, void 0, void 0, (function*() {
            if (!this.table) throw new Error("表格实例不存在或已销毁，无法导入数据！");
            const file = yield this._selectFile(), result = yield this._parseFile(file, options);
            return options.autoTable && this.table && (options.autoColumns && this.table.updateOption(Object.assign({}, this.table.options, {
                columns: result.columns
            })), this.table.setRecords(result.records)), result;
        }));
    }
    _importFromString(type, data, options) {
        return __awaiter(this, void 0, void 0, (function*() {
            let result;
            switch (type) {
              case "csv":
                result = yield this._parseCSVString(data, options);
                break;

              case "json":
                result = yield this._parseJSONString(data, options);
                break;

              case "html":
                result = yield this._parseHTMLString(data, options);
                break;

              default:
                throw new Error(`不支持的导入类型: ${type}`);
            }
            return options.autoTable && this.table && (options.autoColumns && this.table.updateOption({
                columns: result.columns,
                plugins: [ this ]
            }), this.table.setRecords(result.records)), result;
        }));
    }
    _importFromData(type, data, options) {
        return __awaiter(this, void 0, void 0, (function*() {
            let result;
            if ("json" !== type) throw new Error(`不支持的数据类型: ${type}`);
            return result = yield this._parseJSONData(data, options), options.autoTable && this.table && (options.autoColumns && this.table.updateOption({
                columns: result.columns,
                plugins: [ this ]
            }), this.table.setRecords(result.records)), result;
        }));
    }
    _parseFile(file, options) {
        return __awaiter(this, void 0, void 0, (function*() {
            const mergedOptions = Object.assign(Object.assign({}, this.options), options), fileExtension = this._getFileExtension(file.name).toLowerCase();
            switch (fileExtension) {
              case "xlsx":
              case "xls":
                return yield this._parseExcelFile(file, mergedOptions);

              case "csv":
                return yield this._parseCsvFile(file, mergedOptions);

              case "json":
                return yield this._parseJsonFile(file, mergedOptions);

              case "html":
              case "htm":
                return yield this._parseHtmlFile(file, mergedOptions);

              default:
                throw new Error(`不支持的文件类型: ${fileExtension}`);
            }
        }));
    }
    _parseExcelFile(file, options) {
        return __awaiter(this, void 0, void 0, (function*() {
            const mergedOptions = Object.assign(Object.assign({}, this.options), options);
            return yield ExcelImportPlugin.importExcelToVTableData(file, mergedOptions);
        }));
    }
    _parseCsvFile(file, options) {
        return __awaiter(this, void 0, void 0, (function*() {
            const mergedOptions = Object.assign(Object.assign({}, this.options), options), text = yield file.text();
            return this._parseCSVString(text, mergedOptions);
        }));
    }
    _parseJsonFile(file, options) {
        return __awaiter(this, void 0, void 0, (function*() {
            const mergedOptions = Object.assign(Object.assign({}, this.options), options), text = yield file.text();
            return this._parseJSONString(text, mergedOptions);
        }));
    }
    _parseHtmlFile(file, options) {
        return __awaiter(this, void 0, void 0, (function*() {
            const mergedOptions = Object.assign(Object.assign({}, this.options), options), text = yield file.text();
            return this._parseHTMLString(text, mergedOptions);
        }));
    }
    _buildColumnsFromHeaders(headerRows) {
        return 1 === headerRows.length ? headerRows[0].map(((title, index) => ({
            field: `col${index}`,
            title: title || `列${index + 1}`,
            cellType: "text",
            headerType: "text"
        }))) : ExcelImportPlugin.buildColumns(headerRows);
    }
    static importExcelToVTableData(file, options) {
        var _a;
        return __awaiter(this, void 0, void 0, (function*() {
            const workbook = new ExcelJS.Workbook;
            yield workbook.xlsx.load(yield file.arrayBuffer());
            const worksheet = workbook.worksheets[0];
            if (!worksheet) throw new Error("Excel 文件无有效工作表");
            const detectedHeaderRows = null !== (_a = null == options ? void 0 : options.headerRowCount) && void 0 !== _a ? _a : this.detectHeaderRowCount(worksheet), headerRows = [];
            for (let i = 1; i <= detectedHeaderRows; i++) {
                const row = worksheet.getRow(i);
                headerRows.push(Array.prototype.slice.call(row.values, 1));
            }
            const columns = ExcelImportPlugin.buildColumns(headerRows), records = [], dataRows = [];
            worksheet.eachRow(((row, rowNumber) => {
                if (rowNumber <= detectedHeaderRows) return;
                const cells = Array.prototype.slice.call(row.values, 1);
                dataRows.push(cells);
            }));
            const batchSize = (null == options ? void 0 : options.batchSize) || 1e3;
            if (!1 !== (null == options ? void 0 : options.enableBatchProcessing) && dataRows.length > batchSize) for (let i = 0; i < dataRows.length; i += batchSize) {
                const batchRecords = dataRows.slice(i, i + batchSize).map((cells => {
                    const record = {};
                    return cells.forEach(((cell, j) => {
                        record[`col${j}`] = cell;
                    })), record;
                }));
                records.push(...batchRecords), i + batchSize < dataRows.length && (yield new Promise((resolve => setTimeout(resolve, (null == options ? void 0 : options.asyncDelay) || 5))));
            } else dataRows.forEach((cells => {
                const record = {};
                cells.forEach(((cell, i) => {
                    record[`col${i}`] = cell;
                })), records.push(record);
            }));
            if (null == options ? void 0 : options.exportData) {
                const jsContent = ExcelImportPlugin._generateJavaScriptExport(columns, records), blob = new Blob([ jsContent ], {
                    type: "text/javascript"
                }), url = URL.createObjectURL(blob), a = document.createElement("a");
                a.href = url, a.download = "vtable-data.js", document.body.appendChild(a), a.click(), 
                setTimeout((() => {
                    document.body.removeChild(a), URL.revokeObjectURL(url);
                }), 100);
            }
            return {
                columns: columns,
                records: records
            };
        }));
    }
    static detectHeaderRowCount(worksheet) {
        let rowIndex = 1, headerRowCount = 1;
        for (;;) {
            const row = worksheet.getRow(rowIndex), cells = Array.prototype.slice.call(row.values, 1);
            let hasConsecutive = !1;
            for (let i = 1; i < cells.length; i++) if (void 0 !== cells[i] && "" !== cells[i] && cells[i] === cells[i - 1]) {
                hasConsecutive = !0;
                break;
            }
            if (!hasConsecutive) break;
            headerRowCount++, rowIndex++;
        }
        return headerRowCount;
    }
    static buildColumns(headerRows, colStart = 0, colEnd, level = 0) {
        const row = headerRows[level], columns = [];
        colEnd = null != colEnd ? colEnd : row.length;
        let i = colStart;
        for (;i < colEnd; ) {
            const title = String(row[i] || `列${i + 1}`);
            let span = 1;
            for (;i + span < colEnd && row[i + span] === title; ) span++;
            if (level < headerRows.length - 1) {
                const subColumns = ExcelImportPlugin.buildColumns(headerRows, i, i + span, level + 1);
                1 === subColumns.length ? columns.push(subColumns[0]) : columns.push({
                    title: title,
                    columns: subColumns,
                    hideColumnsSubHeader: !1
                });
            } else columns.push({
                field: `col${i}`,
                title: title,
                cellType: "text",
                headerType: "text"
            });
            i += span;
        }
        return columns;
    }
    _parseCSVString(text, options) {
        return __awaiter(this, void 0, void 0, (function*() {
            const delimiter = options.delimiter || ",", lines = text.split(/\r?\n/).filter((line => line.trim().length > 0));
            if (0 === lines.length) throw new Error("CSV文件为空");
            const parseCSVLine = line => {
                const result = [];
                let current = "", inQuotes = !1;
                for (let i = 0; i < line.length; i++) {
                    const char = line[i], nextChar = line[i + 1];
                    '"' === char ? inQuotes && '"' === nextChar ? (current += '"', i++) : inQuotes = !inQuotes : char !== delimiter || inQuotes ? current += char : (result.push(current.trim()), 
                    current = "");
                }
                return result.push(current.trim()), result;
            }, headerRows = lines.slice(0, 1).map((line => parseCSVLine(line).map((cell => String(cell || ""))))), dataRows = lines.slice(1), columns = this._buildColumnsFromHeaders(headerRows), records = [];
            return yield this._processBatchRecords(dataRows, options, (batch => __awaiter(this, void 0, void 0, (function*() {
                const batchRecords = batch.map((line => {
                    const parsedRow = parseCSVLine(line), record = {};
                    return parsedRow.forEach(((cell, i) => {
                        record[`col${i}`] = null != cell ? cell : "";
                    })), record;
                }));
                records.push(...batchRecords);
            })))), options.exportData && this._exportToJS(columns, records), {
                columns: columns,
                records: records
            };
        }));
    }
    _parseJSONString(text, options) {
        return __awaiter(this, void 0, void 0, (function*() {
            let jsonData;
            try {
                jsonData = JSON.parse(text);
            } catch (error) {
                throw new Error("JSON文件格式错误");
            }
            const result = yield this._parseJSONData(jsonData, options);
            return options.exportData && this._exportToJS(result.columns, result.records), result;
        }));
    }
    _parseHTMLString(text, options) {
        return __awaiter(this, void 0, void 0, (function*() {
            const table = (new DOMParser).parseFromString(text, "text/html").querySelector("table");
            if (!table) throw new Error("HTML文件中未找到表格");
            const rows = Array.from(table.querySelectorAll("tr"));
            if (0 === rows.length) throw new Error("表格中没有数据行");
            const headerRows = rows.slice(0, 1).map((row => Array.from(row.querySelectorAll("th, td")).map((cell => {
                var _a;
                return (null === (_a = cell.textContent) || void 0 === _a ? void 0 : _a.trim()) || "";
            })))), dataRows = rows.slice(1), columns = this._buildColumnsFromHeaders(headerRows), records = [];
            return yield this._processBatchRecords(dataRows, options, (batch => __awaiter(this, void 0, void 0, (function*() {
                const batchRecords = batch.map((row => {
                    const cells = Array.from(row.querySelectorAll("td")).map((cell => {
                        var _a;
                        return (null === (_a = cell.textContent) || void 0 === _a ? void 0 : _a.trim()) || "";
                    })), record = {};
                    return cells.forEach(((cell, i) => {
                        record[`col${i}`] = cell || "";
                    })), record;
                }));
                records.push(...batchRecords);
            })))), options.exportData && this._exportToJS(columns, records), {
                columns: columns,
                records: records
            };
        }));
    }
    _parseJSONData(jsonData, options) {
        return __awaiter(this, void 0, void 0, (function*() {
            let result;
            if ("object" == typeof jsonData && !Array.isArray(jsonData) && jsonData && "columns" in jsonData && "records" in jsonData) {
                const data = jsonData, records = [];
                yield this._processBatchRecords(data.records, options, (batch => __awaiter(this, void 0, void 0, (function*() {
                    const batchRecords = batch.map((record => Object.assign({}, record)));
                    records.push(...batchRecords);
                })))), result = {
                    columns: data.columns,
                    records: records
                };
            } else {
                if (!(Array.isArray(jsonData) && jsonData.length > 0)) throw new Error("不支持的JSON格式");
                {
                    const firstRecord = jsonData[0], columns = Object.keys(firstRecord).map((key => ({
                        field: key,
                        title: key,
                        cellType: "text",
                        headerType: "text"
                    }))), records = [];
                    yield this._processBatchRecords(jsonData, options, (batch => __awaiter(this, void 0, void 0, (function*() {
                        const batchRecords = batch.map((record => Object.assign({}, record)));
                        records.push(...batchRecords);
                    })))), result = {
                        columns: columns,
                        records: records
                    };
                }
            }
            return options.exportData && this._exportToJS(result.columns, result.records), result;
        }));
    }
    static _generateJavaScriptExport(columns, records) {
        const formatValue = (value, indent = "") => {
            if (null == value) return "null";
            if ("string" == typeof value) return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
            if ("number" == typeof value || "boolean" == typeof value) return String(value);
            if (Array.isArray(value)) {
                if (0 === value.length) return "[]";
                return `[${value.map((item => formatValue(item, indent + "  "))).join(", ")}]`;
            }
            if ("object" == typeof value) {
                return `{\n${Object.entries(value).map((([key, val]) => `${indent}  ${key}: ${formatValue(val, indent + "  ")}`)).join(",\n")}\n${indent}}`;
            }
            return String(value);
        }, formatColumn = (column, baseIndent = "    ") => {
            const props = Object.entries(column).map((([key, value]) => {
                if ("columns" === key && Array.isArray(value)) {
                    const nestedColumns = value.map((col => formatColumn(col, baseIndent + "  "))).join(",\n");
                    return `${baseIndent}  ${key}: [\n${nestedColumns}\n${baseIndent}  ]`;
                }
                return `${baseIndent}  ${key}: ${formatValue(value)}`;
            })).join(",\n");
            return `${baseIndent}{\n${props}\n${baseIndent}}`;
        }, columnsStr = columns.map((col => formatColumn(col))).join(",\n"), recordsStr = records.map((record => `    {\n${Object.entries(record).map((([key, value]) => `      ${key}: ${formatValue(value)}`)).join(",\n")}\n    }`)).join(",\n");
        return `// VTable 数据导出 - JavaScript 对象字面量格式\n// 生成时间: ${(new Date).toLocaleString()}\n\nconst vtableData = {\n  columns: [\n${columnsStr}\n  ],\n  records: [\n${recordsStr}\n  ]\n};\n`;
    }
    _exportToJS(columns, records) {
        const jsContent = ExcelImportPlugin._generateJavaScriptExport(columns, records), blob = new Blob([ jsContent ], {
            type: "text/javascript"
        }), url = URL.createObjectURL(blob), a = document.createElement("a");
        a.href = url, a.download = "vtable-data.js", document.body.appendChild(a), a.click(), 
        setTimeout((() => {
            document.body.removeChild(a), URL.revokeObjectURL(url);
        }), 100);
    }
    _processBatchRecords(records, options, processor) {
        return __awaiter(this, void 0, void 0, (function*() {
            if (!options.enableBatchProcessing || records.length <= (options.batchSize || 1e3)) return void (yield processor(records, 0));
            const batchSize = options.batchSize || 1e3, totalBatches = Math.ceil(records.length / batchSize);
            for (let i = 0; i < totalBatches; i++) {
                const start = i * batchSize, end = Math.min(start + batchSize, records.length), batch = records.slice(start, end);
                yield processor(batch, i), i < totalBatches - 1 && (yield new Promise((resolve => setTimeout(resolve, options.asyncDelay || 5))));
            }
        }));
    }
}
//# sourceMappingURL=excel-import.js.map