// File: grid.js - Dengan Perbaikan Renderer

// === TRI XID - GLOBAL TRACKER ===
window.gridXidTracker = {};

window.gridMethods = {
    /**
     * Menghitung tinggi optimal untuk grid anak berdasarkan jumlah data
     */
     calculateOptimalGridHeight(data, config) {
        if (!data || data.length === 0) {
            return 150; // Tinggi minimum
        }
        
        // Hitung jumlah baris data + header + footer
        const headerHeight = 35; // Tinggi header
        const rowHeight = 25;   // Tinggi per baris
        const footerHeight = 30; // Tinggi footer
        
        const dataRows = data.length;
        const spareRows = config.model === 'grid' ? 1 : 1;
        const totalRows = dataRows + spareRows;
        
        // Hitung total tinggi
        const calculatedHeight = headerHeight + (totalRows * rowHeight) + footerHeight;
        
        // Batasi maksimal tinggi agar tidak terlalu panjang
        const maxHeight = window.innerHeight * 0.7; // Maks 70% tinggi window
        const minHeight = 150; // Minimal 150px
        
        return Math.min(Math.max(calculatedHeight, minHeight), maxHeight);
    },    
    
    /**
     * Membuat atau menginisialisasi ulang instance Handsontable untuk grid.
     */
    createHandsontableGrid(tabId, configKey, config) {
        const currentTab = window.vm.openTabs.find(tab => tab.id === tabId);
        
        // Validasi konfigurasi
        let isParentPosted = false;
        const parentConfigKey = currentTab.mainConfigKey;
        if (parentConfigKey && configKey !== parentConfigKey && currentTab.dbConfigs[parentConfigKey].model === 'form') {
            const parentRecord = currentTab.currentRecord[parentConfigKey];
            if (parentRecord && parentRecord.posting == '1') {
                isParentPosted = true;
            }
        }
        const isReadOnly = (window.globalAkses === 2) || isParentPosted;
        
        if (!config || !Array.isArray(config.specificFields)) {
            console.error(`GridMethods: createHandsontableGrid dipanggil dengan konfigurasi tidak valid atau 'specificFields' hilang untuk configKey: ${configKey}. Pastikan konfigurasi menu di server lengkap.`, { tabId, configKey, config });
            return;
        }

        if (!currentTab) {
            console.error(`GridMethods: Tab ${tabId} not found.`);
            return;
        }

        const gridElementContainer = document.getElementById(`handsontable-${tabId}-${configKey}`);
        if (!gridElementContainer) {
            console.error(`Elemen handsontable-${tabId}-${configKey} tidak ditemukan.`);
            return;
        }

        const gridComponentWrapper = gridElementContainer.closest('.grid-component-wrapper');
        const container = gridComponentWrapper ? gridComponentWrapper.querySelector('.grid-container') : null;
        const footerElement = gridComponentWrapper ? gridComponentWrapper.querySelector('.footerx') : null;
        
        if (!container || !footerElement) {
            console.error(`Struktur HTML untuk grid ${configKey} tidak lengkap (wrapper/footer tidak ditemukan). Pastikan struktur di index.html sudah benar.`);
            return;
        }

        if (config && config.lebar) {
            footerElement.style.width = `${config.lebar * 1.20}px`;
        } else {
            footerElement.style.width = 'auto';
        }

        if (currentTab.gridInstances[configKey] && typeof currentTab.gridInstances[configKey].destroy === 'function') {
            currentTab.gridInstances[configKey].destroy();
        }
        currentTab.gridInstances[configKey] = null;

        // --- AWAL BLOK PERBAIKAN ---
        // Fungsi pembungkus renderer (wrapper) yang telah diperbaiki.
        const createXidRenderer = (originalRenderer) => {
            return function(instance, td, row, col, prop, value, cellProperties) {
                // Tentukan renderer dasar yang harus digunakan.
                // Jika ada renderer kustom (seperti untuk 'formula' atau 'file'), `originalRenderer` akan berisi fungsi tersebut.
                // Jika tidak, biarkan Handsontable yang menentukan renderer default berdasarkan `cellProperties.type` (misalnya 'checkbox', 'numeric').
const baseRenderer = originalRenderer || Handsontable.renderers.getRenderer(cellProperties.type);
                // Jalankan renderer dasar terlebih dahulu. Ini akan menggambar checkbox, memformat angka, dll., dengan benar.
                baseRenderer.apply(this, arguments);
                
                // Setelah sel dirender dengan benar, SEKARANG baru terapkan logika highlight.
                const gridKey = `${tabId}-${configKey}`;
                const rowData = instance.getSourceDataAtRow(row);
                const idField = config.specificFields.find(f => f.autoIncrement || f.id === 'id');
                
                if (rowData && idField && rowData[idField.id]) {
                    const rowId = rowData[idField.id];
                    if (window.gridXidTracker[gridKey] === rowId) {
                        // Terapkan warna latar belakang kuning
                        td.style.backgroundColor = '#fbf7dcff';
                        td.style.color = 'black';
                    }
                }
                
                return td;
            };
        };
        // --- AKHIR BLOK PERBAIKAN ---

        // Renderer functions (fungsi-fungsi ini tidak berubah)
        const formulaRenderer = (instance, td, row, col, prop, value, cellProperties) => {
            td.classList.add('htRightAlign');
            const rowData = instance.getSourceDataAtRow(row);
            const field = config.specificFields.find(f => f.id === prop);
            if (field && field.type === 'formula' && rowData) {
                const calculatedValue = window.formMethods.calculateFormula(config, rowData, field, true);
                td.innerText = calculatedValue;
            } else {
                td.innerText = value !== null && value !== undefined ? value : '';
            }
            return td;
        };

        const fungsiRenderer = (instance, td, row, col, prop, value, cellProperties) => {
            const rowData = instance.getSourceDataAtRow(row);
            const fieldConfig = config.specificFields.find(f => f.id === prop);
            if (!rowData || !fieldConfig || typeof fieldConfig.isi !== 'string' || fieldConfig.isi.trim() === '') {
                td.innerText = '';
                return td;
            }
            const isiString = fieldConfig.isi.trim();
            const match = isiString.match(/^([a-zA-Z0-9_]+)\s*\((.*)\)$/);
            if (match && match[1] && typeof window[match[1]] === 'function') {
                const functionName = match[1];
                const argsString = match[2];
                let result = `Error: Gagal proses fungsi`;
                try {
                    let parsedArgs = [];
                    if (argsString.trim() !== '') {
                        parsedArgs = argsString.split(',').map(arg => {
                            arg = arg.trim();
                            if ((arg.startsWith("'") && arg.endsWith("'")) || (arg.startsWith('"') && arg.endsWith('"'))) {
                                return arg.substring(1, arg.length - 1);
                            }
                            if (!isNaN(arg) && arg.trim() !== '') {
                                return parseFloat(arg);
                            }
                            if (rowData.hasOwnProperty(arg)) {
                                return rowData[arg];
                            }
                            console.warn(`[fungsiRenderer] Field '${arg}' tidak ditemukan di data baris.`);
                            return null;
                        });
                    }
                    result = window[functionName].apply(null, parsedArgs);
                } catch (e) {
                    console.error(`Error saat eksekusi fungsi '${functionName}' di grid:`, e);
                    result = `Error: ${e.message}`;
                }
                td.innerText = result;
            } else {
                td.innerText = `Error: Fungsi tidak valid`;
            }
            return td;
        };

        const passwordRenderer = (instance, td, row, col, prop, value, cellProperties) => {
            td.classList.add('password-cell');
            td.innerText = value ? '********' : '';
            return td;
        };

        const fileRenderer = (instance, td, row, col, prop, value, cellProperties) => {
            if (!instance || typeof instance.getSourceDataAtRow !== 'function') {
                td.innerHTML = 'Error: Grid instance not ready for file upload.';
                return td;
            }
            const rowData = instance.getSourceDataAtRow(row);
            const idField = config.specificFields.find(f => f.autoIncrement || f.id === 'id');
            let canUpload = false;
            if (rowData && idField && rowData[idField.id]) {
                canUpload = true;
            }
            let acceptFileType = '*/*';
            const currentFieldConfig = config.specificFields.find(f => f.id === prop);
            if (currentFieldConfig && currentFieldConfig.fileType) {
                if (currentFieldConfig.fileType === 'image') acceptFileType = 'image/*';
                else if (currentFieldConfig.fileType === 'pdf') acceptFileType = 'application/pdf';
            }
            let uploadButtonHTML = '';
            if (!isReadOnly && canUpload) {
                uploadButtonHTML = `<input type="file" accept="${acceptFileType}" style="display: none;" onchange="window.handleGridFileUpload('${tabId}', '${configKey}', ${row}, '${prop}', this)"><button class="btn btn-sm btn-info" style="padding: 2px 5px; font-size: 0.8em;" onclick="this.previousElementSibling.click()"><i class="fas fa-upload"></i></button>`;
            } else if (!isReadOnly && !canUpload) {
                uploadButtonHTML = '<button class="btn btn-sm btn-info" style="padding: 2px 5px; font-size: 0.8em;" disabled title="Simpan data dulu untuk upload"><i class="fas fa-upload"></i></button>';
            }
            td.innerHTML = uploadButtonHTML;
            if (value) {
                td.innerHTML += (uploadButtonHTML ? ' ' : '') + `<a href="${value}" target="_blank" class="btn btn-sm btn-secondary btn-view-file" style="padding: 2px 5px; font-size: 0.8em;"><i class="fas fa-eye"></i></a>`;
            } else if (!uploadButtonHTML) {
                td.innerHTML = '<span class="no-file-text">-</span>';
            }
            return td;
        };

        const gridColumnFields = config.specificFields.filter(field => field.type !== 'html');
        const columns = gridColumnFields.map(field => {
            const col = { data: field.id, title: field.label };
            if (field.width) col.width = field.width;
            if (isReadOnly && field.id !== config.filterx) col.readOnly = true;
            else if (field.readonly) col.readOnly = true;
            
            let originalRenderer = null;
            
            switch (field.type) {
                case 'text':
                    col.type = 'text';
                    break;
                case 'number':
                    col.type = 'numeric';
                    col.numericFormat = { pattern: field.sum || field.type === 'formula' ? '0,0' : '0,0', culture: 'id-ID' };
                    break;
                case 'date':
                    col.type = 'date';
                    col.dateFormat = 'YYYY-MM-DD';
                    col.correctFormat = true;
                    break;
                case 'checkbox':
                    col.type = 'checkbox';
                    col.checkedTemplate = '1';
                    col.uncheckedTemplate = '0';
                    col.className = 'htCenter';
                    break;
                case 'select':
                    col.type = 'dropdown';
                    col.source = Array.isArray(field.options) ? field.options.map(opt => typeof opt === 'object' && opt !== null ? opt.value : opt) : [];
                    col.strict = true;
                    col.allowInvalid = false;
                    break;
                case 'autocomplete':
                    col.type = 'autocomplete';
                    col.source = (query, process) => {
                        if (field.link && field.link[0]) {
                            const link = field.link[0];
                            window.searchAutocomplete(query, results => process(results), link.table, link.field1, link.field2);
                        } else { process([]); }
                    };
                    col.strict = false;
                    col.allowInvalid = true;
                    col.filter = true;
                    col.trimDropdown = false;
                    break;
                case 'formula':
                    col.readOnly = true;
                    originalRenderer = formulaRenderer;
                    col.className = 'htRightAlign';
                    break;
                case 'fungsi':
                    col.readOnly = true;
                    originalRenderer = fungsiRenderer;
                    break;
                case 'file':
                    originalRenderer = fileRenderer;
                    col.readOnly = true;
                    break;
                case 'password':
                    col.type = 'password';
                    originalRenderer = passwordRenderer;
                    break;
                case 'dropdown':
                    col.type = 'autocomplete'; // Gunakan autocomplete untuk UI yang lebih fleksibel
                    originalRenderer = function (instance, td, row, col, prop, value, cellProperties) {
                        Handsontable.renderers.TextRenderer.apply(this, arguments);
                        const fullSource = field.source || [];
                        if (value !== null && value !== undefined && fullSource.length > 0) {
                            const matchedOption = fullSource.find(s => String(s).split(' _ ')[0].trim() === String(value).trim());
                            td.innerHTML = matchedOption ? String(matchedOption).split(' _ ')[1].trim() : value;
                        } else {
                            td.innerHTML = value;
                        }
                    };
                    col.source = function (query, process) {
                        const fullSource = field.source || [];
                        const options = fullSource.filter(option => {
                            return option.toLowerCase().includes(query.toLowerCase());
                        });
                        process(options);
                    };
                    col.strict = false;
                    col.allowInvalid = true;
                    col.filter = false;
                    break;
                default:
                    col.type = 'text';
                    break;
            }
            
            // Terapkan wrapper renderer yang sudah diperbaiki
            col.renderer = createXidRenderer(originalRenderer);
            
            if (field.required && !col.readOnly) {
                col.validator = function (value, callback) {
                    if (field.type === 'checkbox') {
                        callback(true);
                    } else {
                        callback(value !== null && value !== undefined && String(value).trim() !== '');
                    }
                };
            }
            return col;
        });

        if (!isReadOnly) {
            columns.push({
                data: '_actions',
                title: 'Aksi',
                renderer: (instance, td, row, col, prop, value, cellProperties) => {
                    if (!instance.isEmptyRow(row)) {
                        const spareRowsCount = instance.getSettings().minSpareRows;
                        if (spareRowsCount > 0 && row >= (instance.countRows() - spareRowsCount)) {
                            td.innerHTML = '';
                        } else {
                            td.innerHTML = `<button class="htButtons delete-row" title="Hapus Baris"><i class="fas fa-trash-alt"></i></button>`;
                            td.style.textAlign = 'center';
                        }
                    } else {
                        td.innerHTML = '';
                    }
                    return td;
                },
                readOnly: true,
                width: 35
            });
        }

        const hiddenColumnsConfig = {
            columns: gridColumnFields
                .map((field, index) => (field.hidden ? index : -1))
                .filter(index => index !== -1),
            indicators: true
        };

        let gridHeight;
        if (configKey === currentTab.mainConfigKey) {
            gridHeight = window.innerHeight * 0.3;
        } else {
            gridHeight = window.innerHeight * 0.3;
        }

        const hotOptions = {
            data: (currentTab && currentTab.gridData && currentTab.gridData[configKey]) ? currentTab.gridData[configKey] : [],
            columns: columns,
            rowHeaders: true,
            fixedColumnsLeft: 0,
            colHeaders: gridColumnFields.map(f => f.label).concat(isReadOnly ? [] : ['Aksi']),
            minSpareRows: isReadOnly ? 0 : 1,
            autoRowSize: false,
            manualColumnResize: true,
            manualRowResize: true,
            stretchH: 'none',
            autoWrapRow: false,
            autoWrapCol: false,
            licenseKey: 'non-commercial-and-evaluation',
            enterMoves: { row: 0, col: 1 },
            tabMoves: { row: 0, col: 1 },
            columnSorting: true,
            filters: true,
            hiddenColumns: hiddenColumnsConfig,
            dropdownMenu: ['filter_by_condition', 'filter_action_bar', 'filter_by_value'],
            manualRowMove: !isReadOnly,
            manualColumnMove: true,
            contextMenu: !isReadOnly ? ['row_above', 'row_below', 'remove_row', '---------', 'undo', 'redo'] : false,
            preventOverflow: 'horizontal',
            fixedRowsTop: 0,
            height: gridHeight,
            
            afterSelection: function(row, col, row2, col2, preventScrolling, selectionLayerLevel) {
                const gridKey = `${tabId}-${configKey}`;
                const hotInstance = currentTab.gridInstances[configKey];
                
                if (hotInstance && row !== null && row !== undefined) {
                    const rowData = hotInstance.getSourceDataAtRow(row);
                    const idField = config.specificFields.find(f => f.autoIncrement || f.id === 'id');
                    
                    if (rowData && idField && rowData[idField.id]) {
                        window.gridXidTracker[gridKey] = rowData[idField.id];
                        hotInstance.render();
                    }
                }
            },
            
            beforeKeyDown: function(e) {
                if (e.key === 'Enter' || e.keyCode === 13) {
                    e.stopImmediatePropagation();
                    e.preventDefault();
                    const hot = this;
                    const selected = hot.getSelected();
                    
                    if (!selected || selected.length === 0) return;
                    
                    const lastSelection = selected[0];
                    const currentRow = lastSelection[0];
                    const currentCol = lastSelection[1];
                    
                    if (currentCol < hot.countCols() - 1) {
                         hot.selectCell(currentRow, currentCol + 1);
                    }
                }
            },
            
            afterColumnResize: (currentColumn, newSize, isDoubleClick) => {
                const hotInstance = currentTab.gridInstances[configKey];
                if (!hotInstance) return;
                const columnId = hotInstance.colToProp(currentColumn);
                const fieldConfig = config.specificFields.find(f => f.id === columnId);
                console.log(`Kolom '${fieldConfig.label}' (ID: ${columnId}) diubah ukurannya menjadi ${newSize}px.`);
            },
            
            afterChange: async (changes, source) => {
                if (source === 'loadData' || source === 'internalUpdate' || !changes || isReadOnly) {
                    if (source === 'loadData' || source === 'internalUpdate') {
                        if (window.gridMethods && typeof window.gridMethods.updateGridSummary === 'function') {
                            this.updateGridSummary(tabId, configKey, config);
                        }
                    }
                    return;
                }
            
                const hotInstance = currentTab.gridInstances[configKey];
                if (!hotInstance) return;
            
                const rowsToSave = new Set();
            
                for (const change of changes) {
                    const [row, prop, oldValue, newValue] = change;
                    if (prop === '_actions') continue;
            
                    const rowData = hotInstance.getSourceDataAtRow(row);
                    if (!rowData || hotInstance.isEmptyRow(row)) continue;
            
                    rowsToSave.add(row);
            
                    const fieldConfigChanged = config.specificFields.find(f => f.id === prop);
            
                    if (fieldConfigChanged && (fieldConfigChanged.type === 'autocomplete' || fieldConfigChanged.type === 'dropdown') && fieldConfigChanged.link && fieldConfigChanged.link[0] && newValue) {
                        const linkInfo = fieldConfigChanged.link[0];
                        let idPart = newValue;
                        let namePart = '';
            
                        if (String(newValue).includes(' _ ')) {
                            const parts = String(newValue).split(' _ ');
                            idPart = parts[0].trim();
                            namePart = parts.length > 1 ? parts[1].trim() : '';
                        }
                        
                        if (rowData[prop] !== idPart) {
                            hotInstance.setDataAtCell(row, hotInstance.propToCol(prop), idPart, 'internalUpdate');
                        }
                        if (linkInfo.field3 && namePart && rowData[linkInfo.field3] !== namePart) {
                            hotInstance.setDataAtCell(row, hotInstance.propToCol(linkInfo.field3), namePart, 'internalUpdate');
                        }
                    }
            
                    config.specificFields.forEach(f => {
                        if (f.type === 'formula' && f.id !== prop) {
                            const calculated = window.formMethods.calculateFormula(config, rowData, f, false);
                            if (rowData[f.id] !== calculated) {
                                hotInstance.setDataAtCell(row, hotInstance.propToCol(f.id), calculated, 'internalUpdate');
                            }
                        }
                    });
                }
            
                for (const row of rowsToSave) {
                    const rowData = hotInstance.getSourceDataAtRow(row);
                    if (!rowData || hotInstance.isEmptyRow(row)) continue;
            
                    const idField = config.specificFields.find(f => f.autoIncrement || f.id === 'id');
                    const currentId = rowData[idField.id];
            
                    let hasMeaningfulData = false;
                    const dataToSend = {};
                    const dataFieldTypes = ['text', 'number', 'date', 'checkbox', 'select', 'autocomplete', 'password', 'dropdown'];
            
                    config.specificFields.forEach(f => {
                        if (!dataFieldTypes.includes(f.type) && !(f.type === 'formula' && f.saveValue === true)) {
                            return;
                        }
            
                        const val = rowData[f.id];
            
                        if (f.type === 'checkbox') {
                            hasMeaningfulData = true;
                            dataToSend[f.id] = (val === '1' || val === 1 || val === true) ? '1' : '0';
                        }
                        else if (val !== undefined && val !== null && String(val).trim() !== '') {
                            dataToSend[f.id] = val;
                            if (f.id !== idField.id) hasMeaningfulData = true;
                        } 
                        else if (f.type === 'number' && val === 0) {
                            dataToSend[f.id] = 0;
                            hasMeaningfulData = true;
                        }
                    });
            
                    if (config.filter && currentTab.parentFilterValue !== null && currentTab.parentFilterValue !== undefined) {
                        if (config.specificFields.some(sf => sf.id === config.filter)) {
                            dataToSend[config.filter] = currentTab.parentFilterValue;
                        }
                        hasMeaningfulData = true;
                    }
            
                    if (!currentId && Array.isArray(config.inheritFields)) {
                        let parentConfigKey = Object.keys(currentTab.dbConfigs).find(k => currentTab.dbConfigs[k] && currentTab.dbConfigs[k].model === 'form');
                        if (parentConfigKey && currentTab.currentRecord[parentConfigKey]) {
                            const parentRecord = currentTab.currentRecord[parentConfigKey];
                            for (const fieldMap of config.inheritFields) {
                                dataToSend[fieldMap.to] = parentRecord[fieldMap.from];
                                const destColIndex = hotInstance.propToCol(fieldMap.to);
                                if (destColIndex !== null) {
                                    hotInstance.setDataAtCell(row, destColIndex, parentRecord[fieldMap.from], 'internalUpdate');
                                }
                            }
                            hasMeaningfulData = true;
                        }
                    }
                    
                    if (hasMeaningfulData) {
                        try {
                            let saveResult;
                            if (currentId && currentId > 0) {
                                saveResult = await window.rubahjson(config.table, dataToSend, currentId, '');
                            } else {
                                if (idField.autoIncrement && dataToSend.hasOwnProperty(idField.id) && !dataToSend[idField.id]) {
                                    delete dataToSend[idField.id];
                                }
                                saveResult = await window.tambahjson(config.table, dataToSend);
                                if (saveResult.success && saveResult.id > 0) {
                                    hotInstance.setDataAtCell(row, hotInstance.propToCol(idField.id), saveResult.id, 'internalUpdate');
                                }
                            }
            
                            if (!saveResult.success) {
                                alert('Gagal menyimpan data grid: ' + (saveResult.message || saveResult.error || 'Unknown error'));
                            }
                        } catch (error) {
                            alert('Error menyimpan data grid: ' + error.message);
                        }
                    }
                }
            
                if (window.gridMethods && typeof window.gridMethods.updateGridSummary === 'function') {
                    this.updateGridSummary(tabId, configKey, config);
                }
            },
            
            afterSelectionEnd: async function(r, c, r2, c2, selectionLayerLevel) {
                if (selectionLayerLevel !== 0) return;
                
                const hotInstance = currentTab.gridInstances[configKey];
                const selectedRowData = hotInstance.getSourceDataAtRow(r);
                
                if (configKey === 'g_user' && selectedRowData && !hotInstance.isEmptyRow(r)) {
                    await syncGUserPermissions(selectedRowData);
                }
                
                if (configKey === 'cabang' && selectedRowData && !hotInstance.isEmptyRow(r)) {
                    await syncCabangPosSettings(selectedRowData, window.vm, tabId, configKey);
                }
                
                if (configKey === 'masterd' && selectedRowData && !hotInstance.isEmptyRow(r)) {
                    await syncMasterdDetails(selectedRowData, window.vm, tabId, configKey);
                }
                
                if (!hotInstance || !config.filterx) return;
                if (selectedRowData && !hotInstance.isEmptyRow(r)) {
                    currentTab.parentFilterValue = selectedRowData[config.filterx];
                    currentTab.parentFilterKeyField = config.filterx;
                } else {
                    currentTab.parentFilterValue = null;
                    currentTab.parentFilterKeyField = config.filterx;
                }
                
                window.vm.refreshDetailGrids(tabId, configKey);
            },
            
            afterLoadData: (sourceData, initialLoad, source) => {
                if (window.gridMethods && typeof window.gridMethods.updateGridSummary === 'function') {
                    this.updateGridSummary(tabId, configKey, config);
                }
                
                if (config.filterx && initialLoad) {
                    const hotInstance = currentTab.gridInstances[configKey];
                    if (hotInstance && hotInstance.countRows() > 0) {
                        const spareRowsCount = hotInstance.getSettings().minSpareRows;
                        if (spareRowsCount === 0 || hotInstance.countRows() > spareRowsCount) {
                            hotInstance.selectCell(0, 0);
                        } else {
                            currentTab.parentFilterValue = null;
                            currentTab.parentFilterKeyField = config.filterx;
                            window.vm.refreshDetailGrids(tabId, configKey);
                        }
                    } else {
                        currentTab.parentFilterValue = null;
                        currentTab.parentFilterKeyField = config.filterx;
                        window.vm.refreshDetailGrids(tabId, configKey);
                    }
                }
            },
            
            afterOnCellMouseDown: (event, coords, TD) => {
                const hotInstance = currentTab.gridInstances[configKey];
                if (!hotInstance || isReadOnly) return;
                const prop = hotInstance.colToProp(coords.col);
                if (prop !== '_actions') return;
                const button = event.target.closest('button.delete-row');
                if (!button) return;
                const rowVisualIndex = coords.row;
                const spareRowsCount = hotInstance.getSettings().minSpareRows;
                if (spareRowsCount > 0 && rowVisualIndex >= (hotInstance.countRows() - spareRowsCount)) {
                    return;
                }
                setTimeout(async () => {
                    const rowData = hotInstance.getSourceDataAtRow(rowVisualIndex);
                    const idFieldConfig = config.specificFields.find(f => f.autoIncrement || f.id === 'id');
                    if (!idFieldConfig) {
                        alert('Error: Konfigurasi field ID tidak ditemukan.');
                        return;
                    }
                    if (rowData && rowData[idFieldConfig.id] && rowData[idFieldConfig.id] > 0) {
                        if (confirm(`Apakah Anda yakin ingin menghapus data dengan ID: ${rowData[idFieldConfig.id]}?`)) {
                            try {
                                const result = await window.hapus(config.table, idFieldConfig.id, rowData[idFieldConfig.id]);
                                if (result.success) {
                                    alert('Data berhasil dihapus dari server.');
                                    hotInstance.alter('remove_row', rowVisualIndex, 1);
                                    this.updateGridSummary(tabId, configKey, config);
                                } else {
                                    alert('Gagal menghapus data dari server: ' + (result.message || result.error || 'Unknown error'));
                                }
                            } catch (error) {
                                alert('Error saat menghubungi server untuk menghapus: ' + error.message);
                            }
                        }
                    } else if (rowData && !hotInstance.isEmptyRow(rowVisualIndex)) {
                        if (confirm('Hapus baris ini dari tampilan? Data belum tersimpan di server.')) {
                            hotInstance.alter('remove_row', rowVisualIndex, 1);
                            this.updateGridSummary(tabId, configKey, config);
                        }
                    }
                }, 0);
            },
            
            afterDestroy: () => {}
        };

        const hot = new Handsontable(container.firstElementChild, hotOptions);
        currentTab.gridInstances[configKey] = hot;
    },

    async loadGridData(tabId, configKey, config, filterValue = null) {
        const currentTab = window.vm.openTabs.find(tab => tab.id === tabId);
        if (!currentTab) { console.error(`Grid load: Tab ${tabId} not found.`); return; }
        
        let hot = currentTab.gridInstances[configKey];
        let isParentPosted = false;
        const parentConfigKey = currentTab.mainConfigKey;
        if (parentConfigKey && configKey !== parentConfigKey && currentTab.dbConfigs[parentConfigKey].model === 'form') {
            const parentRecord = currentTab.currentRecord[parentConfigKey];
            if (parentRecord && parentRecord.posting == '1') {
                isParentPosted = true;
            }
        }
        const isGridReadOnly = (window.globalAkses === 2) || isParentPosted;
        
        if (hot && typeof hot.updateSettings === 'function') {
            hot.updateSettings({
                readOnly: isGridReadOnly,
                contextMenu: !isGridReadOnly,
                minSpareRows: isGridReadOnly ? 0 : 1,
            });
            
            const addButton = document.querySelector(`#handsontable-${tabId}-${configKey}`)?.closest('.grid-component-wrapper')?.querySelector('.footerx button.btn-primary');
            if (addButton) {
                addButton.disabled = isGridReadOnly;
            }
        }
        
        if (!hot) {
            if (config && config.model === 'grid') {
                this.createHandsontableGrid(tabId, configKey, config);
                await window.vm.$nextTick();
                hot = currentTab.gridInstances[configKey];
                if (!hot) {
                    console.error(`GRID.JS: Gagal membuat instance grid ${configKey} saat loadGridData.`);
                    return;
                }
            } else {
                return;
            }
        }
        
        hot.loadData([]);
        
        let result;
        try {
            if (config.filter && filterValue !== null && filterValue !== undefined && String(filterValue).trim() !== '') {
                result = await window.ambil(config.table, config.filter, filterValue, filterValue);
            } else if (config.filter && (filterValue === null || filterValue === undefined || String(filterValue).trim() === '')) {
                result = [];
            } else {
                result = await window.ambil(config.table, 'id', '0', '99999999999');
            }
            
            let data = result;
            if (data && data.error) {
                alert(`Error memuat data grid ${config.nama}: ${data.error}`);
                data = [];
            } else if (data && data.success === false) {
                alert(`Gagal memuat data grid ${config.nama}: ${data.message || data.error}`);
                data = [];
            } else if (!Array.isArray(data)) {
                data = [];
            }
            
            const formattedData = data.map(row => {
                const formattedRow = {};
                config.specificFields.forEach(field => {
                    let value = row[field.id] !== undefined ? row[field.id] : '';
                    if (field.type === 'checkbox') {
                        value = (value === '1' || value === 1 || value === true) ? '1' : '0';
                    }
                    formattedRow[field.id] = value;
                });
                return formattedRow;
            });
            
            if (!currentTab.gridData) currentTab.gridData = {};
            currentTab.gridData[configKey] = formattedData;

            if (currentTab.gridInstances[configKey]) {
                currentTab.gridInstances[configKey].loadData(formattedData);
            }
        } catch (e) {
            console.error(`Gagal memuat data untuk grid ${config.nama} (tabel ${config.table}):`, e);
            if (currentTab.gridInstances[configKey]) {
                currentTab.gridInstances[configKey].loadData([]);
            }
            alert(`Gagal memuat data untuk ${config.nama}. Detail di konsol.`);
        }

        if (config.filterx && currentTab.mainConfigKey && configKey !== currentTab.mainConfigKey) {
            await window.vm.$nextTick();
            
            const hot = currentTab.gridInstances[configKey];
            if (hot) {
                const optimalHeight = this.calculateOptimalGridHeight(
                    currentTab.gridData[configKey], 
                    config
                );
                
                hot.updateSettings({
                    height: optimalHeight
                });
            }
        }
    },

    updateGridSummary(tabId, configKey, config) {
        const currentTab = window.vm.openTabs.find(tab => tab.id === tabId);
        if (!currentTab || !currentTab.gridInstances || !currentTab.gridInstances[configKey] || !config) {
            return;
        }
        
        const hot = currentTab.gridInstances[configKey];
        if (!hot) {
            return;
        }
        
        let actualRows = 0;
        const rowCount = hot.countRows();
        const spareRows = hot.getSettings().minSpareRows;
        const dataRowCount = spareRows > 0 ? Math.max(0, rowCount - spareRows) : rowCount;
        
        for (let i = 0; i < dataRowCount; i++) {
            if (!hot.isEmptyRow(i)) {
                actualRows++;
            }
        }
        
        const newAggregates = {};
        if (config.specificFields && hot && dataRowCount >= 0) {
            config.specificFields.forEach(field => {
                if (field.type === 'number' || field.type === 'formula') {
                    let columnTotal = 0;
                    for (let i = 0; i < dataRowCount; i++) {
                        if (!hot.isEmptyRow(i)) {
                            const rowData = hot.getSourceDataAtRow(i);
                            if (rowData) {
                                let value;
                                if (field.type === 'formula') {
                                    value = window.formMethods.calculateFormula(config, rowData, field, false);
                                } else {
                                    value = parseFloat(rowData[field.id]);
                                }
                                if (typeof value === 'number' && !isNaN(value)) {
                                    columnTotal += value;
                                }
                            }
                        }
                    }
                    newAggregates[`${config.table}_${field.id}`] = columnTotal;
                }
            });
        }
        
        if (!currentTab.gridAggregates) {
            currentTab.gridAggregates = {};
        }
        currentTab.gridAggregates[configKey] = newAggregates;
        
        let summaryText = `Total Baris: ${actualRows}`;
        config.specificFields.forEach(field => {
            if (field.sum && (field.type === 'formula' || field.type === 'number')) {
                const aggregateKey = `${config.table}_${field.id}`;
                if (newAggregates.hasOwnProperty(aggregateKey)) {
                    summaryText += `, Total ${field.label}: ${newAggregates[aggregateKey].toLocaleString('id-ID', { minimumFractionDigits: 0, maximumFractionDigits: 2 })}`;
                }
            }
        });
        
        const summaryElement = document.getElementById(`summary-${tabId}-${configKey}`);
        if (summaryElement) {
            summaryElement.textContent = summaryText;
        }
        if (window.vm) {
            window.vm.$forceUpdate();
        }
    },

    insertRowOrScrollToSpare(tabId, configKey) {
        const currentTab = window.vm.openTabs.find(tab => tab.id === tabId);
        if (!currentTab || window.globalAkses === 2) return;
        
        const hot = currentTab.gridInstances[configKey];
        const currentGridConfig = currentTab.dbConfigs[configKey];
        if (!hot || !currentGridConfig) return;
        
        const spareRows = hot.getSettings().minSpareRows;
        
        if (spareRows > 0) {
            const firstSpareRowIndex = hot.countRows() - spareRows;
            hot.selectCell(firstSpareRowIndex, 0);
            hot.scrollViewportTo(firstSpareRowIndex, 0, true, true);
        } else {
            const lastDataRowIndex = hot.countRows();
            hot.alter('insert_row_below', lastDataRowIndex > 0 ? lastDataRowIndex - 1 : 0);
            const newActualRowIndex = hot.countRows() - 1;
            
            if (currentGridConfig.filter && currentTab.parentFilterValue !== null && currentTab.parentFilterValue !== undefined) {
                const filterColIndex = hot.propToCol(currentGridConfig.filter);
                if (filterColIndex !== null && filterColIndex !== -1) {
                    hot.setDataAtCell(newActualRowIndex, filterColIndex, currentTab.parentFilterValue, 'internalUpdate');
                }
            }
            
            window.vm.$nextTick(() => {
                hot.selectCell(newActualRowIndex, 1);
                hot.scrollViewportTo(newActualRowIndex, 0, true, true);
            });
        }
        
        if (window.gridMethods && typeof window.gridMethods.updateGridSummary === 'function') {
            this.updateGridSummary(tabId, configKey, currentGridConfig);
        }
    },

    exportGridToCsv(tabId, configKey) {
        const currentTab = window.vm.openTabs.find(tab => tab.id === tabId);
        if (!currentTab) return;
        
        const hot = currentTab.gridInstances[configKey];
        const config = currentTab.dbConfigs[configKey];
        if (!hot || !config) {
            alert("Instans Handsontable atau konfigurasi tidak ditemukan.");
            return;
        }
        
        const dataToExport = [];
        const colHeaders = hot.getColHeader();
        dataToExport.push(colHeaders.join(';'));

        const rowCount = hot.countRows();
        const spareRows = hot.getSettings().minSpareRows;
        const dataRowCount = spareRows > 0 ? rowCount - spareRows : rowCount;
        
        for (let i = 0; i < dataRowCount; i++) {
            if (hot.isEmptyRow(i)) continue;
            
            const row = hot.getSourceDataAtRow(i);
            if (!row) continue;
            
            const gridColumnFieldsForExport = config.specificFields.filter(field => field.type !== 'html' && !field.hidden && field.id !== '_actions');
            const rowValues = gridColumnFieldsForExport.map(field => {
                let value = row[field.id];
                
                if (field.type === 'formula') {
                    value = window.formMethods.calculateFormula(config, row, field, false);
                } else if (field.type === 'checkbox') {
                    value = (value === '1' || value === true) ? 'Ya' : 'Tidak';
                } else if (field.type === 'password') {
                    value = '********';
                } else if (field.type === 'dropdown' && field.source && value) {
                    const matchedOption = field.source.find(s => s.split(' _ ')[0].trim() === String(value).trim());
                    value = matchedOption ? matchedOption.split(' _ ').pop().trim() : value;
                }
                
                return `"${String(value === null || value === undefined ? '' : value).replace(/"/g, '""')}"`;
            });
            
            dataToExport.push(rowValues.join(';'));
        }
        
        const csvContent = "data:text/csv;charset=utf-8," + encodeURIComponent(dataToExport.join("\r\n"));
        const link = document.createElement("a");
        link.setAttribute("href", csvContent);
        link.setAttribute("download", `${currentTab.menuId}_${configKey}_grid_data_${new Date().toISOString().slice(0, 10)}.csv`);
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        
        alert('Data grid berhasil diexport ke CSV!');
    }
};

