(function () {
    'use strict';

    const config = window.KARVI_IDB_ADMIN || {};
    const labels = config.labels || {};
    const registry = window.KARVI_INDEXEDDB_REGISTRY || [];

    const loadingEl = document.getElementById('idbLoading');
    const emptyEl = document.getElementById('idbEmpty');
    const tableWrap = document.getElementById('idbTableWrap');
    const tableBody = document.getElementById('idbTableBody');
    const refreshBtn = document.getElementById('idbRefreshBtn');
    const viewModal = document.getElementById('idbViewModal');
    const viewTitle = document.getElementById('idbViewModalTitle');
    const viewContent = document.getElementById('idbViewModalContent');
    const viewCloseButtons = document.querySelectorAll('[data-idb-modal-close]');

    function label(key, fallback) {
        return labels[key] || fallback || key;
    }

    function escapeHtml(value) {
        return String(value ?? '')
            .replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;');
    }

    function formatBytes(bytes) {
        const size = Number(bytes) || 0;
        if (size < 1024) {
            return size + ' B';
        }
        if (size < 1024 * 1024) {
            return (size / 1024).toFixed(1) + ' KB';
        }
        return (size / (1024 * 1024)).toFixed(2) + ' MB';
    }

    function setLoading(isLoading) {
        if (loadingEl) {
            loadingEl.classList.toggle('hidden', !isLoading);
        }
        if (tableWrap && isLoading) {
            tableWrap.classList.add('hidden');
        }
    }

    function discoverDatabases() {
        if (!window.indexedDB || typeof indexedDB.databases !== 'function') {
            return Promise.resolve([]);
        }

        return indexedDB.databases().catch(function () {
            return [];
        });
    }

    function mergeRegistryWithDiscovered(discovered) {
        const map = new Map();

        registry.forEach(function (entry) {
            map.set(entry.dbName, {
                dbName: entry.dbName,
                version: entry.version || 1,
                labelKey: entry.labelKey || '',
                stores: (entry.stores || []).map(function (store) {
                    return {
                        name: store.name,
                        keyPath: store.keyPath || 'id',
                        labelKey: store.labelKey || '',
                    };
                }),
            });
        });

        discovered.forEach(function (item) {
            const dbName = item.name;
            if (!dbName || map.has(dbName)) {
                return;
            }

            map.set(dbName, {
                dbName: dbName,
                version: item.version || 1,
                labelKey: '',
                stores: [],
                discovered: true,
            });
        });

        return Array.from(map.values());
    }

    function openDatabase(dbName, version) {
        return new Promise(function (resolve, reject) {
            const request = indexedDB.open(dbName, version || undefined);

            request.onerror = function () {
                reject(request.error || new Error('Could not open database.'));
            };

            request.onsuccess = function () {
                resolve(request.result);
            };
        });
    }

    function readStore(db, storeName) {
        return new Promise(function (resolve, reject) {
            if (!db.objectStoreNames.contains(storeName)) {
                resolve({
                    keyPath: '',
                    records: [],
                    keys: [],
                    count: 0,
                    sizeBytes: 0,
                });
                return;
            }

            const tx = db.transaction(storeName, 'readonly');
            const store = tx.objectStore(storeName);
            const keyPath = typeof store.keyPath === 'string' ? store.keyPath : '';
            const request = store.getAll();

            request.onsuccess = function () {
                const records = request.result || [];
                const keys = records.map(function (record, index) {
                    if (keyPath && record && record[keyPath] !== undefined) {
                        return record[keyPath];
                    }
                    return index;
                });
                const json = JSON.stringify(records);

                resolve({
                    keyPath: keyPath,
                    records: records,
                    keys: keys,
                    count: records.length,
                    sizeBytes: new Blob([json]).size,
                });
            };

            request.onerror = function () {
                reject(request.error || new Error('Could not read store.'));
            };
        });
    }

    function resolveStoresForDatabase(db, dbEntry) {
        const storeNames = [];

        if (dbEntry.stores && dbEntry.stores.length > 0) {
            dbEntry.stores.forEach(function (store) {
                storeNames.push(store);
            });
        } else {
            Array.from(db.objectStoreNames).forEach(function (name) {
                storeNames.push({ name: name, keyPath: '', labelKey: '' });
            });
        }

        return Promise.all(storeNames.map(function (storeMeta) {
            return readStore(db, storeMeta.name).then(function (stats) {
                return {
                    dbName: dbEntry.dbName,
                    dbVersion: db.version,
                    dbLabelKey: dbEntry.labelKey || '',
                    storeName: storeMeta.name,
                    storeLabelKey: storeMeta.labelKey || '',
                    keyPath: stats.keyPath || storeMeta.keyPath || '—',
                    count: stats.count,
                    sizeBytes: stats.sizeBytes,
                    keys: stats.keys,
                    records: stats.records,
                };
            });
        }));
    }

    function loadAllStores() {
        if (!window.indexedDB) {
            throw new Error(label('idb_not_supported', 'IndexedDB is not available in this browser.'));
        }

        return discoverDatabases().then(function (discovered) {
            const databases = mergeRegistryWithDiscovered(discovered);

            return Promise.all(databases.map(function (dbEntry) {
                return openDatabase(dbEntry.dbName, dbEntry.version)
                    .then(function (db) {
                        return resolveStoresForDatabase(db, dbEntry).finally(function () {
                            db.close();
                        });
                    })
                    .catch(function () {
                        if (!dbEntry.stores || dbEntry.stores.length === 0) {
                            return [];
                        }

                        return dbEntry.stores.map(function (store) {
                            return {
                                dbName: dbEntry.dbName,
                                dbVersion: dbEntry.version,
                                dbLabelKey: dbEntry.labelKey || '',
                                storeName: store.name,
                                storeLabelKey: store.labelKey || '',
                                keyPath: store.keyPath || '—',
                                count: 0,
                                sizeBytes: 0,
                                keys: [],
                                records: [],
                                unavailable: true,
                            };
                        });
                    });
            })).then(function (groups) {
                return groups.flat();
            });
        });
    }

    function keysPreview(keys) {
        if (!keys || keys.length === 0) {
            return '—';
        }

        const preview = keys.slice(0, 5).map(String).join(', ');
        if (keys.length > 5) {
            return preview + ' … (+' + (keys.length - 5) + ')';
        }

        return preview;
    }

    function renderRows(rows) {
        const hasRows = rows.length > 0;

        if (emptyEl) {
            emptyEl.classList.toggle('hidden', hasRows);
        }
        if (tableWrap) {
            tableWrap.classList.toggle('hidden', !hasRows);
        }
        if (!tableBody) {
            return;
        }

        if (!hasRows) {
            tableBody.innerHTML = '';
            return;
        }

        tableBody.innerHTML = rows.map(function (row) {
            const dbLabel = row.dbLabelKey ? label(row.dbLabelKey, row.dbName) : row.dbName;
            const storeLabel = row.storeLabelKey ? label(row.storeLabelKey, row.storeName) : row.storeName;
            const rowId = escapeHtml(row.dbName + '::' + row.storeName);
            const unavailable = row.unavailable ? ' <span class="text-xs text-karvi-400">(' + escapeHtml(label('idb_unavailable', 'not open')) + ')</span>' : '';

            return ''
                + '<tr data-idb-row="' + rowId + '">'
                + '<td><div class="font-semibold text-karvi-950">' + escapeHtml(dbLabel) + '</div><div class="text-xs font-mono text-karvi-500 mt-0.5">' + escapeHtml(row.dbName) + '</div></td>'
                + '<td><div class="font-medium text-karvi-900">' + escapeHtml(storeLabel) + unavailable + '</div><div class="text-xs font-mono text-karvi-500 mt-0.5">' + escapeHtml(row.storeName) + '</div></td>'
                + '<td><span class="font-mono text-xs bg-karvi-100 px-2 py-1 rounded-lg">' + escapeHtml(row.keyPath || '—') + '</span></td>'
                + '<td class="font-semibold">' + escapeHtml(String(row.count)) + '</td>'
                + '<td class="text-karvi-600 text-sm">' + escapeHtml(formatBytes(row.sizeBytes)) + '</td>'
                + '<td class="text-xs text-karvi-600 max-w-[12rem] truncate" title="' + escapeHtml(keysPreview(row.keys)) + '">' + escapeHtml(keysPreview(row.keys)) + '</td>'
                + '<td>'
                + '<div class="flex items-center gap-2 flex-wrap">'
                + '<button type="button" class="btn-secondary !py-1.5 !px-2.5 !text-xs whitespace-nowrap" data-idb-view data-db="' + escapeHtml(row.dbName) + '" data-store="' + escapeHtml(row.storeName) + '">'
                + '<span class="material-symbols-rounded text-base">visibility</span>'
                + escapeHtml(label('idb_view', 'View'))
                + '</button>'
                + '<button type="button" class="btn-secondary !py-1.5 !px-2.5 !text-xs whitespace-nowrap" data-idb-download data-db="' + escapeHtml(row.dbName) + '" data-store="' + escapeHtml(row.storeName) + '">'
                + '<span class="material-symbols-rounded text-base">download</span>'
                + escapeHtml(label('idb_download', 'Download'))
                + '</button>'
                + '<button type="button" class="btn-danger !py-1.5 !px-2.5 !text-xs whitespace-nowrap" data-idb-delete data-db="' + escapeHtml(row.dbName) + '" data-store="' + escapeHtml(row.storeName) + '">'
                + '<span class="material-symbols-rounded text-base">delete</span>'
                + escapeHtml(label('idb_delete', 'Delete'))
                + '</button>'
                + '</div>'
                + '</td>'
                + '</tr>';
        }).join('');

        bindRowActions(rows);
    }

    function findRowData(rows, dbName, storeName) {
        return rows.find(function (row) {
            return row.dbName === dbName && row.storeName === storeName;
        }) || null;
    }

    function openViewModal(row) {
        if (!viewModal || !viewContent || !viewTitle) {
            return;
        }

        const dbLabel = row.dbLabelKey ? label(row.dbLabelKey, row.dbName) : row.dbName;
        const storeLabel = row.storeLabelKey ? label(row.storeLabelKey, row.storeName) : row.storeName;

        viewTitle.textContent = dbLabel + ' / ' + storeLabel;
        viewContent.textContent = JSON.stringify(row.records, null, 2);
        viewModal.classList.remove('hidden');
        document.body.classList.add('admin-modal-open');
    }

    function closeViewModal() {
        if (!viewModal) {
            return;
        }
        viewModal.classList.add('hidden');
        document.body.classList.remove('admin-modal-open');
    }

    function findSyncApiUrl(dbName, storeName) {
        const entry = registry.find(function (item) {
            return item.dbName === dbName;
        });

        if (!entry || !entry.stores) {
            return '';
        }

        const store = entry.stores.find(function (item) {
            return item.name === storeName;
        });

        if (!store || !store.syncApiUrl) {
            return '';
        }

        if (dbName === 'karvi_partner_contracts' && storeName === 'contracts' && config.contractsApiUrl) {
            return config.contractsApiUrl;
        }

        return store.syncApiUrl;
    }

    function syncRowFromApi(row, button) {
        const apiUrl = findSyncApiUrl(row.dbName, row.storeName);
        const db = window.KarviContractsDB;

        if (!apiUrl) {
            window.alert(label('idb_sync_error', 'Could not sync from API.'));
            return;
        }

        if (!db || typeof db.saveContracts !== 'function') {
            window.alert(label('idb_sync_error', 'Could not sync from API.'));
            return;
        }

        const originalHtml = button ? button.innerHTML : '';
        if (button) {
            button.disabled = true;
            button.innerHTML = '<span class="material-symbols-rounded text-base animate-pulse">sync</span> ' + escapeHtml(label('idb_sync_loading', 'Syncing…'));
        }

        fetch(apiUrl, {
            method: 'GET',
            headers: {
                Accept: 'application/json',
            },
            credentials: 'same-origin',
        })
            .then(function (response) {
                return response.json().then(function (payload) {
                    if (!response.ok || !payload.success) {
                        throw new Error(payload.message || label('idb_sync_error', 'Could not sync from API.'));
                    }
                    return payload.contracts || [];
                });
            })
            .then(function (contracts) {
                return db.saveContracts(contracts).then(function () {
                    return contracts.length;
                });
            })
            .then(function (count) {
                window.alert(label('idb_sync_success', 'Synced {count} records from API.').replace('{count}', String(count)));
                return refresh();
            })
            .catch(function (error) {
                window.alert(error.message || label('idb_sync_error', 'Could not sync from API.'));
            })
            .finally(function () {
                if (button) {
                    button.disabled = false;
                    button.innerHTML = originalHtml;
                }
            });
    }

    function clearStore(dbName, version, storeName) {
        return openDatabase(dbName, version).then(function (db) {
            return new Promise(function (resolve, reject) {
                if (!db.objectStoreNames.contains(storeName)) {
                    db.close();
                    resolve();
                    return;
                }

                const tx = db.transaction(storeName, 'readwrite');
                const store = tx.objectStore(storeName);
                const request = store.clear();

                request.onsuccess = function () {
                    resolve();
                };

                request.onerror = function () {
                    reject(request.error || new Error('Could not clear store.'));
                };

                tx.oncomplete = function () {
                    db.close();
                };
            });
        });
    }

    function deleteDatabase(dbName) {
        return new Promise(function (resolve, reject) {
            const request = indexedDB.deleteDatabase(dbName);

            request.onsuccess = function () {
                resolve();
            };

            request.onerror = function () {
                reject(request.error || new Error('Could not delete database.'));
            };

            request.onblocked = function () {
                reject(new Error(label('idb_delete_blocked', 'Delete blocked — close other tabs using this database.')));
            };
        });
    }

    function bindRowActions(rows) {
        document.querySelectorAll('[data-idb-view]').forEach(function (button) {
            button.addEventListener('click', function () {
                const row = findRowData(rows, button.getAttribute('data-db'), button.getAttribute('data-store'));
                if (row) {
                    openViewModal(row);
                }
            });
        });

        document.querySelectorAll('[data-idb-download]').forEach(function (button) {
            button.addEventListener('click', function () {
                const row = findRowData(rows, button.getAttribute('data-db'), button.getAttribute('data-store'));
                if (row) {
                    syncRowFromApi(row, button);
                }
            });
        });

        document.querySelectorAll('[data-idb-delete]').forEach(function (button) {
            button.addEventListener('click', function () {
                const dbName = button.getAttribute('data-db') || '';
                const storeName = button.getAttribute('data-store') || '';
                const row = findRowData(rows, dbName, storeName);
                if (!row) {
                    return;
                }

                const message = label('idb_delete_confirm', 'Delete all data in this IndexedDB store?')
                    .replace('{database}', dbName)
                    .replace('{store}', storeName);

                if (!window.confirm(message)) {
                    return;
                }

                const version = row.dbVersion || 1;
                const registryEntry = registry.find(function (entry) {
                    return entry.dbName === dbName;
                });
                const storeCount = registryEntry && registryEntry.stores ? registryEntry.stores.length : 1;

                const action = storeCount === 1
                    ? deleteDatabase(dbName)
                    : clearStore(dbName, version, storeName);

                action
                    .then(function () {
                        refresh();
                    })
                    .catch(function (error) {
                        window.alert(error.message || label('idb_delete_error', 'Could not delete data.'));
                    });
            });
        });
    }

    let currentRows = [];

    function refresh() {
        setLoading(true);

        loadAllStores()
            .then(function (rows) {
                currentRows = rows;
                renderRows(rows);
            })
            .catch(function (error) {
                currentRows = [];
                renderRows([]);
                window.alert(error.message || label('idb_load_error', 'Could not load IndexedDB data.'));
            })
            .finally(function () {
                setLoading(false);
            });
    }

    if (refreshBtn) {
        refreshBtn.addEventListener('click', refresh);
    }

    viewCloseButtons.forEach(function (button) {
        button.addEventListener('click', closeViewModal);
    });

    document.addEventListener('keydown', function (event) {
        if (event.key === 'Escape' && viewModal && !viewModal.classList.contains('hidden')) {
            closeViewModal();
        }
    });

    refresh();
})();
