/**
 * Spreadsheet Module
 * Handles table rendering, cell editing, and file operations
 */
(function() {
    'use strict';

    let editingCell = null;
    let editingInput = null;

    /**
     * Render spreadsheet from store
     */
    function render() {
        const tbody = window.app.dom.spreadsheetBody;
        tbody.innerHTML = '';

        // Get files from store (already filtered and sorted)
        const files = window.app.modules.store.getDisplayFiles();

        // Render rows
        if (files.length === 0) {
            const message = window.app.modules.store.getDisplayFiles().length === 0 
                ? '<h3>No files to display</h3><p>Select one or more folders from the tree to view files</p>'
                : '<h3>No files match filters</h3><p>Adjust or clear filters to see files</p>';
            
            tbody.innerHTML = `
                <tr>
                    <td colspan="10" class="spreadsheet-empty">
                        ${message}
                    </td>
                </tr>
            `;
            return;
        }

        files.forEach((file, index) => {
            const row = createRow(file, index);
            tbody.appendChild(row);
        });

        // Update UI
        window.app.modules.app.updateStats();
        updateSortIndicators();

        // Calculate SHA256 if enabled
        if (window.app.calculateSha256) {
            calculateSha256ForAll();
        }
    }

    /**
     * Update sort indicators
     */
    function updateSortIndicators() {
        const sortColumns = window.app.modules.store.getSortColumns();
        const headers = window.app.dom.spreadsheet.querySelectorAll('thead th');

        headers.forEach(th => {
            const indicator = th.querySelector('.sort-indicator');
            if (!indicator) return;

            const columnName = th.className.replace('col-', '');
            const sortIndex = sortColumns.findIndex(s => s.column === columnName);

            if (sortIndex >= 0) {
                const sort = sortColumns[sortIndex];
                const arrow = sort.direction === 'asc' ? '▲' : '▼';
                const priority = sortColumns.length > 1 ? (sortIndex + 1) : '';
                indicator.textContent = ` ${arrow}${priority}`;
                indicator.style.display = 'inline';
            } else {
                indicator.textContent = '';
                indicator.style.display = 'none';
            }
        });
    }

    /**
     * Create a table row for a file
     */
    function createRow(file, index) {
        const row = document.createElement('tr');
        row.dataset.index = index;
        row.dataset.folderPath = file.folderPath; // Store folder path for highlighting

        // Add state classes
        if (file.isDirty) row.classList.add('modified');
        if (file.error) row.classList.add('error');
        
        // Highlight folder on hover
        row.addEventListener('mouseenter', () => {
            highlightFolder(file.folderPath);
        });
        
        row.addEventListener('mouseleave', () => {
            clearFolderHighlight();
        });

        // Row number
        row.appendChild(createCell('row-num', index + 1, false));

        // Original filename — plain text (selectable/copyable, no link)
        const originalCell = createCell('original', file.originalFilename, false);
        row.appendChild(originalCell);

        // Extension — hyperlink to open the file
        const extCell = createCell('extension', '', false);
        const extLink = document.createElement('a');
        extLink.className = 'cell-link';
        extLink.textContent = file.extension;
        extLink.title = 'Click to open file';
        extLink.style.cursor = 'pointer';
        extLink.addEventListener('click', async (e) => {
            e.preventDefault();
            e.stopPropagation();
            openFile(file);
        });
        extCell.appendChild(extLink);
        row.appendChild(extCell);

        // Parse original filename to get ZDDC components (always)
        // Pass full filename (name + extension) so the regex can match the .ext suffix
        const parsed = zddc.parseFilename(zddc.joinExtension(file.originalFilename, file.extension)) || {};

        // Fill any empty fields from parsed filename (per-field, not all-or-nothing)
        // Must happen before computeNewFilename so all fields are available
        if (!file.trackingNumber) file.trackingNumber = parsed.trackingNumber || '';
        if (!file.revision) file.revision = parsed.revision || '';
        if (!file.status)   file.status   = parsed.status   || '';
        if (!file.title)    file.title    = parsed.title    || '';

        // New filename: show only if it would actually change the file
        const computedFilename = file.manualFilename || computeNewFilename(file, index);
        const originalFullName = zddc.joinExtension(file.originalFilename, file.extension);
        const wouldChange = computedFilename !== originalFullName;
        const newFilenameDisplay = wouldChange ? computedFilename : '';
        const newFilenameCell = createEditableCell('newFilename', newFilenameDisplay, index);
        // Use computedFilename (not newFilenameDisplay) for validation
        const newFilename = computedFilename;
        if (!file.manualFilename) {
            newFilenameCell.classList.add('computed');
        }
        
        // Validate and show errors
        const validation = window.app.modules.validator.validateFilename(newFilename);
        if (!validation.isValid) {
            newFilenameCell.classList.add('validation-error');
            newFilenameCell.title = validation.errors.join('; ');
        } else if (validation.warnings.length > 0) {
            newFilenameCell.classList.add('validation-warning');
            newFilenameCell.title = validation.warnings.join('; ');
        }
        
        // Only show action buttons if row is dirty
        if (file.isDirty) {
            const actions = document.createElement('span');
            actions.className = 'inline-actions';
            
            const saveBtn = document.createElement('button');
            saveBtn.className = 'btn-inline btn-save';
            saveBtn.textContent = '✓';
            saveBtn.title = 'Save';
            saveBtn.addEventListener('click', (e) => {
                e.stopPropagation();
                saveFile(index);
            });
            
            const cancelBtn = document.createElement('button');
            cancelBtn.className = 'btn-inline btn-cancel';
            cancelBtn.textContent = '✗';
            cancelBtn.title = 'Clear all fields';
            cancelBtn.addEventListener('click', (e) => {
                e.stopPropagation();
                cancelFile(index);
            });
            
            actions.appendChild(saveBtn);
            actions.appendChild(cancelBtn);
            newFilenameCell.appendChild(actions);
        }
        
        row.appendChild(newFilenameCell);

        // For each field: use file value (already populated above) for display
        const displayTracking = file.trackingNumber || '';
        const displayRevision = file.revision || '';
        const displayStatus   = file.status   || '';
        const displayTitle    = file.title    || '';

        const trackingCell = createEditableCell('trackingNumber', displayTracking, index);
        const revisionCell = createEditableCell('revision', displayRevision, index);
        const statusCell   = createEditableCell('status',   displayStatus,   index);
        const titleCell    = createEditableCell('title',    displayTitle,    index);

        // Gray = field value matches what the original filename parses to (no change)
        // Blue = field value differs from the parsed original (would produce a different filename)
        if (displayTracking === (parsed.trackingNumber || '')) trackingCell.classList.add('auto-populated');
        else trackingCell.classList.add('field-changed');
        if (displayRevision === (parsed.revision || '')) revisionCell.classList.add('auto-populated');
        else revisionCell.classList.add('field-changed');
        if (displayStatus === (parsed.status || '')) statusCell.classList.add('auto-populated');
        else statusCell.classList.add('field-changed');
        if (displayTitle === (parsed.title || '')) titleCell.classList.add('auto-populated');
        else titleCell.classList.add('field-changed');
        
        row.appendChild(trackingCell);
        row.appendChild(revisionCell);
        row.appendChild(statusCell);
        row.appendChild(titleCell);

        // SHA256 (if enabled)
        if (window.app.calculateSha256) {
            const sha256Cell = createCell('sha256', file.sha256 || 'calculating...', false);
            if (!file.sha256) {
                sha256Cell.classList.add('sha256-calculating');
            }
            row.appendChild(sha256Cell);
        }

        return row;
    }

    /**
     * Create a table cell
     */
    function createCell(className, content, editable = false) {
        const td = document.createElement('td');
        td.className = `col-${className}`;
        td.textContent = content;
        return td;
    }

    /**
     * Create an editable cell
     */
    function createEditableCell(columnName, value, rowIndex) {
        const td = document.createElement('td');
        td.className = `col-${columnName} cell-editable`;
        td.textContent = value;
        
        // Double-click to edit
        td.addEventListener('dblclick', (e) => {
            e.stopPropagation();
            startEditing(td, columnName, rowIndex);
        });
        
        return td;
    }


    /**
     * Start editing a cell
     */
    function startEditing(cell, columnName, rowIndex) {
        // Cancel any existing edit
        if (editingCell) {
            cancelEditing();
        }

        const files = window.app.modules.store.getDisplayFiles();
        const file = files[rowIndex];
        if (!file) return;
        
        const currentValue = file[columnName] || '';

        // Clear any cell selection
        if (window.app.modules.selection) {
            window.app.modules.selection.clearSelection();
        }

        // Store references
        editingCell = { cell, columnName, rowIndex, originalValue: currentValue };

        // Save original content and make cell contenteditable
        cell.dataset.originalContent = cell.innerHTML;
        cell.contentEditable = 'true';
        cell.classList.add('editing');
        editingInput = cell;

        // Set content (text only for editing)
        cell.textContent = currentValue;

        // Focus and select all
        cell.focus();
        
        // Select all text — guard against cell being detached from document
        // (can happen if a re-render fires between dblclick and this point)
        if (document.contains(cell)) {
            const range = document.createRange();
            range.selectNodeContents(cell);
            const selection = window.getSelection();
            selection.removeAllRanges();
            selection.addRange(range);
        }

        // Event listeners
        cell.addEventListener('blur', handleBlur, { once: true });
        cell.addEventListener('keydown', handleKeyDown);
    }

    /**
     * Handle blur event
     */
    function handleBlur() {
        // Small delay to allow click events to fire first
        setTimeout(() => finishEditing(), 100);
    }

    /**
     * Handle keydown in contenteditable
     */
    function handleKeyDown(e) {
        if (e.key === 'Enter') {
            // Enter exits edit mode
            e.preventDefault();
            finishEditing();
        } else if (e.key === 'Escape') {
            // Escape undoes and exits edit mode
            e.preventDefault();
            cancelEditing();
        } else if (e.key === 'Tab') {
            // Tab/Shift+Tab moves to next/prev cell
            e.preventDefault();
            const { rowIndex, columnName } = editingCell || {};
            const shiftKey = e.shiftKey;
            finishEditingQuiet();  // Don't trigger store update
            if (rowIndex !== undefined) {
                if (shiftKey) {
                    moveToPreviousCell(rowIndex, columnName);
                } else {
                    moveToNextCell(rowIndex, columnName);
                }
            }
        } else if (e.key === 'ArrowUp') {
            e.preventDefault();
            const { rowIndex, columnName } = editingCell || {};
            finishEditingQuiet();
            if (rowIndex !== undefined) {
                moveUpRow(rowIndex, columnName);
            }
        } else if (e.key === 'ArrowDown') {
            e.preventDefault();
            const { rowIndex, columnName } = editingCell || {};
            finishEditingQuiet();
            if (rowIndex !== undefined) {
                moveDownRow(rowIndex, columnName);
            }
        } else if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
            // Allow normal cursor movement within cell
            e.stopPropagation();
        }
    }

    /**
     * Finish editing and save value
     */
    function finishEditing() {
        if (!editingCell || !editingInput) return;

        const { cell, columnName, rowIndex } = editingCell;
        const newValue = editingInput.textContent.trim();
        const files = window.app.modules.store.getDisplayFiles();
        const file = files[rowIndex];
        if (!file) return;
        
        const oldValue = file[columnName] || '';

        // Remove contenteditable
        editingInput.contentEditable = 'false';
        editingInput.classList.remove('editing');
        editingInput.removeEventListener('keydown', handleKeyDown);

        // Update file data if changed
        if (newValue !== oldValue) {
            // Special handling for newFilename column
            if (columnName === 'newFilename') {
                if (newValue) {
                    window.app.modules.store.updateFileField(rowIndex, 'manualFilename', newValue);
                } else {
                    window.app.modules.store.updateFile(rowIndex, { manualFilename: null });
                }
            } else {
                window.app.modules.store.updateFileField(rowIndex, columnName, newValue);
            }

            const updatedFile = window.app.modules.store.getDisplayFiles()[rowIndex];
            validateFile(updatedFile);
        }

        editingCell = null;
        editingInput = null;
    }
    
    /**
     * Finish editing without triggering store update (for Tab/Arrow navigation)
     */
    function finishEditingQuiet() {
        if (!editingCell || !editingInput) return;

        const { columnName, rowIndex } = editingCell;
        const newValue = editingInput.textContent.trim();
        const files = window.app.modules.store.getDisplayFiles();
        const file = files[rowIndex];
        
        // Remove contenteditable
        editingInput.contentEditable = 'false';
        editingInput.classList.remove('editing');
        editingInput.removeEventListener('keydown', handleKeyDown);

        // Update file object directly (no store notification)
        if (file) {
            if (columnName === 'newFilename') {
                file.manualFilename = newValue || null;
            } else {
                file[columnName] = newValue;
            }
            file.isDirty = true;
        }

        editingCell = null;
        editingInput = null;
    }

    /**
     * Cancel editing without saving
     */
    function cancelEditing() {
        if (!editingCell) return;

        const { rowIndex } = editingCell;
        const files = window.app.modules.store.getDisplayFiles();
        const file = files[rowIndex];
        if (!file) return;

        // Clear editing state
        editingCell = null;
        editingInput = null;

        // Re-render the row
        const row = window.app.dom.spreadsheetBody.querySelector(`tr[data-index="${rowIndex}"]`);
        if (row) {
            const newRow = createRow(file, rowIndex);
            row.replaceWith(newRow);
        }
    }

    /**
     * Move to next editable cell
     */
    function moveToNextCell(rowIndex, currentColumn) {
        const columns = ['newFilename', 'trackingNumber', 'revision', 'status', 'title'];
        const currentIndex = columns.indexOf(currentColumn);
        
        if (currentIndex < columns.length - 1) {
            // Next column in same row
            const nextColumn = columns[currentIndex + 1];
            const row = window.app.dom.spreadsheetBody.querySelector(`tr[data-index="${rowIndex}"]`);
            const nextCell = row.querySelector(`.col-${nextColumn}`);
            if (nextCell) {
                startEditing(nextCell, nextColumn, rowIndex);
            }
        } else if (rowIndex < window.app.modules.store.getDisplayFiles().length - 1) {
            // First column of next row
            const nextColumn = columns[0];
            const nextRow = window.app.dom.spreadsheetBody.querySelector(`tr[data-index="${rowIndex + 1}"]`);
            const nextCell = nextRow.querySelector(`.col-${nextColumn}`);
            if (nextCell) {
                startEditing(nextCell, nextColumn, rowIndex + 1);
            }
        }
    }

    /**
     * Move to previous editable cell
     */
    function moveToPreviousCell(rowIndex, currentColumn) {
        const columns = ['newFilename', 'trackingNumber', 'revision', 'status', 'title'];
        const currentIndex = columns.indexOf(currentColumn);
        
        if (currentIndex > 0) {
            // Previous column in same row
            const prevColumn = columns[currentIndex - 1];
            const row = window.app.dom.spreadsheetBody.querySelector(`tr[data-index="${rowIndex}"]`);
            const prevCell = row.querySelector(`.col-${prevColumn}`);
            if (prevCell) {
                startEditing(prevCell, prevColumn, rowIndex);
            }
        } else if (rowIndex > 0) {
            // Last column of previous row
            const prevColumn = columns[columns.length - 1];
            const prevRow = window.app.dom.spreadsheetBody.querySelector(`tr[data-index="${rowIndex - 1}"]`);
            const prevCell = prevRow.querySelector(`.col-${prevColumn}`);
            if (prevCell) {
                startEditing(prevCell, prevColumn, rowIndex - 1);
            }
        }
    }

    /**
     * Move up one row, same column
     */
    function moveUpRow(rowIndex, currentColumn) {
        if (rowIndex > 0) {
            const prevRow = window.app.dom.spreadsheetBody.querySelector(`tr[data-index="${rowIndex - 1}"]`);
            const cell = prevRow.querySelector(`.col-${currentColumn}`);
            if (cell) {
                startEditing(cell, currentColumn, rowIndex - 1);
            }
        }
    }

    /**
     * Move down one row, same column
     */
    function moveDownRow(rowIndex, currentColumn) {
        if (rowIndex < window.app.modules.store.getDisplayFiles().length - 1) {
            const nextRow = window.app.dom.spreadsheetBody.querySelector(`tr[data-index="${rowIndex + 1}"]`);
            const cell = nextRow.querySelector(`.col-${currentColumn}`);
            if (cell) {
                startEditing(cell, currentColumn, rowIndex + 1);
            }
        }
    }

    function computeNewFilename(file) {
        return window.app.modules.utils.computeNewFilename(file);
    }

    /**
     * Validate a file
     */
    function validateFile(file) {
        const newFilename = computeNewFilename(file, 0);
        const validation = window.app.modules.validator.validateFilename(newFilename);
        
        file.validation = validation;
        file.error = !validation.isValid;
    }

    /**
     * Open file in new tab
     */
    async function openFile(file) {
        try {
            let blob;
            if (file.isVirtual) {
                // Virtual file from ZIP - get from cache
                const cached = window.app.modules.scanner.getZipCache(file.zipPath);
                if (!cached) throw new Error('ZIP not found in cache');
                const zipEntry = cached.zip.file(file.zipEntryPath);
                if (!zipEntry) throw new Error('File not found in ZIP');
                const arrayBuffer = await zipEntry.async('arraybuffer');
                const mimeType = getMimeType(file.extension);
                blob = new Blob([arrayBuffer], { type: mimeType });
            } else {
                blob = await file.handle.getFile();
            }
            const url = URL.createObjectURL(blob);
            window.open(url, '_blank');
            
            // Clean up URL after a delay
            setTimeout(() => URL.revokeObjectURL(url), 60000);
        } catch (err) {
            console.error('Error opening file:', err);
            alert('Cannot open file: ' + err.message);
        }
    }
    
    function getMimeType(extension) {
        return window.app.modules.utils.getMimeType(extension);
    }

    /**
     * Save a single file
     */
    async function saveFile(index, skipValidation = false) {
        const files = window.app.modules.store.getDisplayFiles();
        const file = files[index];
        if (!file.isDirty) {

            return;
        }
        
        // Virtual files (from ZIPs) cannot be renamed - must extract first
        if (file.isVirtual) {
            alert('Cannot rename files inside ZIP archives.\nExtract the ZIP first to rename files.');
            return;
        }

        const row = window.app.dom.spreadsheetBody.querySelector(`tr[data-index="${index}"]`);
        if (!row) {
            console.error(`Row not found for index ${index}`);
            return;
        }
        row.classList.add('saving');

        try {
            const newFilename = computeNewFilename(file, index);
            const currentFilename = zddc.joinExtension(file.originalFilename, file.extension);
            
            // Check if already has correct name
            if (currentFilename === newFilename) {

                row.classList.remove('saving');
                
                // Just clear dirty flag and fields
                file.isDirty = false;
                file.error = false;
                delete file.manualFilename;
                file.trackingNumber = '';
                file.revision = '';
                file.status = '';
                file.title = '';
                
                const newRow = createRow(file, index);
                row.replaceWith(newRow);
                window.app.modules.app.updateStats();
                return;
            }
            
            // Validate filename
            if (!skipValidation) {
                const validation = window.app.modules.validator.validateFilename(newFilename);
                if (!validation.isValid) {
                    const errors = validation.errors.join('\n');
                    const confirmed = confirm(
                        `⚠️ Warning: Filename is not ZDDC compliant!\n\n` +
                        `Errors:\n${errors}\n\n` +
                        `Current filename: ${newFilename}\n\n` +
                        `Do you want to save it anyway?`
                    );
                    
                    if (!confirmed) {
                        row.classList.remove('saving');
                        return;
                    }
                }
            }
            
            // Request write permission for the folder
            const folderPermission = await file.folderHandle.queryPermission({ mode: 'readwrite' });
            if (folderPermission !== 'granted') {
                const granted = await file.folderHandle.requestPermission({ mode: 'readwrite' });
                if (granted !== 'granted') {
                    throw new Error('Write permission denied');
                }
            }
            
            // Rename. HTTP-backed handles (zddc-server) get the atomic
            // POST /op=move path — single round-trip, server-side
            // os.Rename, no risk of half-renamed state. Local FS Access
            // API handles use copy+remove because the API has no native
            // rename verb.
            const oldFilename = zddc.joinExtension(file.originalFilename, file.extension);

            try {
                if (window.zddc.source.isHttpHandle(file.folderHandle)) {
                    const folderUrl = file.folderHandle.url();
                    const folderPath = new URL(folderUrl).pathname;
                    const srcPath = folderPath + encodeURIComponent(oldFilename);
                    const dstPath = folderPath + encodeURIComponent(newFilename);
                    await window.zddc.source.moveFile(srcPath, dstPath);
                    file.handle = await file.folderHandle.getFileHandle(newFilename);
                } else {
                    // Get fresh handle for old file
                    const oldHandle = await file.folderHandle.getFileHandle(oldFilename);

                    // Read the file content
                    const fileData = await oldHandle.getFile();

                    // Create new file with new name
                    const newHandle = await file.folderHandle.getFileHandle(newFilename, { create: true });
                    const writable = await newHandle.createWritable();
                    await writable.write(fileData);
                    await writable.close();

                    // Delete old file
                    await file.folderHandle.removeEntry(oldFilename);

                    // Update file handle
                    file.handle = newHandle;
                }
            } catch (err) {
                console.error(`Failed to rename file:`, err);
                throw err;
            }
            
            // Update file data directly (don't trigger store notification during batch save)
            file.originalFilename = zddc.splitExtension(newFilename).name;
            file.isDirty = false;
            file.error = false;
            file.manualFilename = null;
            file.trackingNumber = '';
            file.revision = '';
            file.status = '';
            file.title = '';
            file.autoPopulated = false;
            
            // Update row UI
            row.classList.remove('saving');
            const newRow = createRow(file, index);
            row.replaceWith(newRow);
            
        } catch (err) {
            console.error('Error saving file:', err);
            file.error = true;
            file.errorMessage = err.message;
            row.classList.remove('saving');
            row.classList.add('error');
            // Re-throw so caller can handle
            throw err;
        }
    }

    /**
     * Cancel/Clear all fields for a single file
     */
    function cancelFile(index) {
        // Clear all fields through store
        window.app.modules.store.updateFile(index, {
            trackingNumber: '',
            revision: '',
            status: '',
            title: '',
            manualFilename: null,
            isDirty: false,
            error: false,
            validation: null,
            autoPopulated: false
        });
    }

    /**
     * Save all modified files (only ZDDC-compliant ones)
     */
    async function saveAllFiles() {
        const files = window.app.modules.store.getDisplayFiles();
        const modifiedFiles = files
            .map((file, index) => ({ file, index }))
            .filter(({ file }) => file.isDirty);

        if (modifiedFiles.length === 0) {
            alert('No modified files to save.');
            return;
        }

        let successCount = 0;
        let skippedCount = 0;
        let errorCount = 0;
        const errors = [];
        const skipped = [];

        for (let i = 0; i < modifiedFiles.length; i++) {
            const { file, index } = modifiedFiles[i];
            
            try {
                // Add small delay between operations to prevent race conditions
                if (i > 0) {
                    await new Promise(resolve => setTimeout(resolve, 200));
                }
                
                // Validate before saving
                const newFilename = computeNewFilename(file, index);
                const currentFilename = zddc.joinExtension(file.originalFilename, file.extension);
                

                
                const validation = window.app.modules.validator.validateFilename(newFilename);
                
                if (!validation.isValid) {
                    // Skip non-compliant files in Save All
                    skippedCount++;
                    skipped.push(`${zddc.joinExtension(file.originalFilename, file.extension)}: ${validation.errors[0]}`);

                    continue;
                }
                
                // Check if already has correct name
                if (currentFilename === newFilename) {

                    // Just clear dirty flag
                    file.isDirty = false;
                    file.error = false;
                    delete file.manualFilename;
                    file.trackingNumber = '';
                    file.revision = '';
                    file.status = '';
                    file.title = '';
                    successCount++;
                    continue;
                }
                
                // Save with validation already done - ensure properly awaited
                try {
                    await saveFile(index, true);
                    successCount++;

                } catch (saveErr) {
                    console.error(`Error saving file ${index}:`, saveErr);
                    errorCount++;
                    errors.push(`${zddc.joinExtension(file.originalFilename, file.extension)}: ${saveErr.message}`);
                    
                    // Add delay after errors to let filesystem stabilize
                    await new Promise(resolve => setTimeout(resolve, 300));
                }
            } catch (err) {
                console.error(`Error processing file ${index}:`, err);
                errorCount++;
                errors.push(`${file.originalFilename}${file.extension}: ${err.message}`);
            }
        }

        // Trigger store notification to update UI after all saves
        window.app.modules.store.notify('files');
        
        let message = `Saved ${successCount} compliant file(s).`;
        
        if (skippedCount > 0) {
            message += `\n\n⚠️ Skipped ${skippedCount} non-compliant file(s):`;
            message += `\n${skipped.slice(0, 3).join('\n')}`;
            if (skipped.length > 3) {
                message += `\n... and ${skipped.length - 3} more`;
            }
            message += `\n\nUse individual save buttons (✓) to save non-compliant files.`;
        }
        
        if (errorCount > 0) {
            message += `\n\n❌ ${errorCount} error(s):`;
            message += `\n${errors.slice(0, 3).join('\n')}`;
            if (errors.length > 3) {
                message += `\n... and ${errors.length - 3} more`;
            }
        }
        
        alert(message);
    }

    /**
     * Cancel all changes
     */
    function cancelAllChanges() {
        const files = window.app.modules.store.getDisplayFiles();
        files.forEach((file, index) => {
            if (file.isDirty) {
                cancelFile(index);
            }
        });
    }

    /**
     * Calculate SHA256 for all files
     */
    async function calculateSha256ForAll() {
        const files = window.app.modules.store.getDisplayFiles();
        for (let i = 0; i < files.length; i++) {
            const file = files[i];
            if (!file.sha256) {
                calculateSha256(file, i);
            }
        }
    }

    /**
     * Calculate SHA256 for a single file
     */
    async function calculateSha256(file, index) {
        try {
            let hashHex;
            if (file.isVirtual) {
                // Virtual file from ZIP
                const cached = window.app.modules.scanner.getZipCache(file.zipPath);
                if (!cached) throw new Error('ZIP not found in cache');
                const zipEntry = cached.zip.file(file.zipEntryPath);
                if (!zipEntry) throw new Error('File not found in ZIP');
                const buffer = await zipEntry.async('arraybuffer');
                hashHex = await zddc.crypto.sha256Hex(buffer);
            } else {
                const fileObj = await file.handle.getFile();
                hashHex = await zddc.crypto.sha256File(fileObj);
            }

            file.sha256 = hashHex;
            
            // Update cell
            const row = window.app.dom.spreadsheetBody.querySelector(`tr[data-index="${index}"]`);
            if (row) {
                const sha256Cell = row.querySelector('.col-sha256');
                if (sha256Cell) {
                    sha256Cell.textContent = hashHex.substring(0, 16) + '...';
                    sha256Cell.title = hashHex;
                    sha256Cell.classList.remove('sha256-calculating');
                }
            }
        } catch (err) {
            console.error('Error calculating SHA256:', err);
        }
    }

    /**
     * Highlight folder in tree when hovering over file
     */
    function highlightFolder(folderPath) {
        if (!folderPath) return;
        
        // Find folder in tree
        const folderTree = document.getElementById('folderTree');
        if (!folderTree) return;
        
        // Find the folder item by data-path attribute
        const folderItem = folderTree.querySelector(`[data-path="${folderPath}"]`);
        if (!folderItem) return;
        
        // Add highlight class
        folderItem.classList.add('folder-hover-highlight');
        
        // Scroll into view if autoscroll is enabled
        const autoScrollCheckbox = document.getElementById('autoScrollCheckbox');
        if (autoScrollCheckbox && autoScrollCheckbox.checked) {
            folderItem.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
        }
    }
    
    /**
     * Clear folder highlight
     */
    function clearFolderHighlight() {
        const folderTree = document.getElementById('folderTree');
        if (!folderTree) return;
        
        // Remove all highlights
        const highlighted = folderTree.querySelectorAll('.folder-hover-highlight');
        highlighted.forEach(el => el.classList.remove('folder-hover-highlight'));
    }

    /**
     * Initialize spreadsheet - subscribe to store
     */
    function init() {
        // Subscribe to store changes (only call this after DOM is ready)
        window.app.modules.store.on('files', render);
    }

    // Export module
    window.app.modules.spreadsheet = {
        init,
        render,
        computeNewFilename,
        saveAllFiles,
        cancelAllChanges,
        cancelEditing
    };
})();