// Helper functions (tidak berubah)
async function syncGUserPermissions(g_user_record) {
    if (!g_user_record || !g_user_record.kode) {
        return;
    }
    try {
        const otorData = await window.ambil('otor', 'id', '0', '99999999999');
        const botorData = await window.ambil('botor', 'g_user', g_user_record.kode, g_user_record.kode);
        
        if (!Array.isArray(otorData) || !Array.isArray(botorData)) {
            console.error("Data 'otor' atau 'botor' yang diterima tidak valid.");
            return;
        }
        
        const existingOtor = new Set(botorData.map(b => b.otor));
        const promises = [];
        
        for (const otor of otorData) {
            if (!existingOtor.has(otor.kode)) {
                const newOtorRecord = {
                    g_user: g_user_record.kode,
                    otor: otor.kode,
                    nama: otor.nama,
                    ya: '0',
                    brand: window.xbrand,
                    cab: window.xcab
                };
                promises.push(window.tambahjson('botor', newOtorRecord));
            }
        }
        
        if (promises.length > 0) {
            console.log(`Menyinkronkan ${promises.length} otorisasi baru...`);
            await Promise.all(promises);
            console.log("Sinkronisasi selesai.");
        }
    } catch (error) {
        console.error(`Error pada sinkronisasi g_user:`, error);
    }
}

