"use strict";

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

class CrossSheetFormulaValidator {
    constructor(formulaEngine, crossSheetManager, sheetManager) {
        this.validationCache = new Map, this.CACHE_TTL = 5e3, this.defaultOptions = {
            checkCircularReferences: !0,
            checkMissingReferences: !0,
            checkCellBounds: !0,
            maxRecursionDepth: 100
        }, this.formulaEngine = formulaEngine, this.crossSheetManager = crossSheetManager, 
        this.sheetManager = sheetManager;
    }
    validateSheet(sheet, options) {
        const validationOptions = Object.assign(Object.assign({}, this.defaultOptions), options), cacheKey = this.getCacheKey(sheet, validationOptions), cached = this.validationCache.get(cacheKey);
        if (cached && Date.now() - this.getCacheTimestamp(cacheKey) < this.CACHE_TTL) return cached;
        const errors = [];
        try {
            const formulas = this.formulaEngine.exportFormulas(sheet);
            for (const [cellRef, formula] of Object.entries(formulas)) {
                const cell = this.parseA1Notation(cellRef), cellWithSheet = {
                    sheet: sheet,
                    row: cell.row,
                    col: cell.col
                }, formulaErrors = this.validateFormula(formula, cellWithSheet, validationOptions);
                errors.push(...formulaErrors);
            }
            if (validationOptions.checkCircularReferences) {
                const circularErrors = this.checkCircularReferences(sheet);
                errors.push(...circularErrors);
            }
            if (validationOptions.checkMissingReferences) {
                const missingErrors = this.checkMissingReferences(sheet);
                errors.push(...missingErrors);
            }
        } catch (error) {
            errors.push({
                type: "MISSING_REFERENCE",
                message: `Validation failed: ${error instanceof Error ? error.message : "Unknown error"}`,
                sheet: sheet,
                cell: {
                    sheet: sheet,
                    row: 0,
                    col: 0
                }
            });
        }
        const result = {
            valid: 0 === errors.length,
            errors: errors,
            warnings: []
        };
        return this.validationCache.set(cacheKey, result), result;
    }
    validateFormula(formula, cell, options) {
        const errors = [];
        if (!formula.startsWith("=")) return errors;
        try {
            const crossSheetRefs = this.extractCrossSheetReferences(formula);
            for (const ref of crossSheetRefs) if (this.isValidSheet(ref.targetSheet)) {
                if (options.checkCellBounds) {
                    const cellErrors = this.validateCellReferences(ref, cell);
                    errors.push(...cellErrors);
                }
            } else errors.push({
                type: "INVALID_SHEET",
                message: `Invalid sheet reference: ${ref.targetSheet}`,
                sheet: cell.sheet,
                cell: cell,
                target: ref.targetSheet
            });
        } catch (error) {
            errors.push({
                type: "MISSING_REFERENCE",
                message: `Formula validation failed: ${error instanceof Error ? error.message : "Unknown error"}`,
                sheet: cell.sheet,
                cell: cell
            });
        }
        return errors;
    }
    extractCrossSheetReferences(formula) {
        const references = [], sheetRefPattern = /'?([^'!！]+)'?[!！]([A-Z]+[0-9]+)(?:\s*:\s*(?:'?([^'!！]+)'?[!！])?([A-Z]+[0-9]+))?/g;
        let match;
        for (;null !== (match = sheetRefPattern.exec(formula)); ) {
            const targetSheet = match[1], startRef = match[2], endSheetMaybe = match[3], endRef = match[4], targetCells = [];
            if (endRef) {
                if (endSheetMaybe && endSheetMaybe.toLowerCase() !== targetSheet.toLowerCase()) continue;
                const startCell = this.parseA1Notation(startRef), endCell = this.parseA1Notation(endRef);
                for (let row = Math.min(startCell.row, endCell.row); row <= Math.max(startCell.row, endCell.row); row++) for (let col = Math.min(startCell.col, endCell.col); col <= Math.max(startCell.col, endCell.col); col++) targetCells.push({
                    sheet: targetSheet,
                    row: row,
                    col: col
                });
            } else {
                const cell = this.parseA1Notation(startRef);
                targetCells.push({
                    sheet: targetSheet,
                    row: cell.row,
                    col: cell.col
                });
            }
            references.push({
                targetSheet: targetSheet,
                targetCells: targetCells
            });
        }
        return references;
    }
    validateCellReferences(ref, sourceCell) {
        const errors = [];
        for (const targetCell of ref.targetCells) try {
            const cellValue = this.formulaEngine.getCellValue(targetCell);
            if (cellValue.error) {
                const targetRef = `${ref.targetSheet}!${this.getA1Notation(targetCell.row, targetCell.col)}`;
                errors.push({
                    type: "INVALID_CELL",
                    message: `Invalid cell reference: ${targetRef} - ${cellValue.error}`,
                    sheet: sourceCell.sheet,
                    cell: sourceCell,
                    target: targetRef
                });
            }
        } catch (error) {
            errors.push({
                type: "INVALID_CELL",
                message: `Cell validation failed: ${ref.targetSheet}!${this.getA1Notation(targetCell.row, targetCell.col)}`,
                sheet: sourceCell.sheet,
                cell: sourceCell,
                target: `${ref.targetSheet}!${this.getA1Notation(targetCell.row, targetCell.col)}`
            });
        }
        return errors;
    }
    checkCircularReferences(sheet) {
        const errors = [], visited = new Set, recursionStack = new Set, dfs = (currentSheet, path) => {
            const key = currentSheet;
            if (recursionStack.has(key)) {
                const cycleStart = path.indexOf(currentSheet), cycle = path.slice(cycleStart).concat([ currentSheet ]);
                return errors.push({
                    type: "CIRCULAR_REFERENCE",
                    message: `Circular reference detected: ${cycle.join(" -> ")}`,
                    sheet: currentSheet,
                    cell: {
                        sheet: currentSheet,
                        row: 0,
                        col: 0
                    }
                }), !0;
            }
            if (visited.has(key)) return !1;
            visited.add(key), recursionStack.add(key);
            const dependencies = this.crossSheetManager.getCrossSheetDependencies(currentSheet);
            for (const dep of dependencies) if (dfs(dep.precedentSheet, [ ...path, currentSheet ])) return !0;
            return recursionStack.delete(key), !1;
        };
        return dfs(sheet, []), errors;
    }
    checkMissingReferences(sheet) {
        const errors = [], allSheets = this.formulaEngine.getAllSheets(), existingSheets = new Set(allSheets.map((s => s.key))), formulas = this.formulaEngine.exportFormulas(sheet);
        for (const [cellRef, formula] of Object.entries(formulas)) {
            const cell = this.parseA1Notation(cellRef), cellWithSheet = {
                sheet: sheet,
                row: cell.row,
                col: cell.col
            }, crossSheetRefs = this.extractCrossSheetReferences(formula);
            for (const ref of crossSheetRefs) existingSheets.has(ref.targetSheet) || errors.push({
                type: "MISSING_REFERENCE",
                message: `Missing sheet reference: ${ref.targetSheet}`,
                sheet: sheet,
                cell: cellWithSheet,
                target: ref.targetSheet
            });
        }
        return errors;
    }
    validateAllSheets(options) {
        const results = new Map, allSheets = this.formulaEngine.getAllSheets();
        for (const sheetInfo of allSheets) {
            const result = this.validateSheet(sheetInfo.key, options);
            results.set(sheetInfo.key, result);
        }
        return results;
    }
    validateFormulaSyntax(formula) {
        try {
            if (!formula.startsWith("=")) return {
                valid: !0
            };
            const expression = formula.substring(1).trim();
            this.validateExpressionStructure(expression);
            const crossSheetPattern = /([A-Za-z0-9_一-龥]+)[！!]([A-Z]+[0-9]+(?::[A-Z]+[0-9]+)?)/g;
            let match;
            for (;null !== (match = crossSheetPattern.exec(expression)); ) {
                const sheetName = match[1], cellRef = match[2];
                if (!this.isValidSheetName(sheetName)) return {
                    valid: !1,
                    error: `Invalid sheet name: ${sheetName}`
                };
                if (!this.isValidSheet(sheetName)) return {
                    valid: !1,
                    error: `Invalid sheet name: ${sheetName}`
                };
                if (!this.isValidCellReference(cellRef)) return {
                    valid: !1,
                    error: `Invalid cell reference: ${cellRef}`
                };
            }
            return {
                valid: !0
            };
        } catch (error) {
            return {
                valid: !1,
                error: error instanceof Error ? error.message : "Syntax validation failed"
            };
        }
    }
    validateExpressionStructure(expression) {
        if ((expression.match(/\(/g) || []).length !== (expression.match(/\)/g) || []).length) throw new Error("Unmatched parentheses");
        if ((expression.match(/"/g) || []).length % 2 != 0) throw new Error("Unmatched quotes");
    }
    isValidSheetName(sheetName) {
        return /^[A-Za-z_一-龥][A-Za-z0-9_一-龥]*$/.test(sheetName);
    }
    isValidCellReference(cellRef) {
        return /^[A-Z]+[0-9]+$/.test(cellRef) || /^[A-Z]+[0-9]+:[A-Z]+[0-9]+$/.test(cellRef);
    }
    isValidSheet(sheetName) {
        if (this.sheetManager) {
            return this.sheetManager.getAllSheets().some((sheet => sheet.sheetTitle.toLowerCase() === sheetName.toLowerCase()));
        }
        return this.formulaEngine.getAllSheets().some((sheet => sheet.title.toLowerCase() === sheetName.toLowerCase()));
    }
    parseA1Notation(a1Notation) {
        const match = a1Notation.match(/^([A-Z]+)([0-9]+)$/);
        if (!match) throw new Error(`Invalid cell reference: ${a1Notation}`);
        const colLetters = match[1], rowNumber = parseInt(match[2], 10);
        let col = 0;
        for (let i = 0; i < colLetters.length; i++) col = 26 * col + (colLetters.charCodeAt(i) - 65);
        return {
            row: rowNumber - 1,
            col: col
        };
    }
    getA1Notation(row, col) {
        let colStr = "", tempCol = col;
        do {
            colStr = String.fromCharCode(65 + tempCol % 26) + colStr, tempCol = Math.floor(tempCol / 26) - 1;
        } while (tempCol >= 0);
        return `${colStr}${row + 1}`;
    }
    getCacheKey(sheet, options) {
        return `${sheet}_${JSON.stringify(options)}`;
    }
    getCacheTimestamp(_cacheKey) {
        return Date.now();
    }
    clearCache() {
        this.validationCache.clear();
    }
    destroy() {
        this.clearCache();
    }
}

exports.CrossSheetFormulaValidator = CrossSheetFormulaValidator;
//# sourceMappingURL=cross-sheet-formula-validator.js.map