const { createApp, ref, computed, onMounted, nextTick, watch, provide, inject, defineComponent } = Vue;

// --- Helper Functions ---
const findElementById = (nodes, id) => {
    for (const node of nodes) {
        if (node.id === id) return node;
        if (node.children && node.children.length > 0) {
            const found = findElementById(node.children, id);
            if (found) return found;
        }
    }
    return null;
};

const deleteElementById = (nodes, id) => {
    for (let i = 0; i < nodes.length; i++) {
        if (nodes[i].id === id) {
            nodes.splice(i, 1);
            return true;
        }
        if (nodes[i].children) {
            if (deleteElementById(nodes[i].children, id)) return true;
        }
    }
    return false;
};

const normalizeElements = (nodes) => {
    if (!nodes || !Array.isArray(nodes)) return;
    for (const node of nodes) {
        if (!node.children) {
            node.children = [];
        }
        normalizeElements(node.children);
    }
};

// --- Aplikasi Utama ---
const app = createApp({
    setup() {
        // --- STATE ---
        const isLoading = ref(true);
        const error = ref(null);
        const isEditMode = ref(false);
        const allElements = ref([]);
        const selectedElementId = ref(null);
        const detailRecords = ref([]);
        const detailConfig = ref(null);
        const masterRecord = ref({});
        const menuConfig = ref({});
        const recordId = ref(null);
        const menuId = ref(null);
        const dragContext = ref({
            offsetX: 0,
            offsetY: 0
        });

        // --- COMPUTED ---
        const selectedElement = computed(() => {
            if (!selectedElementId.value) return null;
            return findElementById(allElements.value, selectedElementId.value);
        });

        // --- Inisialisasi ---
        const initialize = async () => {
            isLoading.value = true;
            error.value = null;
            try {
                const urlParams = new URLSearchParams(window.location.search);
                menuId.value = urlParams.get('menuId');
                recordId.value = urlParams.get('recordId');

                if (!menuId.value || !recordId.value) {
                    throw new Error("Parameter 'menuId' dan 'recordId' diperlukan.");
                }

                const [mConfig, savedLayout] = await Promise.all([
                    loadNodeConfig(menuId.value),
                    window.getPrintLayout(menuId.value)
                ]);

                menuConfig.value = mConfig;

                const recordData = await loadRecordData(recordId.value, mConfig);
                masterRecord.value = recordData.mRecord;
                detailRecords.value = recordData.dRecords;
                detailConfig.value = Object.values(mConfig.db).find(c => c.model === 'grid');

                if (savedLayout && savedLayout.elements && savedLayout.elements.length > 0) {
                    allElements.value = savedLayout.elements;
                    await updateDatabaseElementContents(allElements.value, mConfig, recordData);
                } else {
                    const adoptedElements = adoptHtmlElements();
                    const dataElements = await generateDataElements(mConfig, recordData);
                    allElements.value = [...adoptedElements, ...dataElements];
                }

                normalizeElements(allElements.value);

                nextTick(() => {
                    makeWindowsDraggable();
                });

            } catch (err) {
                console.error("Initialization Error:", err);
                error.value = err.stack || err.message;
            } finally {
                isLoading.value = false;
            }
        };

        // --- FUNGSI SUMBER DATA ---
        const adoptHtmlElements = () => {
            const elements = [];
            document.querySelectorAll('[data-editable="true"]').forEach(node => {
                const styleObj = {};
                for (let i = 0; i < node.style.length; i++) {
                    const key = node.style[i];
                    styleObj[key] = node.style.getPropertyValue(key);
                }

                elements.push({
                    id: node.id || `adopted-${Date.now()}`,
                    tag: node.tagName.toLowerCase(),
                    content: node.innerHTML,
                    style: {
                        ...styleObj,
                        position: 'absolute',
                        top: node.offsetTop + 'px',
                        left: node.offsetLeft + 'px',
                        width: node.offsetWidth + 'px',
                        height: node.offsetHeight + 'px'
                    },
                    children: [],
                    isAdopted: true
                });
                node.style.display = 'none';
            });
            return elements;
        };

        const generateDataElements = async (config, recordData) => {
            const elements = [];
            const masterConf = Object.values(config.db).find(c => c.model === 'form');
            let yPos = 80;
            if (masterConf && masterConf.specificFields) {
                for (const field of masterConf.specificFields) {
                    if (field.hiddenPrint === true) continue;
                    let content = recordData.mRecord[field.id] || '';
                    if (field.link && field.link[0] && field.link[0].field2 && content && window.ambil) {
                        const linkConf = field.link[0];
                        try {
                            const results = await window.ambil(linkConf.table, linkConf.field1, content);
                            if (results && results[0] && results[0][linkConf.field2]) {
                                content += ` - ${results[0][linkConf.field2]}`;
                            }
                        } catch (e) { console.warn("Gagal mengambil data link:", e); }
                    }
                    if (content) {
                        elements.push({
                            id: `data-${field.id}`,
                            tag: 'div',
                            content: `${field.label || field.id}: ${content}`,
                            style: { position: 'absolute', top: `${yPos}px`, left: '75px' },
                            children: [],
                            isDatabaseField: true
                        });
                        yPos += 30;
                    }
                }
            }
            elements.push({
                id: 'c_items_table', tag: 'div', content: 'AREA TABEL ITEM', isPlaceholder: true,
                style: { position: 'absolute', top: `${yPos + 20}px`, left: '75px', width: 'calc(100% - 150px)', height: '300px', border: '1px dashed #ccc' },
                children: []
            });
            return elements;
        };

        const updateDatabaseElementContents = async (elements, config, recordData) => {
            const masterConf = Object.values(config.db).find(c => c.model === 'form');
            if (!masterConf || !masterConf.specificFields) return;
            for (const element of elements) {
                if (element.id.startsWith('data-') && element.isDatabaseField) {
                    const fieldId = element.id.replace('data-', '');
                    let content = recordData.mRecord[fieldId] || '';
                    const field = masterConf.specificFields.find(f => f.id === fieldId);
                    if (field && field.link && field.link[0] && field.link[0].field2 && content && window.ambil) {
                        const linkConf = field.link[0];
                        try {
                            const results = await window.ambil(linkConf.table, linkConf.field1, content);
                            if (results && results[0] && results[0][linkConf.field2]) {
                                content += ` - ${results[0][linkConf.field2]}`;
                            }
                        } catch (e) { console.warn("Gagal mengambil data link:", e); }
                    }
                    element.content = content ? `${field.label || fieldId}: ${content}` : '';
                }
                if (element.children && element.children.length > 0) {
                    await updateDatabaseElementContents(element.children, config, recordData);
                }
            }
        };

        const loadNodeConfig = async (id) => {
            const result = await window.ambil('node', 'node', id);
            alert(JSON.stringify(result+id));
            return JSON.parse(result[0].json);
        };

        const saveLayout = async () => {
            try {
                if (!menuId.value) throw new Error('menuId tidak tersedia untuk menyimpan layout.');
                const layoutToSave = {
                    elements: JSON.parse(JSON.stringify(allElements.value))
                };
                filterDatabaseElementContents(layoutToSave.elements);
                const response = await window.savePrintLayout(menuId.value, layoutToSave);
                if (response.success) {
                    alert(`Layout untuk menu "${menuId.value}" berhasil disimpan!`);
                } else {
                    throw new Error(response.message || 'Gagal menyimpan layout.');
                }
            } catch (err) {
                console.error("Error saving layout:", err);
                alert('Gagal menyimpan layout: ' + err.message);
            }
        };

        const filterDatabaseElementContents = (elements) => {
            for (const element of elements) {
                if (element.id.startsWith('data-') && element.isDatabaseField) {
                    delete element.content;
                }
                if (element.children && element.children.length > 0) {
                    filterDatabaseElementContents(element.children);
                }
            }
        };

        const loadRecordData = async (recId, nodeConfig) => {
            const masterConf = Object.values(nodeConfig.db).find(c => c.model === 'form');
            const detailConf = Object.values(nodeConfig.db).find(c => c.model === 'grid');
            if (!masterConf) throw new Error("Konfigurasi master 'form' tidak ditemukan.");
            const mRecordResult = await window.ambil(masterConf.table, 'id', recId);
            if (!mRecordResult || mRecordResult.length === 0) {
                throw new Error(`Record master dengan ID '${recId}' tidak ditemukan di tabel '${masterConf.table}'.`);
            }
            const mRecord = mRecordResult[0];
            let dRecords = [];
            if (detailConf && masterConf.filterx && detailConf.filter) {
                const filterValue = mRecord[masterConf.filterx];
                if (filterValue) {
                    dRecords = await window.ambil(detailConf.table, detailConf.filter, filterValue);
                }
            }
            return { mRecord, dRecords };
        };

        // --- FUNGSI KONTROL UTAMA ---
        const printPage = () => {
            const originalMode = isEditMode.value;
            isEditMode.value = false;
            nextTick(() => {
                window.print();
                isEditMode.value = originalMode;
            });
        };

        const resetLayout = async () => {
            if (confirm('Anda yakin ingin mereset layout? Semua kustomisasi akan dihapus dan layout akan kembali ke default.')) {
                try {
                    isLoading.value = true;
                    await window.savePrintLayout(menuId.value, { elements: [] });
                    alert('Layout telah berhasil direset.');
                    await initialize();
                } catch (err) {
                    console.error("Error resetting layout:", err);
                    alert('Gagal mereset layout: ' + err.message);
                } finally {
                    isLoading.value = false;
                }
            }
        };

        // --- FUNGSI EDITOR ---
        const addElement = (type) => {
            const newElement = {
                id: `el-${Date.now()}`,
                tag: type,
                content: `Elemen Baru`,
                children: [],
                style: { position: 'absolute', top: '150px', left: '150px', width: '200px', minHeight: '50px' }
            };
            if (type === 'img') {
                newElement.src = 'https://via.placeholder.com/150';
                newElement.content = '';
            }
            allElements.value.push(newElement);
            selectedElementId.value = newElement.id;
        };

        const removeSelectedElement = () => {
            if (!selectedElement.value) return;
            if (confirm(`Anda yakin ingin menghapus elemen ID: ${selectedElement.value.id}?`)) {
                if (deleteElementById(allElements.value, selectedElement.value.id)) {
                    selectedElementId.value = null;
                }
            }
        };

        const selectElement = (event, element) => {
            if (!isEditMode.value) return;
            event.stopPropagation();
            selectedElementId.value = element.id;
        };

        const deselectElement = () => {
            selectedElementId.value = null;
        };

        // --- FUNGSI DRAG & DROP & JENDELA ---
        const onDragStart = (event, element) => {
            if (!isEditMode.value) return;
            event.dataTransfer.setData('text/plain', element.id);
            event.dataTransfer.dropEffect = 'move';
            const rect = event.target.getBoundingClientRect();
            dragContext.value.offsetX = event.clientX - rect.left;
            dragContext.value.offsetY = event.clientY - rect.top;
        };

        const onDrop = (event) => {
            event.preventDefault();
            const elementId = event.dataTransfer.getData('text/plain');
            const droppedElement = findElementById(allElements.value, elementId);
            if (droppedElement) {
                const containerRect = event.currentTarget.getBoundingClientRect();
                let newLeft = event.clientX - containerRect.left - dragContext.value.offsetX;
                let newTop = event.clientY - containerRect.top - dragContext.value.offsetY;
                droppedElement.style.top = `${Math.max(0, newTop)}px`;
                droppedElement.style.left = `${Math.max(0, newLeft)}px`;
            }
        };

        const makeWindowsDraggable = () => {
            document.querySelectorAll('.ve-floating-window').forEach(win => {
                const header = win.querySelector('.ve-window-header');
                if (!header) return;
                header.onmousedown = (e) => {
                    e.preventDefault();
                    let shiftX = e.clientX - win.offsetLeft;
                    let shiftY = e.clientY - win.offsetTop;
                    function moveAt(pageX, pageY) {
                        win.style.left = pageX - shiftX + 'px';
                        win.style.top = pageY - shiftY + 'px';
                    }
                    function onMouseMove(event) { moveAt(event.pageX, event.pageY); }
                    document.addEventListener('mousemove', onMouseMove);
                    document.addEventListener('mouseup', () => {
                        document.removeEventListener('mousemove', onMouseMove);
                    }, { once: true });
                };
                header.ondragstart = () => false;
            });
        };

        // --- Handler untuk event dari PropertiesPanel ---
        const handleUpdateProperty = ({ path, value }) => {
            if (!selectedElement.value) return;
            const keys = path.split('.');
            let obj = selectedElement.value;
            for (let i = 0; i < keys.length - 1; i++) {
                if (!obj[keys[i]]) obj[keys[i]] = {};
                obj = obj[keys[i]];
            }
            obj[keys[keys.length - 1]] = value;
        };

        // --- PROVIDE & MOUNTED ---
        provide('isEditMode', isEditMode);
        provide('selectedElementId', selectedElementId);
        provide('detailRecords', detailRecords);
        provide('detailConfig', detailConfig);
        provide('selectElement', selectElement);
        provide('dragHandlers', { onDragStart });

        onMounted(initialize);

        return {
            isLoading, error, isEditMode, allElements, selectedElement,
            printPage, resetLayout, addElement, removeSelectedElement, saveLayout,
            onDrop, deselectElement, handleUpdateProperty
        };
    },
    // DAFTARKAN KOMPONEN BARU DAN KOMPONEN LAMA
    components: {
        'toolbox': Toolbox,
        'properties-panel': PropertiesPanel,
        'render-element': defineComponent({
            name: 'render-element',
            props: ['element'],
            setup(props) {
                const isEditMode = inject('isEditMode');
                const selectedElementId = inject('selectedElementId');
                const detailRecords = inject('detailRecords');
                const detailConfig = inject('detailConfig');
                const selectElement = inject('selectElement');
                const { onDragStart } = inject('dragHandlers');
                const isSelected = computed(() => selectedElementId.value === props.element.id);

                const getColumnStyle = (col) => {
                    switch (col.type) {
                        case 'number':
                        case 'formula':
                            return { 'text-align': 'right' };
                        case 'checkbox':
                            return { 'text-align': 'center' };
                        default:
                            return { 'text-align': 'left' };
                    }
                };

                // --- MODIFIKASI UNTUK KALKULASI FORMULA ---
                const formatCellValue = (value, col, row) => {
                    if (value === null || value === undefined) {
                        // Jika kolom adalah formula, tetap coba hitung meskipun value aslinya kosong
                        if (col.type !== 'formula') return '';
                    }

                    switch (col.type) {
                        case 'number':
                            const num = parseFloat(value);
                            return isNaN(num) ? value : new Intl.NumberFormat('id-ID').format(num);

                        case 'date':
                            const date = new Date(value);
                            if (isNaN(date.getTime())) return value;
                            const year = date.getFullYear();
                            const month = (date.getMonth() + 1).toString().padStart(2, '0');
                            const day = date.getDate().toString().padStart(2, '0');
                            return `${year}-${month}-${day}`;

                        case 'checkbox':
                            return (value === '1' || value === 1 || value === true) ? '1' : '0';

                        case 'formula':
                            if (!col.rumus) return 'No Formula';
                            try {
                                // Ekstrak semua nama variabel dari string rumus
                                const varNames = [...new Set(col.rumus.match(/[a-zA-Z_]\w*/g) || [])];

                                // Ambil nilai dari objek 'row' untuk setiap variabel
                                const varValues = varNames.map(name => {
                                    const v = row[name];
                                    // Jika nilai tidak ditemukan atau bukan angka, anggap 0
                                    return parseFloat(v) || 0;
                                });

                                // Buat fungsi dinamis yang aman
                                const formulaFunc = new Function(...varNames, `return ${col.rumus}`);

                                // Panggil fungsi dengan nilai-nilai dari baris
                                const result = formulaFunc(...varValues);

                                // Format hasilnya sebagai angka
                                return isNaN(result) ? 'Error' : new Intl.NumberFormat('id-ID').format(result);
                            } catch (e) {
                                console.error(`Error evaluasi rumus untuk kolom '${col.id}':`, e);
                                return 'Error';
                            }

                        case 'text':
                        default:
                            return value;
                    }
                };
                // --- AKHIR MODIFIKASI ---

                return {
                    isEditMode, isSelected, detailRecords, detailConfig, selectElement, onDragStart,
                    getColumnStyle,
                    formatCellValue
                };
            },
            template: `
              <div v-if="element.id === 'c_items_table' && !isEditMode" :style="element.style">
                  <table class="real-items-table" v-if="detailRecords && detailRecords.length > 0 && detailConfig">
                    <thead>
                      <tr>
                        <th v-for="col in detailConfig.specificFields.filter(c => !c.hiddenPrint)" :style="getColumnStyle(col)">{{ col.label }}</th>
                      </tr>
                    </thead>
                    <tbody>
                      <tr v-for="(row, rowIndex) in detailRecords" :key="rowIndex">
                        <td v-for="col in detailConfig.specificFields.filter(c => !c.hiddenPrint)" :style="getColumnStyle(col)">
                            {{ formatCellValue(row[col.id], col, row) }}
                        </td>
                      </tr>
                    </tbody>
                  </table>
              </div>
              <component v-else :is="element.tag"
                         :class="['editable-element', { selected: isSelected, 'placeholder-area': element.isPlaceholder }]"
                         :style="element.style" :src="element.src"
                         @click.stop="selectElement($event, element)"
                         @dragstart="onDragStart($event, element)"
                         :draggable="isEditMode && element.style.position === 'absolute'">
                  <template v-if="!element.children || element.children.length === 0">{{ element.content }}</template>
                  <render-element v-for="child in element.children" :key="child.id" :element="child" />
              </component>
            `
        })
    },
    // TEMPLATE UTAMA
    template: `
      <div class="main-container" :class="{ 'is-editor-mode': isEditMode }">
        
        <div class="main-controls no-print">
            <button @click="printPage" title="Cetak"><i class="fa-solid fa-print"></i></button>
            <button @click="isEditMode = !isEditMode" :class="{ active: isEditMode }" title="Mode Edit">
                <i class="fa-solid fa-pen-ruler"></i>
            </button>
        </div>

        <toolbox v-if="isEditMode" 
            @add-element="addElement" 
            @save-layout="saveLayout" 
            @reset-layout="resetLayout" />

        <properties-panel v-if="isEditMode && selectedElement"
            :selected-element="selectedElement"
            @update-property="handleUpdateProperty"
            @delete-element="removeSelectedElement" />

        <div class="workspace">
          <div v-if="isLoading" class="loading-overlay">Memuat...</div>
          <div v-if="error" class="error-overlay"><strong>Error:</strong><br><pre>{{ error }}</pre></div>
          <div v-if="!isLoading && !error" id="printable-container" @dragover.prevent @drop="onDrop" @click="deselectElement">
            <render-element v-for="element in allElements" :key="element.id" :element="element"></render-element>
          </div>
        </div>
      </div>
    `
});

app.mount('#app');