async function syncCabangPosSettings(cabang_record, vueInstance, tabId, configKey) {
    if (!cabang_record || !cabang_record.kode) {
        return; 
    }
    try {
        console.log(`Memulai sinkronisasi pengaturan POS untuk cabang kode: ${cabang_record.kode}`);
        
        const posData = await window.ambil('pos', 'id', '0', '99999999999');
        const cposData = await window.ambil('cpos', 'cab', cabang_record.kode, cabang_record.kode);
        
        if (!Array.isArray(posData) || !Array.isArray(cposData)) {
            console.error("Data 'pos' atau 'cpos' yang diterima tidak valid.");
            return;
        }
        
        const existingPosSettings = new Set(cposData.map(c => c.kode));
        const promises = [];
        
        for (const pos of posData) {
            if (!existingPosSettings.has(pos.kode)) {
                const newCposRecord = {
                    cab: cabang_record.kode,
                    kode: pos.kode,
                    jenis: pos.jenis,
                    keterangan: pos.keterangan,
                    tipe: pos.tipe,
                    opsi: pos.opsi,
                    isi: pos.isi,
                    brand: '001'
                };
                promises.push(window.tambahjson('cpos', newCposRecord));
            }
        }
        
        if (promises.length > 0) {
            console.log(`Menyinkronkan ${promises.length} pengaturan POS baru untuk cabang ${cabang_record.kode}...`);
            await Promise.all(promises);
            console.log("Sinkronisasi selesai, me-refresh grid detail...");
            vueInstance.refreshDetailGrids(tabId, configKey);
        }
    } catch (error) {
        console.error(`Error pada sinkronisasi cpos untuk cabang ${cabang_record.kode}:`, error);
    }
}

