// form.js
// Expose formMethods globally
window.formMethods = {
    /**
     * Menginisialisasi objek record baru dengan nilai default.
     * @param {Array} specificFields - Array objek konfigurasi field.
     * @returns {Object} Objek record baru.
     */
    initializeNewRecord(specificFields) {
        const newRecord = {};
        specificFields.forEach(field => {
            if (field.id === 'id' && field.autoIncrement) {
                newRecord[field.id] = null;
            } else if (field.type === 'checkbox') {
                newRecord[field.id] = '0';
            } else if (field.type === 'number' && !field.readonly && field.type !== 'noseri') {
                newRecord[field.id] = 0;
            }

            // --- AWAL BLOK TAMBAHAN ---
            else if (field.type === 'date') {
                const today = new Date();
                const year = today.getFullYear();
                const month = String(today.getMonth() + 1).padStart(2, '0'); // Bulan dimulai dari 0 (Januari)
                const day = String(today.getDate()).padStart(2, '0');
                newRecord[field.id] = `${year}-${month}-${day}`;
            }
            // --- AKHIR BLOK TAMBAHAN ---

            else if (field.type === 'formula' || field.type === 'noseri' || field.type === 'fungsi') {
                newRecord[field.id] = '';
            }
            // Inisialisasi field untuk path gambar (asumsi nama field adalah 'image_paths')
            // Nama field ini harus konsisten dengan yang Anda gunakan di database
            else if (field.id === 'image_paths') {
                newRecord[field.id] = '';
            } else {
                newRecord[field.id] = '';
            }
        });
        // Pastikan field image_paths selalu ada di record baru, walau tidak di specificFields
        if (!newRecord.hasOwnProperty('image_paths')) {
            newRecord.image_paths = ''; // Default string kosong jika tidak ada di config
        }
        return newRecord;
    },

    async loadFormData(tabId, configKey, config, loadType = 'first', idValue = null) {

        const currentTab = window.vm.openTabs.find(tab => tab.id === tabId);
        if (!currentTab || !config) {
            console.error('Form.js: Tab or configuration not found for:', tabId, configKey);
            return;
        }

        if (currentTab.functionCache) {
            currentTab.functionCache = {};
        }

        currentTab.dataLoaded[configKey] = false;
        let recordData = null;
        let result;

        try {
            if (loadType === 'first') {
                result = await window.ambilawal(config.table, 'id');
            } else if (loadType === 'last') {
                result = await window.ambilakhir(config.table, 'id', '', '');
            } else if (loadType === 'prev' && currentTab.currentRecord[configKey] && currentTab.currentRecord[configKey].id !== null) {
                result = await window.sebelum(config.table, 'id', currentTab.currentRecord[configKey].id);
            } else if (loadType === 'next' && currentTab.currentRecord[configKey] && currentTab.currentRecord[configKey].id !== null) {
                result = await window.berikut(config.table, 'id', currentTab.currentRecord[configKey].id);
            } else if (loadType === 'specificId' && idValue !== null) {
                result = await window.ambil(config.table, 'id', idValue, idValue);
            } else {
                currentTab.currentRecord[configKey] = this.initializeNewRecord(config.specificFields);
                currentTab.currentIndex[configKey] = -1;
                currentTab.totalRecords[configKey] = 0;
                if (config.filterx) {
                    currentTab.parentFilterValue = null;
                    currentTab.parentFilterKeyField = config.filterx;
                    if (window.vm && typeof window.vm.refreshDetailGrids === 'function') {
                        await window.vm.refreshDetailGrids(tabId, configKey);
                    }
                }
                if (window.vm) await window.vm.$nextTick();
                currentTab.dataLoaded[configKey] = true;
                this.updateSummary(tabId, configKey, config);
                return;
            };

            let data = result;

            if (data && data.error) {
                console.error("Server error:", data.error);
                data = [];
            } else if (data && data.success === false) {
                console.error("Server fail:", data.message || data.error);
                data = [];
            } else if (!Array.isArray(data)) {
                data = [];
            }


            if (data.length > 0) {
                recordData = data[0];
                const formattedRecord = {};
                config.specificFields.forEach(field => {
                    formattedRecord[field.id] = recordData[field.id] !== undefined ? recordData[field.id] : (field.type === 'checkbox' ? '0' : '');
                    if (field.type === 'checkbox') {
                        formattedRecord[field.id] = (formattedRecord[field.id] === '1' || formattedRecord[field.id] === 1 || formattedRecord[field.id] === true) ? '1' : '0';
                    }
                });

                formattedRecord.image_paths = recordData.image_paths !== undefined ? recordData.image_paths : '';

                if (recordData.posting !== undefined) {
                    formattedRecord.posting = recordData.posting;
                }

                currentTab.currentRecord[configKey] = formattedRecord;

                if (config.filterx) {
                    currentTab.parentFilterValue = formattedRecord[config.filterx];
                    currentTab.parentFilterKeyField = config.filterx;
                }

                let totalCountSql = `SELECT COUNT(id) as total FROM \`${config.table}\``;
                const xcab = window.xcab || 'yyy';
                const xbrand = window.xbrand || 'zzz';
                let whereClause = [];
                if (xcab !== 'yyy' && config.table !== 'menus' && config.table !== 'menugroups' && config.table !== 'paket' && config.table !== 'config') {
                    const hasBrandCab = !['menus', 'menugroups', 'paket', 'config'].includes(config.table);
                    if (hasBrandCab) {
                        if (xbrand !== 'zzz') whereClause.push(`brand = '${xbrand}'`);
                        whereClause.push(`cab = '${xcab}'`);
                    }
                }
                if (whereClause.length > 0) totalCountSql += ` WHERE ${whereClause.join(' AND ')}`;

                const totalCountResult = await window.ambil_select(totalCountSql);
                currentTab.totalRecords[configKey] = (totalCountResult && totalCountResult[0]) ? parseInt(totalCountResult[0].total) : 0;

                if (currentTab.currentRecord[configKey].id && currentTab.totalRecords[configKey] > 0) {
                    let positionWhere = whereClause.slice();
                    positionWhere.push(`id < ${parseInt(currentTab.currentRecord[configKey].id)}`);
                    const currentPositionSql = `SELECT COUNT(id) as position FROM \`${config.table}\` ${positionWhere.length > 0 ? 'WHERE ' + positionWhere.join(' AND ') : ''}`;
                    const posResult = await window.ambil_select(currentPositionSql);
                    currentTab.currentIndex[configKey] = (posResult && posResult[0]) ? parseInt(posResult[0].position) : 0;
                } else if (currentTab.totalRecords[configKey] > 0 && (!currentTab.currentRecord[configKey].id || ['first', 'last'].includes(loadType))) {
                    currentTab.currentIndex[configKey] = (loadType === 'first' || data.length > 0) ? 0 : (currentTab.totalRecords[configKey] - 1);
                } else {
                    currentTab.currentIndex[configKey] = -1;
                }


                if (config.filterx && currentTab.parentFilterValue !== undefined) {
                    if (window.vm && typeof window.vm.refreshDetailGrids === 'function') {
                        await window.vm.refreshDetailGrids(tabId, configKey);
                    }
                }
            } else {
                currentTab.currentRecord[configKey] = this.initializeNewRecord(config.specificFields);
                currentTab.currentIndex[configKey] = -1;
                currentTab.totalRecords[configKey] = 0;
                if (config.filterx) {
                    currentTab.parentFilterValue = null;
                    currentTab.parentFilterKeyField = config.filterx;
                    if (window.vm && typeof window.vm.refreshDetailGrids === 'function') {
                        await window.vm.refreshDetailGrids(tabId, configKey);
                    }
                }
            }
        } catch (e) {
            console.error('Error loading form data for', config.table, ':', e);
            currentTab.currentRecord[configKey] = this.initializeNewRecord(config.specificFields);
            currentTab.currentIndex[configKey] = -1;
            currentTab.totalRecords[configKey] = 0;
            if (config.filterx) {
                currentTab.parentFilterValue = null;
                currentTab.parentFilterKeyField = config.filterx;
                if (window.vm && typeof window.vm.refreshDetailGrids === 'function') {
                    await window.vm.refreshDetailGrids(tabId, configKey);
                }
            }
        } finally {
            if (window.vm) await window.vm.$nextTick();
            this.updateSummary(tabId, configKey, config);
            currentTab.dataLoaded[configKey] = true;
            if (window.vm) window.vm.$forceUpdate();
            if (window.customHooks && typeof window.customHooks.afterDataLoad === 'function') {
                window.customHooks.afterDataLoad(window.vm, tabId, configKey);
            }
        }
    },

    updateSummary(tabId, configKey, config) {
        const currentTab = window.vm.openTabs.find(tab => tab.id === tabId);
        if (!currentTab || !currentTab.currentRecord[configKey]) return;

        let totalRows = currentTab.totalRecords[configKey];
        let summaryText;

        if (totalRows === 0) {
            summaryText = `Total Baris: 0`;
            currentTab.currentIndex[configKey] = -1;
        } else {
            if (currentTab.currentIndex[configKey] === -1 && totalRows > 0 && currentTab.currentRecord[configKey] && currentTab.currentRecord[configKey].id !== null) {
                currentTab.currentIndex[configKey] = 0;
            }
            summaryText = `Record ${currentTab.currentIndex[configKey] + 1} of ${totalRows}`;
        }

        if (config) {
            const jumlahSksField = config.specificFields.find(f => f.id === 'jumlah_sks' && f.type === 'formula');
            if (jumlahSksField && currentTab.currentRecord[configKey]) {
                let totalSKS = this.calculateFormula(config, currentTab.currentRecord[configKey], jumlahSksField, false);
                summaryText += `, SKS Record Ini: ${totalSKS.toLocaleString('en-US')}`;
            }
        }

        const summaryElement = document.getElementById(`summary-${tabId}-${configKey}`);
        if (summaryElement) {
            summaryElement.innerText = summaryText;
        }
    },

    async newRecord(tabId, configKey) {
        const currentTab = window.vm.openTabs.find(tab => tab.id === tabId);
        if (!currentTab) return;

        if (currentTab.functionCache) {
            currentTab.functionCache = {};
        }

        const config = currentTab.dbConfigs[configKey];
        if (config) {
            currentTab.formMode[configKey] = 'new';
            currentTab.currentRecord[configKey] = this.initializeNewRecord(config.specificFields);
            currentTab.autocompleteResults = {};
            currentTab.autocompleteSelectedIndex = {};
            currentTab.showAutocomplete = {};
            currentTab.selectedFiles = {};
            currentTab.currentIndex[configKey] = -1;
            
            for (const field of config.specificFields) {
                if (field.id == "created_by") {
                    currentTab.currentRecord[configKey][field.id] = login.nama;
                    currentTab.currentRecord[configKey]['id_cabang'] = login.cab;
                };
                if (field.type === 'noseri' && field.format) {
                    try {
                        let formatString = field.format;
                        let searchPrefixParts = [];
                        let numericLength = 0;
                        let i = 0;

                        while (i < formatString.length) {
                            if (formatString.substring(i, i + 3).toUpperCase() === "CCC") {
                                searchPrefixParts.push(window.xcab || "000");
                                i += 3;
                            } else if (formatString.substring(i, i + 2).toUpperCase() === "YY") {
                                searchPrefixParts.push(new Date().getFullYear().toString().slice(-2));
                                i += 2;
                            } else if (formatString[i] === '9') {
                                while (i < formatString.length && formatString[i] === '9') {
                                    numericLength++;
                                    i++;
                                }
                                break;
                            } else {
                                searchPrefixParts.push(formatString[i]);
                                i++;
                            }
                        }
                        const searchPrefix = searchPrefixParts.join('');

                        if (numericLength === 0) {
                            console.error(`Format noseri untuk field '${field.id}' tidak memiliki bagian numerik (9): ${field.format}`);
                            currentTab.currentRecord[configKey][field.id] = "FORMAT_ERROR";
                            continue;
                        }

                        const serialResponse = await window.dapatkanNomorSeriBerikutnya(
                            config.table,
                            field.id,
                            searchPrefix,
                            numericLength
                        );

                        if (serialResponse && serialResponse.success && serialResponse.new_serial_number) {
                            currentTab.currentRecord[configKey][field.id] = serialResponse.new_serial_number;
                        } else {
                            console.error("Gagal generate noseri:", serialResponse.error || "Unknown error from backend");
                            currentTab.currentRecord[configKey][field.id] = "NOSERI_ERROR";
                        }
                    } catch (e) {
                        console.error(`Error saat memproses noseri untuk field ${field.id}:`, e);
                        currentTab.currentRecord[configKey][field.id] = "NOSERI_EXCEPTION";
                    }
                }
            }

            if (config.filterx) {
                currentTab.parentFilterValue = null;
                currentTab.parentFilterKeyField = config.filterx;
                if (window.vm && typeof window.vm.refreshDetailGrids === 'function') {
                    await window.vm.refreshDetailGrids(tabId, configKey);
                }
            }
            if (window.vm) {
                await window.vm.$nextTick();
                window.vm.$forceUpdate();
            }
            this.updateSummary(tabId, configKey, config);
            this.setFocusToFirstField(tabId, configKey);
        }
    },

    async editRecord(tabId, configKey) {
        const currentTab = window.vm.openTabs.find(tab => tab.id === tabId);
        if (!currentTab) return;

        if (currentTab.currentRecord[configKey] && currentTab.currentRecord[configKey].id !== null && currentTab.currentIndex[configKey] !== -1) {
            currentTab.formMode[configKey] = 'edit';
            currentTab.autocompleteResults = {};
            currentTab.autocompleteSelectedIndex = {};
            currentTab.showAutocomplete = {};
            currentTab.selectedFiles = {};

            await window.vm.refreshDetailGrids(tabId, configKey);

            await window.vm.$nextTick();
            this.setFocusToFirstField(tabId, configKey);

        } else {
            alert('Pilih record yang akan diedit terlebih dahulu.');
        }
    },

    async deleteRecord(tabId, configKey) {
        const currentTab = window.vm.openTabs.find(tab => tab.id === tabId);
        if (!currentTab) return;

        const config = currentTab.dbConfigs[configKey];
        if (!config) return;

        const idField = config.specificFields.find(f => f.autoIncrement || f.id === 'id');
        if (!idField) {
            alert('Error: Field ID tidak ditemukan dalam konfigurasi.');
            return;
        }

        const recordId = currentTab.currentRecord[configKey] ? currentTab.currentRecord[configKey][idField.id] : null;
        if (!recordId) {
            alert('Pilih record yang akan dihapus terlebih dahulu.');
            return;
        }

        if (confirm(`Anda yakin ingin menghapus record ini (ID: ${recordId}) beserta semua data detail terkait?`)) {
            try {
                if (config.filterx) {
                    console.log("Memulai proses Cascade Delete untuk detail...");
                    const masterFilterField = config.filterx;
                    const masterFilterValue = currentTab.currentRecord[configKey][masterFilterField];

                    const deletePromises = [];
                    for (const detailKey in currentTab.dbConfigs) {
                        if (detailKey === configKey) continue;
                        const detailConfig = currentTab.dbConfigs[detailKey];
                        if (detailConfig.model === 'grid' && detailConfig.filter === masterFilterField) {
                            console.log(`Menyiapkan penghapusan untuk tabel detail '${detailConfig.table}' dimana '${detailConfig.filter}' = '${masterFilterValue}'`);
                            deletePromises.push(window.hapus(detailConfig.table, detailConfig.filter, masterFilterValue));
                        }
                    }
                    if (deletePromises.length > 0) {
                        const results = await Promise.all(deletePromises);
                        const failedDeletes = results.filter(res => !res.success);
                        if (failedDeletes.length > 0) {
                            throw new Error('Gagal menghapus beberapa data detail. Proses dibatalkan. Detail: ' + JSON.stringify(failedDeletes));
                        }
                        console.log("Semua data detail berhasil dihapus.");
                    }
                }

                console.log(`Menghapus master record dari tabel '${config.table}'...`);
                const masterDeleteResult = await window.hapus(config.table, idField.id, recordId);

                if (masterDeleteResult.success) {
                    alert('Record master dan semua detail terkait berhasil dihapus!');
                    const totalRecordsAfterDelete = currentTab.totalRecords[configKey] - 1;
                    if (totalRecordsAfterDelete <= 0) {
                        await this.loadFormData(tabId, configKey, config, null);
                    } else {
                        await this.loadFormData(tabId, configKey, config, 'first');
                    }
                } else {
                    throw new Error('Gagal menghapus data master: ' + (masterDeleteResult.message || masterDeleteResult.error || 'Unknown error'));
                }
            } catch (error) {
                alert('Operasi hapus gagal: ' + error.message);
                console.error('Delete failed:', error);
            }
        }
    },

    async saveRecord(tabId, configKey, config) {
        if (window.customHooks && typeof window.customHooks.beforeSave === 'function') {
            const proceed = window.customHooks.beforeSave(window.vm, tabId, configKey);
            if (!proceed) {
                return;
            }
        }

        const currentTab = window.vm.openTabs.find(tab => tab.id === tabId);
        if (!currentTab || !config) return;

        for (const field of config.specificFields) {
            if (field.required && !field.readonly && !field.hidden && field.type !== 'html' && field.type !== 'fungsi' && field.type !== 'noseri') {
                const value = currentTab.currentRecord[configKey][field.id];
                if (value === null || value === undefined || String(value).trim() === '') {
                    alert(`Field "${field.label}" (${field.id}) harus diisi.`);
                    const inputElement = document.getElementById(`${tabId}-${configKey}-${field.id}`);
                    if (inputElement) inputElement.focus();
                    return;
                }
            }
        }

        const recordToSave = JSON.parse(JSON.stringify(currentTab.currentRecord[configKey]));

        config.specificFields.forEach(field => {
            if (field.type === 'formula' && !field.saveValue) { delete recordToSave[field.id]; } 
            else if (field.type === 'formula' && field.saveValue === true) { recordToSave[field.id] = this.calculateFormula(config, recordToSave, field, false); }
            if (field.type === 'html' || field.type === 'fungsi') { delete recordToSave[field.id]; }
            if (field.type === 'checkbox') { recordToSave[field.id] = recordToSave[field.id] ? '1' : '0'; }
        });
        if (currentTab.currentRecord[configKey].hasOwnProperty('image_paths')) {
            recordToSave.image_paths = currentTab.currentRecord[configKey].image_paths;
        }

        const idField = config.specificFields.find(f => f.autoIncrement || f.id === 'id') || { id: 'id' };
        const recordId = recordToSave[idField.id];

        try {
            let result;
            let newRecordId = recordId;

            if (currentTab.formMode[configKey] === 'new') {
                if (idField.autoIncrement && recordToSave.hasOwnProperty(idField.id) && (recordToSave[idField.id] === null || recordToSave[idField.id] === '')) {
                    delete recordToSave[idField.id];
                }
                result = await window.tambahjson(config.table, recordToSave);
                if (result.success && result.id > 0) {
                    newRecordId = result.id;
                    if (!recordToSave[idField.id]) {
                        recordToSave[idField.id] = newRecordId;
                    }

                    if (currentTab.currentRecord[configKey] && idField.autoIncrement && (currentTab.currentRecord[configKey][idField.id] === null || currentTab.currentRecord[configKey][idField.id] === '')) {
                        currentTab.currentRecord[configKey][idField.id] = newRecordId;
                    }
                    alert('Record berhasil ditambahkan!');

                    // ======================================================================
                    // ||          AWAL BLOK LOGIKA PENYIMPANAN RECORD ANAK (DIPERBAIKI)   ||
                    // ======================================================================
                    console.log(`Induk disimpan. Memulai penyimpanan data anak.`);
                    
                    const parentFilterField = config.filterx; 
                    if (parentFilterField) {
                        const parentFilterValue = recordToSave[parentFilterField];
                        
                        if(parentFilterValue !== undefined && parentFilterValue !== null){
                            for (const detailKey in currentTab.dbConfigs) {
                                if (detailKey === configKey) continue;
    
                                const detailConfig = currentTab.dbConfigs[detailKey];
                                if (detailConfig.model === 'grid' && detailConfig.filter) {
                                    const childForeignKeyField = detailConfig.filter;
                                    const hotInstance = currentTab.gridInstances[detailKey];
    
                                    if (hotInstance) {
                                        const childSavePromises = [];
                                        const allGridData = hotInstance.getSourceData();
    
                                        for (const childRecord of allGridData) {
                                            const idFieldChild = detailConfig.specificFields.find(f => f.autoIncrement || f.id === 'id');
                                            const childId = idFieldChild ? childRecord[idFieldChild.id] : null;
                                            let hasData = Object.values(childRecord).some(val => val !== null && val !== '' && val !== 0);
    
                                            if (!childId && hasData) {
                                                const childRecordToSave = { ...childRecord };
                                                
                                                childRecordToSave[childForeignKeyField] = parentFilterValue;
    
                                                if (idFieldChild && childRecordToSave.hasOwnProperty(idFieldChild.id)) {
                                                    delete childRecordToSave[idFieldChild.id];
                                                }
    
                                                console.log(`Menyimpan data anak untuk tabel '${detailConfig.table}':`, childRecordToSave);
                                                childSavePromises.push(window.tambahjson(detailConfig.table, childRecordToSave));
                                            }
                                        }
    
                                        if (childSavePromises.length > 0) {
                                            const childResults = await Promise.all(childSavePromises);
                                            const failedSaves = childResults.filter(res => !res.success);
                                            if (failedSaves.length > 0) {
                                                alert(`Peringatan: Record induk berhasil disimpan, tetapi ${failedSaves.length} baris detail gagal disimpan.`);
                                                console.error('Gagal menyimpan data anak:', failedSaves);
                                            } else {
                                                console.log(`${childSavePromises.length} data anak berhasil disimpan.`);
                                            }
                                        }
                                    }
                                }
                            }
                        } else {
                             console.error(`Nilai untuk kunci filter '${parentFilterField}' tidak ditemukan pada record induk yang disimpan.`);
                        }
                    }
                    // ======================================================================
                    // ||           AKHIR BLOK LOGIKA PENYIMPANAN RECORD ANAK                ||
                    // ======================================================================

                } else {
                    throw new Error('Gagal menambah data: ' + (result.message || result.error || 'Unknown error'));
                }

            } else if (currentTab.formMode[configKey] === 'edit') {
                result = await window.rubahjson(config.table, recordToSave, recordId, '');
                if (result.success) {
                    alert('Record berhasil diperbarui!');
                } else {
                    throw new Error('Gagal memperbarui data: ' + (result.message || result.error || 'Unknown error'));
                }
            }

            if (result.success && config.filterx) {
                console.log("Memulai proses Cascade Update untuk detail...");
                const masterRecordForUpdate = await this.fetchSingleRecord(config.table, idField.id, newRecordId);
                const masterFilterField = config.filterx;
                const masterFilterValue = masterRecordForUpdate[masterFilterField];

                for (const detailKey in currentTab.dbConfigs) {
                    if (detailKey === configKey) continue;

                    const detailConfig = currentTab.dbConfigs[detailKey];
                    if (detailConfig.model === 'grid' && detailConfig.filter === masterFilterField && Array.isArray(detailConfig.inheritFields) && detailConfig.inheritFields.length > 0) {
                        
                        const dataToUpdate = {};
                        detailConfig.inheritFields.forEach(fieldMap => {
                            dataToUpdate[fieldMap.to] = masterRecordForUpdate[fieldMap.from];
                        });

                        if (Object.keys(dataToUpdate).length > 0) {
                             console.log(`Mengupdate tabel detail '${detailConfig.table}' dengan data:`, dataToUpdate, `dimana '${detailConfig.filter}' = '${masterFilterValue}'`);
                            try {
                                const updateMassalResult = await window.updateMassal(detailConfig.table, dataToUpdate, detailConfig.filter, masterFilterValue);
                                if (updateMassalResult.success) {
                                    console.log(`Tabel '${detailConfig.table}' berhasil diupdate.`);
                                    await window.vm.loadGridData(tabId, detailKey, detailConfig, masterFilterValue);
                                } else {
                                    console.error(`Gagal melakukan update massal pada tabel '${detailConfig.table}':`, updateMassalResult.error);
                                }
                            } catch (e) {
                                 console.error(`Error saat memanggil updateMassal untuk tabel '${detailConfig.table}':`, e);
                            }
                        }
                    }
                }
            }

            currentTab.formMode[configKey] = 'view';
            currentTab.selectedFiles = {};
            delete currentTab.formModeBeforeCancel;
            await this.loadFormData(tabId, configKey, config, 'specificId', newRecordId);

        } catch (error) {
            alert('Operasi simpan gagal: ' + error.message);
            console.error('Save failed:', error);
        }
    },

    async cancelForm(tabId, configKey, config) {
        const currentTab = window.vm.openTabs.find(tab => tab.id === tabId);
        if (!currentTab) return;

        currentTab.formMode[configKey] = 'view';
        const currentIdInForm = (currentTab.currentRecord[configKey] && currentTab.currentRecord[configKey].id) ?
            currentTab.currentRecord[configKey].id :
            null;

        if (currentTab.formModeBeforeCancel === 'new' && !currentIdInForm) {
            if (currentTab.totalRecords[configKey] > 0) {
                await this.loadFormData(tabId, configKey, config, 'first');
            } else {
                await this.newRecord(tabId, configKey);
                currentTab.formMode[configKey] = 'view';
            }
        } else if (currentIdInForm) {
            await this.loadFormData(tabId, configKey, config, 'specificId', currentIdInForm);
        } else {
            await this.loadFormData(tabId, configKey, config, 'first');
        }
        currentTab.autocompleteResults = {};
        currentTab.autocompleteSelectedIndex = {};
        currentTab.showAutocomplete = {};
        currentTab.selectedFiles = {};
        delete currentTab.formModeBeforeCancel;
    },

    async navigateRecord(tabId, configKey, direction) {
        const currentTab = window.vm.openTabs.find(tab => tab.id === tabId);
        if (!currentTab) return;

        const config = currentTab.dbConfigs[configKey];
        if (!config) return;

        currentTab.formMode[configKey] = 'view';
        await this.loadFormData(tabId, configKey, config, direction);
    },

    debounceSearchAutocompleteForm(tabId, field, query, formConfigKey) {
        const currentTab = window.vm.openTabs.find(tab => tab.id === tabId);
        if (!currentTab) return;

        if (currentTab.debounceTimeout) {
            clearTimeout(currentTab.debounceTimeout);
        }
        currentTab.debounceTimeout = setTimeout(() => {
            this.searchAutocompleteForm(tabId, field, query, formConfigKey);
        }, 300);
    },

    async searchAutocompleteForm(tabId, field, query, formConfigKey) {
        const currentTab = window.vm.openTabs.find(tab => tab.id === tabId);
        if (!currentTab) return;

        if (currentTab.autocompleteResults && !currentTab.autocompleteResults.hasOwnProperty(field.id)) {
            currentTab.autocompleteResults[field.id] = [];
        }
        if (currentTab.autocompleteSelectedIndex && !currentTab.autocompleteSelectedIndex.hasOwnProperty(field.id)) {
            currentTab.autocompleteSelectedIndex[field.id] = -1;
        }
        if (currentTab.showAutocomplete && !currentTab.showAutocomplete.hasOwnProperty(field.id)) {
            currentTab.showAutocomplete[field.id] = false;
        }


        if (!query || query.length < 1) {
            if (currentTab.autocompleteResults && currentTab.autocompleteResults[field.id]) {
                currentTab.autocompleteResults[field.id] = [];
            }
            if (currentTab.autocompleteSelectedIndex && currentTab.autocompleteSelectedIndex[field.id] !== undefined) {
                currentTab.autocompleteSelectedIndex[field.id] = -1;
            }
            if (currentTab.showAutocomplete && currentTab.showAutocomplete[field.id] !== undefined) {
                currentTab.showAutocomplete[field.id] = false;
            }
            return;
        }

        const link = field.link[0];
        try {
            window.searchAutocomplete(query, (results) => {
                if (currentTab.autocompleteResults && currentTab.autocompleteResults[field.id] !== undefined) {
                    currentTab.autocompleteResults[field.id] = results.length > 0 ? results : ['No Results'];
                }
                if (currentTab.autocompleteSelectedIndex && currentTab.autocompleteSelectedIndex[field.id] !== undefined) {
                    currentTab.autocompleteSelectedIndex[field.id] = -1;
                }
                if (currentTab.showAutocomplete && currentTab.showAutocomplete[field.id] !== undefined) {
                    currentTab.showAutocomplete[field.id] = true;
                }
            }, link.table, link.field1, link.field2);
        } catch (e) {
            console.error('Autocomplete AJAX error:', e);
            if (currentTab.autocompleteResults && currentTab.autocompleteResults[field.id] !== undefined) {
                currentTab.autocompleteResults[field.id] = ['AJAX Error'];
            }
            if (currentTab.autocompleteSelectedIndex && currentTab.autocompleteSelectedIndex[field.id] !== undefined) {
                currentTab.autocompleteSelectedIndex[field.id] = -1;
            }
            if (currentTab.showAutocomplete && currentTab.showAutocomplete[field.id] !== undefined) {
                currentTab.showAutocomplete[field.id] = true;
            }
        }
    },

    selectAutocompleteResult(tabId, field, result, formConfigKey) {
        const currentTab = window.vm.openTabs.find(tab => tab.id === tabId);
        if (!currentTab || !currentTab.currentRecord[formConfigKey]) return;

        if (result === 'No Results' || result === 'AJAX Error') {
            this.hideAutocompleteList(tabId, field.id);
            return;
        }

        const parts = String(result).split(' _ ');
        const idValue = parts[0].trim();
        const nameValue = parts.length > 1 ? parts[1].trim() : '';

        currentTab.currentRecord[formConfigKey][field.id] = idValue;

        if (field.link && field.link.length > 0 && field.link[0].field3) {
            const linkedFieldName = field.link[0].field3;
            if (currentTab.currentRecord[formConfigKey].hasOwnProperty(linkedFieldName)) {
                currentTab.currentRecord[formConfigKey][linkedFieldName] = nameValue;
            } else {
                console.warn(`Autocomplete linked field "${linkedFieldName}" does not exist in record for config "${formConfigKey}".`);
            }
        }
        this.hideAutocompleteList(tabId, field.id);
    },

    selectAutocompleteResultOnEnter(tabId, fieldId, formConfigKey) {
        const currentTab = window.vm.openTabs.find(tab => tab.id === tabId);
        if (!currentTab || !currentTab.dbConfigs[formConfigKey]) return;

        const field = currentTab.dbConfigs[formConfigKey].specificFields.find(f => f.id === fieldId);
        if (!field) return;

        if (currentTab.showAutocomplete[fieldId] && currentTab.autocompleteSelectedIndex[fieldId] !== -1 &&
            currentTab.autocompleteResults[fieldId] &&
            currentTab.autocompleteResults[fieldId].length > currentTab.autocompleteSelectedIndex[fieldId]) {
            const selectedResult = currentTab.autocompleteResults[fieldId][currentTab.autocompleteSelectedIndex[fieldId]];
            if (selectedResult !== 'No Results' && selectedResult !== 'AJAX Error') {
                this.selectAutocompleteResult(tabId, field, selectedResult, formConfigKey);
            } else {
                this.hideAutocompleteList(tabId, fieldId);
            }
        } else {
            this.hideAutocompleteList(tabId, fieldId);
        }
        this.focusNextField(tabId, formConfigKey, fieldId, null);
    },

    moveAutocompleteSelection(tabId, fieldId, direction, formConfigKey) {
        const currentTab = window.vm.openTabs.find(tab => tab.id === tabId);
        if (!currentTab || !currentTab.autocompleteResults[fieldId]) return;

        const results = currentTab.autocompleteResults[fieldId];
        if (!results || results.length === 0 || (results.length === 1 && (results[0] === 'No Results' || results[0] === 'AJAX Error'))) return;

        let newIndex = (currentTab.autocompleteSelectedIndex[fieldId] === undefined ? -1 : currentTab.autocompleteSelectedIndex[fieldId]) + direction;

        if (newIndex < 0) {
            newIndex = results.length - 1;
        } else if (newIndex >= results.length) {
            newIndex = 0;
        }
        currentTab.autocompleteSelectedIndex[fieldId] = newIndex;
    },

    showAutocompleteList(tabId, fieldId) {
        const currentTab = window.vm.openTabs.find(tab => tab.id === tabId);
        if (!currentTab) return;

        if (currentTab.autocompleteResults && !currentTab.autocompleteResults.hasOwnProperty(fieldId)) {
            currentTab.autocompleteResults[field.id] = [];
        }
        if (currentTab.autocompleteSelectedIndex && !currentTab.autocompleteSelectedIndex.hasOwnProperty(fieldId)) {
            currentTab.autocompleteSelectedIndex[fieldId] = -1;
        }
        if (currentTab.showAutocomplete && !currentTab.showAutocomplete.hasOwnProperty(fieldId)) {
            currentTab.showAutocomplete[fieldId] = false;
        }

        const formConfigKey = currentTab.mainConfigKey;
        const query = currentTab.currentRecord[formConfigKey] ? currentTab.currentRecord[formConfigKey][fieldId] : '';

        if (query && query.length > 0 && currentTab.autocompleteResults[fieldId] && currentTab.autocompleteResults[fieldId].length > 0) {
            if (currentTab.showAutocomplete) currentTab.showAutocomplete[fieldId] = true;
        } else if (!query || query.length === 0) {
            if (currentTab.showAutocomplete) currentTab.showAutocomplete[fieldId] = false;
        }
    },

    hideAutocompleteList(tabId, fieldId) {
        const currentTab = window.vm.openTabs.find(tab => tab.id === tabId);
        if (!currentTab) return;

        setTimeout(() => {
            if (currentTab.showAutocomplete && currentTab.showAutocomplete.hasOwnProperty(fieldId)) {
                currentTab.showAutocomplete[fieldId] = false;
            }
        }, 200);
    },

    handleFileChange(event, tabId, configKey, fieldId) {
        const currentTab = window.vm.openTabs.find(tab => tab.id === tabId);
        if (!currentTab) return;

        const file = event.target.files[0];
        if (file) {
            if (!currentTab.selectedFiles) {
                currentTab.selectedFiles = {};
            }
            currentTab.selectedFiles[fieldId] = file;
            console.log(`File selected for ${fieldId} in tab ${tabId} (${configKey}):`, file.name);

            const fieldConfig = currentTab.dbConfigs[configKey].specificFields.find(f => f.id === fieldId);
            if (fieldConfig && fieldConfig.fileType === 'image') {
                const reader = new FileReader();
                reader.onload = (e) => {
                    console.log("File preview ready for", fieldId);
                }
                reader.readAsDataURL(file);
            }
        } else {
            if (currentTab.selectedFiles && currentTab.selectedFiles[fieldId]) {
                delete currentTab.selectedFiles[fieldId];
            }
        }
    },

    async uploadFile(tableName, recordId, fieldName, file) {
        return new Promise((resolve, reject) => {
            const formData = new FormData();
            formData.append('action', 'upload');
            formData.append('file', file);
            formData.append('tabel', tableName);
            formData.append('idField', 'id');
            formData.append('idValue', recordId);
            formData.append('field', fieldName);
            formData.append('xbrand', window.xbrand || '001');
            formData.append('xcab', window.xcab || '002');

            $.ajax({
                url: 'crud.php',
                type: 'POST',
                data: formData,
                processData: false,
                contentType: false,
                dataType: 'json',
                success: function(response) {
                    if (response.success) {
                        resolve(response);
                    } else {
                        reject(new Error(response.message || 'Unknown error from server upload'));
                    }
                },
                error: function(xhr, status, error) {
                    console.error("Upload AJAX error:", status, error, xhr.responseText);
                    reject(new Error('AJAX error during file upload: ' + error + ' - ' + xhr.responseText));
                }
            });
        });
    },

    async fetchSingleRecord(tableName, idField, idValue) {
        try {
            const result = await window.ambil(tableName, idField, idValue, idValue);
            const data = result;
            if (data && data.error) {
                throw new Error(data.error);
            }
            return data && data.length > 0 ? data[0] : null;
        } catch (e) {
            console.error('Error fetching single record:', e);
            return null;
        }
    },

    calculateFormula(config, record, field, forDisplay = true) {
        if (field.type !== 'formula' || !field.rumus || !record) {
            return forDisplay ? 'N/A' : 0;
        }

        const formula = String(field.rumus);
        let result = 0;
        let processedFormula = formula;

        try {
            const fieldIdsInFormula = formula.match(/[a-zA-Z_][a-zA-Z0-9_]*/g) || [];
            
            for (const fieldId of fieldIdsInFormula) {
                const operandFieldConfig = config.specificFields.find(f => f.id === fieldId);
                if (operandFieldConfig) {
                    let valueForEval;
                    if (record.hasOwnProperty(fieldId) && record[fieldId] !== null && record[fieldId] !== undefined && String(record[fieldId]).trim() !== '') {
                        const parsedValue = parseFloat(String(record[fieldId]).replace(',', '.'));
                        if (!isNaN(parsedValue)) {
                            valueForEval = parsedValue;
                        } else {
                            valueForEval = 0;
                        }
                    } else {
                        valueForEval = 0;
                    }
                    processedFormula = processedFormula.replace(new RegExp('\\b' + fieldId + '\\b', 'g'), valueForEval.toString());
                }
            }
            const sanitizedFormula = processedFormula
                .replace(/[^-()\d/*+.\s]/g, '')
                .replace(/\s+/g, '')
                .trim();

            if (sanitizedFormula === '' || /^[*/+-.]$/.test(sanitizedFormula) || /[*/+-.]{2,}/.test(sanitizedFormula) || !/[\d]/.test(sanitizedFormula)) {
                result = 0;
            } else {
                result = eval(sanitizedFormula);
            }
            if (isNaN(result) || !isFinite(result)) {
                result = 0;
            }
        } catch (e) {
            console.error(`Formula error untuk field ${field.id} ('${formula}'). Diproses menjadi: '${processedFormula}'. Error:`, e, "Record Data:", JSON.parse(JSON.stringify(record)));
            result = 0;
        }
        return forDisplay ? result.toLocaleString('id-ID', {
            minimumFractionDigits: 0,
            maximumFractionDigits: 2
        }) : result;
    },


    exportToCsv(tabId, configKey) {
        const currentTab = window.vm.openTabs.find(tab => tab.id === tabId);
        if (!currentTab) return;

        const config = currentTab.dbConfigs[configKey];
        const record = currentTab.currentRecord[configKey];

        if (!record || Object.keys(record).length === 0) {
            alert('Tidak ada data untuk diexport.');
            return;
        }
        if (!config) return;

        const headers = config.specificFields
            .filter(field => !field.hidden && field.type !== 'html' && field.type !== 'fungsi')
            .map(field => field.label);

        const rowDataValues = config.specificFields
            .filter(field => !field.hidden && field.type !== 'html' && field.type !== 'fungsi')
            .map(field => {
                let value = record[field.id];
                if (field.type === 'checkbox') {
                    return value === '1' ? 'Ya' : 'Tidak';
                } else if (field.type === 'formula') {
                    return this.calculateFormula(config, record, field, false);
                } else if (field.type === 'password') {
                    return '********';
                }
                return `"${String(value === null || value === undefined ? '' : value).replace(/"/g, '""')}"`;
            });

        const csvContent = "data:text/csv;charset=utf-8," +
            headers.join(';') + "\r\n" +
            rowDataValues.join(';');

        const encodedUri = encodeURI(csvContent);
        const link = document.createElement("a");
        link.setAttribute("href", encodedUri);
        link.setAttribute("download", `${currentTab.menuId}_${configKey}_form_data_${new Date().toISOString().slice(0, 10)}.csv`);
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        alert('Data form berhasil diexport ke CSV!');
    },

    setFocusToFirstField(tabId, configKey) {
        const currentTab = window.vm.openTabs.find(tab => tab.id === tabId);
        if (!currentTab || !currentTab.dbConfigs[configKey]) return;

        const config = currentTab.dbConfigs[configKey];
        const focusableFields = config.specificFields.filter(f => !f.hidden && !f.readonly && f.type !== 'formula' && f.type !== 'html' && f.type !== 'fungsi' && f.type !== 'noseri');

        if (focusableFields.length > 0) {
            const firstFieldId = focusableFields[0].id;
            setTimeout(() => {
                const element = document.getElementById(`${tabId}-${configKey}-${firstFieldId}`);
                if (element) {
                    element.focus();
                    if (typeof element.select === 'function' && (element.type === 'text' || element.type === 'number' || element.type === 'password' || element.type === 'date' || element.tagName === 'TEXTAREA')) {
                        element.select();
                    }
                }
            }, 100);
        }
    },

    focusNextField(tabId, configKey, currentFieldId, event) {
        if (event) event.preventDefault();

        const currentTab = window.vm.openTabs.find(tab => tab.id === tabId);
        if (!currentTab || !currentTab.dbConfigs[configKey]) return;

        if (currentTab.showAutocomplete && currentTab.showAutocomplete[currentFieldId] && event && event.key === "Enter") {
            return;
        }

        const config = currentTab.dbConfigs[configKey];
        const fields = config.specificFields.filter(f => !f.hidden && !f.readonly && f.type !== 'formula' && f.type !== 'html' && f.type !== 'fungsi' && f.type !== 'noseri');

        const currentFieldIndex = fields.findIndex(f => f.id === currentFieldId);

        if (currentFieldIndex > -1 && currentFieldIndex < fields.length - 1) {
            const nextField = fields[currentFieldIndex + 1];
            const nextInputElement = document.getElementById(`${tabId}-${configKey}-${nextField.id}`);
            if (nextInputElement) {
                nextInputElement.focus();
                if (typeof nextInputElement.select === 'function' && (nextInputElement.type === 'text' || nextInputElement.type === 'number' || nextInputElement.type === 'password' || nextInputElement.type === 'date' || nextInputElement.tagName === 'TEXTAREA')) {
                    nextInputElement.select();
                }
            }
        } else {
            const saveButton = document.querySelector(`.tab-content.active .form-actions-inline button.btn-success[type="button"], .tab-content.active .form-actions button.btn-success[type="submit"]`);
            if (saveButton) {
                saveButton.focus();
            } else {
                const genericSaveButton = document.querySelector(`#app .tab-content.active .form-actions button[type="submit"], #app .tab-content.active .form-actions button.btn-success`);
                if (genericSaveButton) {
                    genericSaveButton.focus();
                }
            }
        }
    }
};

