"use strict";

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());
    }));
}, __importDefault = this && this.__importDefault || function(mod) {
    return mod && mod.__esModule ? mod : {
        default: mod
    };
};

Object.defineProperty(exports, "__esModule", {
    value: !0
}), exports.ExcelImportPlugin = void 0;

const exceljs_1 = __importDefault(require("exceljs")), vtable_1 = require("@visactor/vtable");

class ExcelImportPlugin {
    constructor(options) {
        this.id = "excel-import-plugin", this.name = "ExcelImportPlugin", this.runTime = [ vtable_1.TABLE_EVENT_TYPE.INITIALIZED ], 
        this._tableInstance = null, this.options = Object.assign({
            autoTable: !0,
            autoColumns: !0,
            delimiter: ",",
            exportData: !1,
            batchSize: 1e3,
            enableBatchProcessing: !0,
            asyncDelay: 5
        }, options), (null == options ? void 0 : options.id) && (this.id = options.id);
    }
    run(...args) {
        const tableInstance = args[2];
        this._tableInstance = tableInstance, this._tableInstance.importFile = () => {
            this.import("file");
        };
    }
    release() {
        this._tableInstance = null;
    }
    importFile() {
        return __awaiter(this, void 0, void 0, (function*() {
            return this.import("file");
        }));
    }
    import(type, source, options) {
        return __awaiter(this, void 0, void 0, (function*() {
            const mergedOptions = Object.assign(Object.assign({}, this.options), options);
            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*() {
            return new Promise(((resolve, reject) => {
                if (!this._tableInstance) return void reject(new Error("表格实例不存在或已销毁，无法导入数据！"));
                const input = document.createElement("input");
                input.type = "file", input.style.display = "none", document.body.appendChild(input), 
                input.addEventListener("change", (e => __awaiter(this, void 0, void 0, (function*() {
                    var _a;
                    try {
                        const file = null === (_a = e.target.files) || void 0 === _a ? void 0 : _a[0];
                        if (!file) return document.body.removeChild(input), void reject(new Error("未选择文件"));
                        const result = yield this._parseFile(file, options);
                        options.autoTable && this._tableInstance && (options.autoColumns && this._tableInstance.updateOption(Object.assign({}, this._tableInstance.options, {
                            columns: result.columns
                        })), this._tableInstance.setRecords(result.records)), input.value = "", document.body.removeChild(input), 
                        resolve(result);
                    } catch (error) {
                        document.body.removeChild(input), reject(error);
                    }
                })))), input.click();
            }));
        }));
    }
    _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._tableInstance && (options.autoColumns && this._tableInstance.updateOption({
                columns: result.columns,
                plugins: [ this ]
            }), this._tableInstance.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._tableInstance && (options.autoColumns && this._tableInstance.updateOption({
                columns: result.columns,
                plugins: [ this ]
            }), this._tableInstance.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}`);
            }
        }));
    }
    _getFileExtension(filename) {
        const parts = filename.split(".");
        return parts.length > 1 ? parts[parts.length - 1] : "";
    }
    _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_1.default.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 lines = text.split("\n").filter((line => line.trim()));
            if (0 === lines.length) throw new Error("CSV文件为空");
            const delimiter = options.delimiter || ",", parseCSVLine = line => line.split(delimiter).map((cell => cell.trim().replace(/^"|"$/g, ""))), headerRows = lines.slice(0, 1).map(parseCSVLine), 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}`] = 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))));
            }
        }));
    }
}

exports.ExcelImportPlugin = ExcelImportPlugin;
//# sourceMappingURL=excel-import.js.map