async function syncMasterdDetails(masterd_record, vueInstance, tabId, configKey) {
    if (!masterd_record || !masterd_record.kode) {
        return; 
    }
    try {
        console.log(`Memulai sinkronisasi detail untuk masterd kode: ${masterd_record.kode}`);
        
        const menusPromise = window.ambil('menus', 'id', '0', '99999999999');
        const menugroupsPromise = window.ambil('menugroups', 'id', '0', '99999999999');
        const masterdbPromise = window.ambil('masterdb', 'masterd', masterd_record.kode, masterd_record.kode);
        const masterdsPromise = window.ambil('masterds', 'masterd', masterd_record.kode, masterd_record.kode);
        
        const [
            menusData,
            menugroupsData,
            masterdbData,
            masterdsData
        ] = await Promise.all([menusPromise, menugroupsPromise, masterdbPromise, masterdsPromise]);
        
        if (!Array.isArray(menusData) || !Array.isArray(menugroupsData) || !Array.isArray(masterdbData) || !Array.isArray(masterdsData)) {
            console.error("Salah satu data yang diterima tidak valid (bukan array).");
            return;
        }
        
        const promises = [];
        
        const existingBarang = new Set(masterdbData.map(db => db.barang));
        for (const menu of menusData) {
            if (!existingBarang.has(menu.kode)) {
                const newMasterdbRecord = {
                    masterd: masterd_record.kode,
                    barang: menu.kode,
                    nama: menu.name,
                    ya: '0',
                    cab: window.xcab,
                    brand: window.xbrand
                };
                promises.push(window.tambahjson('masterdb', newMasterdbRecord));
            }
        }
        
        const existingSubmenu = new Set(masterdsData.map(ds => ds.submenu));
        for (const menugroup of menugroupsData) {
            if (!existingSubmenu.has(menugroup.kode)) {
                const newMasterdsRecord = {
                    masterd: masterd_record.kode,
                    submenu: menugroup.kode,
                    nama: menugroup.name,
                    ya: '0',
                    cab: window.xcab,
                    brand: window.xbrand
                };
                promises.push(window.tambahjson('masterds', newMasterdsRecord));
            }
        }
        
        if (promises.length > 0) {
            console.log(`Menyinkronkan ${promises.length} detail baru untuk masterd ${masterd_record.kode}...`);
            await Promise.all(promises);
            console.log("Sinkronisasi selesai, me-refresh semua grid detail...");
            vueInstance.refreshDetailGrids(tabId, configKey);
        }
    } catch (error) {
        console.error(`Error pada sinkronisasi detail untuk masterd ${masterd_record.kode}:`, error);
    }
}