window.createInteractiveGrid = function(containerSelector, formConfigObject, onRowClickCallback) {
    const $container = $(containerSelector);
    $container.empty().show();

    const config = formConfigObject;

    if (!config || !config.table || !config.specificFields) {
        console.error("createInteractiveGrid: Invalid formConfigObject provided:", config);
        $container.html('<div class="alert alert-danger">Error: Konfigurasi grid pencarian tidak valid.</div>');
        return;
    }

    const tableName = config.table;
    const ACTUAL_DB_PRIMARY_KEY_NAME = 'id';

    let currentPage = 1;
    const rowsPerPage = 10;
    let totalRows = 0;
    let totalPages = 1;
    let currentSearchField = '';
    let currentSearchKeyword = '';
    const loadingSpinnerId = `loadingSpinnerGridModal_${tableName}_${Date.now()}`;

    let displayFieldsConfig = config.specificFields.filter(f =>
        !f.hidden &&
        f.type !== 'password' &&
        f.type !== 'file' &&
        f.type !== 'html' &&
        f.type !== 'fungsi'
    );
    if (displayFieldsConfig.length === 0) {
        displayFieldsConfig = [{ id: ACTUAL_DB_PRIMARY_KEY_NAME, label: 'ID', type: 'text' }];
    }

    let queryFieldsToSelectArray = displayFieldsConfig.map(f => `\`${f.id}\``);
    if (!queryFieldsToSelectArray.includes(`\`${ACTUAL_DB_PRIMARY_KEY_NAME}\``)) {
        queryFieldsToSelectArray.push(`\`${ACTUAL_DB_PRIMARY_KEY_NAME}\``);
    }
    let queryFieldsToSelect = queryFieldsToSelectArray.join(', ');
    if (!queryFieldsToSelect) {
        queryFieldsToSelect = `\`${ACTUAL_DB_PRIMARY_KEY_NAME}\``;
    }

    let gridHtml = `
        <div class="grid-viewer-wrapper">
            <h4><i class="${config.icon || 'fas fa-table'}"></i> Data ${config.nama || 'Pencarian'}</h4>
            <div id="${loadingSpinnerId}" class="spinner-border text-primary" role="status" style="display:none;">
                <span class="sr-only">Loading...</span>
            </div>
            <div class="search-controls form-inline">
                <select id="gridSearchField_${tableName}" class="form-control custom-select mr-sm-2">
                    <option value="">-- Pilih Field --</option>`;
    displayFieldsConfig.forEach(field => {
        gridHtml += `<option value="${field.id}">${field.label}</option>`;
    });
    gridHtml += `
                </select>
                <input type="text" id="gridSearchKeyword_${tableName}" class="form-control mr-sm-2" placeholder="Kata kunci...">
                <button id="btnGridSearch_${tableName}" class="btn btn-primary"><i class="fas fa-search"></i> Cari</button>
                <button id="btnGridResetSearch_${tableName}" class="btn btn-secondary ml-2"><i class="fas fa-undo"></i> Reset</button>
            </div>
            <table class="table table-striped table-bordered table-hover">
                <thead class="thead-dark"><tr>`;
    displayFieldsConfig.forEach(field => {
        gridHtml += `<th>${field.label}</th>`;
    });
    gridHtml += `</tr></thead><tbody id="gridTableBody_${tableName}"></tbody></table>
            </div>
            <div class="pagination-controls">
                <div>
                    <button class="btn btn-sm btn-outline-secondary" id="btnGridFirst_${tableName}"><i class="fas fa-angle-double-left"></i> First</button>
                    <button class="btn btn-sm btn-outline-secondary" id="btnGridPrev_${tableName}"><i class="fas fa-angle-left"></i> Prev</button>
                </div>
                <div class="form-inline">
                    Page <input type="number" class="form-control form-control-sm page-input" id="gridGotoPageInput_${tableName}" value="1" min="1"> of <span id="gridTotalPages_${tableName}">1</span>
                    <button class="btn btn-sm btn-outline-primary ml-1" id="btnGridGotoPage_${tableName}">Go</button>
                </div>
                <div>
                    <button class="btn btn-sm btn-outline-secondary" id="btnGridNext_${tableName}">Next <i class="fas fa-angle-right"></i></button>
                    <button class="btn btn-sm btn-outline-secondary" id="btnGridLast_${tableName}">Last <i class="fas fa-angle-double-right"></i></button>
                </div>
            </div>`;
    $container.html(gridHtml);

    const $tableBody = $container.find(`#gridTableBody_${tableName}`);
    const $searchFieldSelect = $container.find(`#gridSearchField_${tableName}`);
    const $searchKeywordInput = $container.find(`#gridSearchKeyword_${tableName}`);
    const $totalPagesSpan = $container.find(`#gridTotalPages_${tableName}`);
    const $gotoPageInput = $container.find(`#gridGotoPageInput_${tableName}`);
    const $loadingSpinner = $container.find(`#${loadingSpinnerId}`);


    function showLoading() { $loadingSpinner.show(); }
    function hideLoading() { $loadingSpinner.hide(); }

    async function loadGridData(page = 1) {
        showLoading();
        currentPage = parseInt(page) || 1;
        let whereClause = "";
        let params = [];

        if (currentSearchField && currentSearchKeyword) {
            let conditions = [`\`${currentSearchField}\` LIKE ?`];
            params.push(`%${currentSearchKeyword}%`);
            if (window.xbrand && tableName !== 'menus' && tableName !== 'paket' && tableName !== 'menugroups' && config.specificFields.find(f => f.id === 'brand')) {
                conditions.push("`brand` = ?"); params.push(window.xbrand);
            }
            if (window.xcab && tableName !== 'menus' && tableName !== 'paket' && tableName !== 'menugroups' && config.specificFields.find(f => f.id === 'cab')) {
                conditions.push("`cab` = ?"); params.push(window.xcab);
            }
            whereClause = ` WHERE ${conditions.join(' AND ')}`;
        } else {
            let conditions = [];
            if (window.xbrand && tableName !== 'menus' && tableName !== 'paket' && tableName !== 'menugroups' && config.specificFields.find(f => f.id === 'brand')) {
                conditions.push("`brand` = ?"); params.push(window.xbrand);
            }
            if (window.xcab && tableName !== 'menus' && tableName !== 'paket' && tableName !== 'menugroups' && config.specificFields.find(f => f.id === 'cab')) {
                conditions.push("`cab` = ?"); params.push(window.xcab);
            }
            if (conditions.length > 0) whereClause = ` WHERE ${conditions.join(' AND ')}`;
        }

        const offset = (currentPage - 1) * rowsPerPage;
        try {
            let countSql = `SELECT COUNT(*) as total FROM \`${tableName}\`${whereClause}`;
            let dataSql = `SELECT ${queryFieldsToSelect} FROM \`${tableName}\`${whereClause} ORDER BY \`${ACTUAL_DB_PRIMARY_KEY_NAME}\` ASC LIMIT ${rowsPerPage} OFFSET ${offset}`;

            let paramIndex = 0;
            const escapeSqlValue = (val) => {
                if (val === null || val === undefined) return 'NULL';
                if (typeof val === 'string') return `'${val.replace(/'/g, "''")}'`;
                return val;
            };

            if (params.length > 0) {
                countSql = countSql.replace(/\?/g, () => escapeSqlValue(params[paramIndex++]));
                paramIndex = 0;
                dataSql = dataSql.replace(/\?/g, () => escapeSqlValue(params[paramIndex++]));
            }

            const countResult = await window.ambil_select(countSql);
            if (countResult && countResult.length > 0 && countResult[0].total !== undefined) {
                totalRows = parseInt(countResult[0].total);
                totalPages = Math.ceil(totalRows / rowsPerPage) || 1;
            } else { totalRows = 0; totalPages = 1; }
            if (currentPage > totalPages) currentPage = totalPages;
            if (currentPage < 1) currentPage = 1;
            $gotoPageInput.val(currentPage);


            const data = await window.ambil_select(dataSql);
            $tableBody.empty();
            if (data && data.length > 0) {
                data.forEach(item => {
                    const itemId = item[ACTUAL_DB_PRIMARY_KEY_NAME];
                    
                    let rowHtml = `<tr data-id="${itemId}">`;
                    displayFieldsConfig.forEach(field => {
                        let val = item[field.id];
                        if (val === null || val === undefined) val = '';
                        else if (field.type === 'number' && typeof val === 'number') val = val.toLocaleString('id-ID');
                        else if (field.type === 'checkbox') val = (String(val) === '1' || val === 1) ? 'Ya' : 'Tidak';
                        rowHtml += `<td>${val}</td>`;
                    });
                    rowHtml += `</tr>`;
                    $tableBody.append(rowHtml);
                });
            } else {
                const colspan = displayFieldsConfig.length || 1;
                $tableBody.append(`<tr><td colspan="${colspan}" class="text-center">Tidak ada data.</td></tr>`);
            }
            updatePaginationControls();
        } catch (error) {
            console.error(`Error loading data for ${tableName}:`, error);
            const colspan = displayFieldsConfig.length || 1;
            $tableBody.append(`<tr><td colspan="${colspan}" class="text-center">Gagal memuat data. Cek console.</td></tr>`);
        } finally { hideLoading(); }
    }

    function updatePaginationControls() {}

    $container.find(`#btnGridSearch_${tableName}`).on('click', function () { currentSearchField = $searchFieldSelect.val(); currentSearchKeyword = $searchKeywordInput.val().trim(); loadGridData(1); });
    $container.find(`#btnGridResetSearch_${tableName}`).on('click', function () { currentSearchField = ''; currentSearchKeyword = ''; $searchFieldSelect.val(''); $searchKeywordInput.val(''); loadGridData(1); });
    $searchKeywordInput.on('keypress', function (e) { if (e.which === 13) { $container.find(`#btnGridSearch_${tableName}`).click(); } });

    $tableBody.on('click', 'tr', function () {
        const clickedIdRaw = $(this).data('id');
        const clickedIdStr = String(clickedIdRaw);

        if (clickedIdRaw !== undefined && clickedIdRaw !== null && clickedIdStr !== "undefined" && clickedIdStr !== "" && onRowClickCallback && typeof onRowClickCallback === 'function') {
            onRowClickCallback(clickedIdStr);
        } else {
            console.warn("createInteractiveGrid: clickedId tidak valid atau callback tidak ada.", clickedIdRaw);
        }
    });

    $container.find(`#btnGridFirst_${tableName}`).on('click', () => loadGridData(1));
    $container.find(`#btnGridPrev_${tableName}`).on('click', () => { if (currentPage > 1) loadGridData(currentPage - 1); });
    $container.find(`#btnGridNext_${tableName}`).on('click', () => { if (currentPage < totalPages) loadGridData(currentPage + 1); });
    $container.find(`#btnGridLast_${tableName}`).on('click', () => loadGridData(totalPages));
    $container.find(`#btnGridGotoPage_${tableName}`).on('click', function () {
        let targetPage = parseInt($gotoPageInput.val());
        if (!isNaN(targetPage) && targetPage >= 1 && targetPage <= totalPages) {
            loadGridData(targetPage);
        } else {
            alert(`Nomor halaman tidak valid. Masukkan antara 1 dan ${totalPages}.`);
            $gotoPageInput.val(currentPage);
        }
    });
    $gotoPageInput.on('keypress', function (e) { if (e.which === 13) { $container.find(`#btnGridGotoPage_${tableName}`).click(); } });

    loadGridData(1);
};