/**
 * LandSalesPro Offline Database Module
 * IndexedDB helper for offline data storage and synchronization
 */

const LandSalesOfflineDB = {
    DB_NAME: 'landsalespro-offline',
    DB_VERSION: 1,
    db: null,

    // Object store names
    STORES: {
        PROJECTS: 'projects',
        PLOTS: 'plots',
        BUYERS: 'buyers',
        SALES: 'sales',
        PAYMENTS: 'payments',
        AGENTS: 'agents',
        EXPENSES: 'expenses',
        SYNC_QUEUE: 'sync-queue',
        CACHE: 'cache'
    },

    /**
     * Initialize the database
     */
    async init() {
        return new Promise((resolve, reject) => {
            if (this.db) {
                resolve(this.db);
                return;
            }

            const request = indexedDB.open(this.DB_NAME, this.DB_VERSION);

            request.onerror = (event) => {
                console.error('[LandSalesDB] Database error:', event.target.error);
                reject(event.target.error);
            };

            request.onsuccess = (event) => {
                this.db = event.target.result;
                console.log('[LandSalesDB] Database initialized');
                resolve(this.db);
            };

            request.onupgradeneeded = (event) => {
                const db = event.target.result;

                // Projects store
                if (!db.objectStoreNames.contains(this.STORES.PROJECTS)) {
                    const store = db.createObjectStore(this.STORES.PROJECTS, { keyPath: 'id' });
                    store.createIndex('user_id', 'user_id', { unique: false });
                    store.createIndex('synced', 'synced', { unique: false });
                }

                // Plots store
                if (!db.objectStoreNames.contains(this.STORES.PLOTS)) {
                    const store = db.createObjectStore(this.STORES.PLOTS, { keyPath: 'id' });
                    store.createIndex('project_id', 'project_id', { unique: false });
                    store.createIndex('status', 'status', { unique: false });
                    store.createIndex('synced', 'synced', { unique: false });
                }

                // Buyers store
                if (!db.objectStoreNames.contains(this.STORES.BUYERS)) {
                    const store = db.createObjectStore(this.STORES.BUYERS, { keyPath: 'id' });
                    store.createIndex('user_id', 'user_id', { unique: false });
                    store.createIndex('synced', 'synced', { unique: false });
                }

                // Sales store
                if (!db.objectStoreNames.contains(this.STORES.SALES)) {
                    const store = db.createObjectStore(this.STORES.SALES, { keyPath: 'id' });
                    store.createIndex('project_id', 'project_id', { unique: false });
                    store.createIndex('buyer_id', 'buyer_id', { unique: false });
                    store.createIndex('plot_id', 'plot_id', { unique: false });
                    store.createIndex('synced', 'synced', { unique: false });
                }

                // Payments store
                if (!db.objectStoreNames.contains(this.STORES.PAYMENTS)) {
                    const store = db.createObjectStore(this.STORES.PAYMENTS, { keyPath: 'id' });
                    store.createIndex('sale_id', 'sale_id', { unique: false });
                    store.createIndex('synced', 'synced', { unique: false });
                }

                // Agents store
                if (!db.objectStoreNames.contains(this.STORES.AGENTS)) {
                    const store = db.createObjectStore(this.STORES.AGENTS, { keyPath: 'id' });
                    store.createIndex('user_id', 'user_id', { unique: false });
                    store.createIndex('synced', 'synced', { unique: false });
                }

                // Expenses store
                if (!db.objectStoreNames.contains(this.STORES.EXPENSES)) {
                    const store = db.createObjectStore(this.STORES.EXPENSES, { keyPath: 'id' });
                    store.createIndex('project_id', 'project_id', { unique: false });
                    store.createIndex('synced', 'synced', { unique: false });
                }

                // Sync queue store
                if (!db.objectStoreNames.contains(this.STORES.SYNC_QUEUE)) {
                    const store = db.createObjectStore(this.STORES.SYNC_QUEUE, { keyPath: 'id', autoIncrement: true });
                    store.createIndex('type', 'type', { unique: false });
                    store.createIndex('created_at', 'created_at', { unique: false });
                    store.createIndex('status', 'status', { unique: false });
                }

                // Cache store
                if (!db.objectStoreNames.contains(this.STORES.CACHE)) {
                    const store = db.createObjectStore(this.STORES.CACHE, { keyPath: 'key' });
                    store.createIndex('expires_at', 'expires_at', { unique: false });
                }

                console.log('[LandSalesDB] Database schema created');
            };
        });
    },

    /**
     * Generic save method
     */
    async save(storeName, data) {
        await this.init();
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction([storeName], 'readwrite');
            const store = transaction.objectStore(storeName);

            const record = {
                ...data,
                synced: navigator.onLine,
                updated_at: new Date().toISOString()
            };

            // Use local ID for offline records
            if (!record.id) {
                record.id = `local_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
                record.is_local = true;
            }

            const request = store.put(record);

            request.onsuccess = () => {
                console.log(`[LandSalesDB] Saved to ${storeName}:`, record.id);

                // Add to sync queue if offline
                if (!navigator.onLine && record.is_local) {
                    this.addToSyncQueue(storeName, 'create', record);
                }

                resolve(record);
            };

            request.onerror = (event) => {
                reject(event.target.error);
            };
        });
    },

    /**
     * Generic get all method
     */
    async getAll(storeName, indexName = null, indexValue = null) {
        await this.init();
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction([storeName], 'readonly');
            const store = transaction.objectStore(storeName);

            let request;
            if (indexName && indexValue !== null) {
                const index = store.index(indexName);
                request = index.getAll(indexValue);
            } else {
                request = store.getAll();
            }

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

            request.onerror = (event) => {
                reject(event.target.error);
            };
        });
    },

    /**
     * Generic get one method
     */
    async get(storeName, id) {
        await this.init();
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction([storeName], 'readonly');
            const store = transaction.objectStore(storeName);
            const request = store.get(id);

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

            request.onerror = (event) => {
                reject(event.target.error);
            };
        });
    },

    /**
     * Generic delete method
     */
    async delete(storeName, id) {
        await this.init();
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction([storeName], 'readwrite');
            const store = transaction.objectStore(storeName);
            const request = store.delete(id);

            request.onsuccess = () => {
                resolve(true);
            };

            request.onerror = (event) => {
                reject(event.target.error);
            };
        });
    },

    // ==========================================
    // Projects
    // ==========================================
    async saveProject(project) {
        return this.save(this.STORES.PROJECTS, project);
    },

    async getProjects(userId = null) {
        return this.getAll(this.STORES.PROJECTS, userId ? 'user_id' : null, userId);
    },

    async getProject(id) {
        return this.get(this.STORES.PROJECTS, id);
    },

    // ==========================================
    // Plots
    // ==========================================
    async savePlot(plot) {
        return this.save(this.STORES.PLOTS, plot);
    },

    async savePlots(plots) {
        await this.init();
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction([this.STORES.PLOTS], 'readwrite');
            const store = transaction.objectStore(this.STORES.PLOTS);

            plots.forEach(plot => {
                const record = {
                    ...plot,
                    synced: true,
                    updated_at: new Date().toISOString()
                };
                store.put(record);
            });

            transaction.oncomplete = () => {
                console.log(`[LandSalesDB] Saved ${plots.length} plots`);
                resolve(true);
            };

            transaction.onerror = (event) => {
                reject(event.target.error);
            };
        });
    },

    async getPlots(projectId = null) {
        return this.getAll(this.STORES.PLOTS, projectId ? 'project_id' : null, projectId);
    },

    async getPlotsByStatus(status) {
        return this.getAll(this.STORES.PLOTS, 'status', status);
    },

    // ==========================================
    // Buyers
    // ==========================================
    async saveBuyer(buyer) {
        return this.save(this.STORES.BUYERS, buyer);
    },

    async getBuyers(userId = null) {
        return this.getAll(this.STORES.BUYERS, userId ? 'user_id' : null, userId);
    },

    async getBuyer(id) {
        return this.get(this.STORES.BUYERS, id);
    },

    // ==========================================
    // Sales
    // ==========================================
    async saveSale(sale) {
        return this.save(this.STORES.SALES, sale);
    },

    async getSales(projectId = null) {
        return this.getAll(this.STORES.SALES, projectId ? 'project_id' : null, projectId);
    },

    async getSale(id) {
        return this.get(this.STORES.SALES, id);
    },

    async getSalesByBuyer(buyerId) {
        return this.getAll(this.STORES.SALES, 'buyer_id', buyerId);
    },

    // ==========================================
    // Payments
    // ==========================================
    async savePayment(payment) {
        return this.save(this.STORES.PAYMENTS, payment);
    },

    async getPayments(saleId = null) {
        return this.getAll(this.STORES.PAYMENTS, saleId ? 'sale_id' : null, saleId);
    },

    // ==========================================
    // Agents
    // ==========================================
    async saveAgent(agent) {
        return this.save(this.STORES.AGENTS, agent);
    },

    async getAgents(userId = null) {
        return this.getAll(this.STORES.AGENTS, userId ? 'user_id' : null, userId);
    },

    // ==========================================
    // Expenses
    // ==========================================
    async saveExpense(expense) {
        return this.save(this.STORES.EXPENSES, expense);
    },

    async getExpenses(projectId = null) {
        return this.getAll(this.STORES.EXPENSES, projectId ? 'project_id' : null, projectId);
    },

    // ==========================================
    // Sync Queue
    // ==========================================
    async addToSyncQueue(entity, action, data) {
        await this.init();
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction([this.STORES.SYNC_QUEUE], 'readwrite');
            const store = transaction.objectStore(this.STORES.SYNC_QUEUE);

            const syncItem = {
                entity: entity,
                action: action,
                data: data,
                created_at: new Date().toISOString(),
                status: 'pending',
                attempts: 0
            };

            const request = store.add(syncItem);

            request.onsuccess = () => {
                console.log('[LandSalesDB] Added to sync queue:', entity, action);
                this.updateSyncBadge();
                resolve(syncItem);
            };

            request.onerror = (event) => {
                reject(event.target.error);
            };
        });
    },

    async getPendingSyncItems() {
        await this.init();
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction([this.STORES.SYNC_QUEUE], 'readonly');
            const store = transaction.objectStore(this.STORES.SYNC_QUEUE);
            const index = store.index('status');
            const request = index.getAll('pending');

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

            request.onerror = (event) => {
                reject(event.target.error);
            };
        });
    },

    async getSyncQueueCount() {
        const items = await this.getPendingSyncItems();
        return items.length;
    },

    async removeSyncItem(id) {
        return this.delete(this.STORES.SYNC_QUEUE, id);
    },

    async updateSyncItem(id, updates) {
        await this.init();
        const item = await this.get(this.STORES.SYNC_QUEUE, id);
        if (item) {
            Object.assign(item, updates);
            return this.save(this.STORES.SYNC_QUEUE, item);
        }
    },

    // ==========================================
    // Cache
    // ==========================================
    async cacheData(key, data, ttlMinutes = 60) {
        await this.init();
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction([this.STORES.CACHE], 'readwrite');
            const store = transaction.objectStore(this.STORES.CACHE);

            const cacheItem = {
                key: key,
                data: data,
                cached_at: new Date().toISOString(),
                expires_at: new Date(Date.now() + ttlMinutes * 60 * 1000).toISOString()
            };

            const request = store.put(cacheItem);

            request.onsuccess = () => {
                resolve(cacheItem);
            };

            request.onerror = (event) => {
                reject(event.target.error);
            };
        });
    },

    async getCachedData(key) {
        await this.init();
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction([this.STORES.CACHE], 'readonly');
            const store = transaction.objectStore(this.STORES.CACHE);
            const request = store.get(key);

            request.onsuccess = () => {
                const item = request.result;
                if (item && new Date(item.expires_at) > new Date()) {
                    resolve(item.data);
                } else {
                    resolve(null);
                }
            };

            request.onerror = (event) => {
                reject(event.target.error);
            };
        });
    },

    async clearExpiredCache() {
        await this.init();
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction([this.STORES.CACHE], 'readwrite');
            const store = transaction.objectStore(this.STORES.CACHE);
            const request = store.getAll();

            request.onsuccess = () => {
                const items = request.result;
                const now = new Date();

                items.forEach(item => {
                    if (new Date(item.expires_at) < now) {
                        store.delete(item.key);
                    }
                });

                resolve(true);
            };

            request.onerror = (event) => {
                reject(event.target.error);
            };
        });
    },

    // ==========================================
    // Bulk operations
    // ==========================================
    async cacheAPIResponse(url, data) {
        const key = `api_${url.replace(/[^a-zA-Z0-9]/g, '_')}`;
        return this.cacheData(key, data, 5); // 5 minutes cache
    },

    async getCachedAPIResponse(url) {
        const key = `api_${url.replace(/[^a-zA-Z0-9]/g, '_')}`;
        return this.getCachedData(key);
    },

    // ==========================================
    // Status & UI
    // ==========================================
    async getSyncStatus() {
        const pendingItems = await this.getPendingSyncItems();
        return {
            pendingCount: pendingItems.length,
            isOnline: navigator.onLine,
            lastSync: localStorage.getItem('landsales_last_sync') || null,
            hasPendingChanges: pendingItems.length > 0
        };
    },

    updateSyncBadge() {
        this.getSyncQueueCount().then(count => {
            const badge = document.getElementById('sync-badge');
            if (badge) {
                if (count > 0) {
                    badge.textContent = count;
                    badge.style.display = 'inline-block';
                } else {
                    badge.style.display = 'none';
                }
            }
        });
    },

    // ==========================================
    // Clear all data
    // ==========================================
    async clearAll() {
        await this.init();
        const stores = Object.values(this.STORES);

        return Promise.all(stores.map(storeName => {
            return new Promise((resolve, reject) => {
                const transaction = this.db.transaction([storeName], 'readwrite');
                const store = transaction.objectStore(storeName);
                const request = store.clear();

                request.onsuccess = () => resolve(true);
                request.onerror = (event) => reject(event.target.error);
            });
        }));
    }
};

/**
 * LandSalesPro Sync Manager
 * Handles synchronization of offline data with server
 */
const LandSalesSyncManager = {
    isSyncing: false,
    syncInterval: null,

    /**
     * Start automatic sync
     */
    startAutoSync(intervalMs = 30000) {
        if (this.syncInterval) {
            clearInterval(this.syncInterval);
        }

        this.syncInterval = setInterval(() => {
            if (navigator.onLine && !this.isSyncing) {
                this.syncAll();
            }
        }, intervalMs);

        console.log('[SyncManager] Auto-sync started');
    },

    /**
     * Stop automatic sync
     */
    stopAutoSync() {
        if (this.syncInterval) {
            clearInterval(this.syncInterval);
            this.syncInterval = null;
        }
    },

    /**
     * Sync all pending changes
     */
    async syncAll() {
        if (this.isSyncing || !navigator.onLine) {
            return { success: false, reason: this.isSyncing ? 'already_syncing' : 'offline' };
        }

        this.isSyncing = true;
        this.showSyncingIndicator();

        try {
            const pendingItems = await LandSalesOfflineDB.getPendingSyncItems();
            const results = { synced: 0, failed: 0, errors: [] };

            for (const item of pendingItems) {
                try {
                    await this.syncItem(item);
                    await LandSalesOfflineDB.removeSyncItem(item.id);
                    results.synced++;
                } catch (error) {
                    console.error('[SyncManager] Sync failed:', item, error);
                    results.failed++;
                    results.errors.push({ item: item.entity, error: error.message });

                    // Update retry count
                    await LandSalesOfflineDB.updateSyncItem(item.id, {
                        attempts: (item.attempts || 0) + 1,
                        last_error: error.message,
                        last_attempt: new Date().toISOString()
                    });
                }
            }

            localStorage.setItem('landsales_last_sync', new Date().toISOString());
            this.hideSyncingIndicator(results);
            LandSalesOfflineDB.updateSyncBadge();

            console.log('[SyncManager] Sync complete:', results);
            return { success: true, results };

        } catch (error) {
            console.error('[SyncManager] Sync error:', error);
            this.hideSyncingIndicator({ failed: 1 });
            return { success: false, error: error.message };
        } finally {
            this.isSyncing = false;
        }
    },

    /**
     * Sync a single item
     */
    async syncItem(item) {
        const endpoints = {
            'projects': '/dashboard/ajax/sync_project.php',
            'plots': '/dashboard/ajax/sync_plot.php',
            'buyers': '/dashboard/ajax/sync_buyer.php',
            'sales': '/dashboard/ajax/sync_sale.php',
            'payments': '/dashboard/ajax/sync_payment.php',
            'agents': '/dashboard/ajax/sync_agent.php',
            'expenses': '/dashboard/ajax/sync_expense.php'
        };

        const endpoint = endpoints[item.entity];
        if (!endpoint) {
            throw new Error(`Unknown entity type: ${item.entity}`);
        }

        const response = await fetch(endpoint, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-Requested-With': 'XMLHttpRequest'
            },
            body: JSON.stringify({
                action: item.action,
                data: item.data
            })
        });

        if (!response.ok) {
            throw new Error(`Server error: ${response.status}`);
        }

        const result = await response.json();

        if (!result.success) {
            throw new Error(result.message || 'Sync failed');
        }

        // Update local record with server ID if needed
        if (result.id && item.data.is_local) {
            await this.updateLocalWithServerId(item.entity, item.data.id, result.id);
        }

        return result;
    },

    /**
     * Update local record with server ID
     */
    async updateLocalWithServerId(entity, localId, serverId) {
        const record = await LandSalesOfflineDB.get(entity, localId);
        if (record) {
            record.server_id = serverId;
            record.is_local = false;
            record.synced = true;
            await LandSalesOfflineDB.save(entity, record);
        }
    },

    /**
     * Show syncing indicator
     */
    showSyncingIndicator() {
        const indicator = document.getElementById('sync-indicator');
        if (indicator) {
            indicator.classList.add('syncing');
            indicator.innerHTML = '<i class="fas fa-sync fa-spin"></i> Syncing...';
        }
    },

    /**
     * Hide syncing indicator
     */
    hideSyncingIndicator(results) {
        const indicator = document.getElementById('sync-indicator');
        if (indicator) {
            indicator.classList.remove('syncing');
            if (results.failed > 0) {
                indicator.innerHTML = '<i class="fas fa-exclamation-triangle text-warning"></i> Sync incomplete';
            } else {
                indicator.innerHTML = '<i class="fas fa-check text-success"></i> Synced';
                setTimeout(() => {
                    if (navigator.onLine) {
                        indicator.innerHTML = '<i class="fas fa-cloud"></i> Online';
                    }
                }, 3000);
            }
        }
    },

    /**
     * Register background sync
     */
    async registerBackgroundSync() {
        if ('serviceWorker' in navigator && 'SyncManager' in window) {
            try {
                const registration = await navigator.serviceWorker.ready;
                await registration.sync.register('sync-data');
                console.log('[SyncManager] Background sync registered');
            } catch (error) {
                console.log('[SyncManager] Background sync not available:', error);
            }
        }
    }
};

// Initialize on page load
document.addEventListener('DOMContentLoaded', async () => {
    try {
        await LandSalesOfflineDB.init();
        await LandSalesOfflineDB.clearExpiredCache();
        await LandSalesOfflineDB.updateSyncBadge();

        // Start auto-sync when online
        if (navigator.onLine) {
            LandSalesSyncManager.startAutoSync();
        }

        console.log('[LandSalesDB] Offline database ready');
    } catch (error) {
        console.error('[LandSalesDB] Initialization error:', error);
    }
});

// Handle online/offline events
window.addEventListener('online', async () => {
    console.log('[LandSalesDB] Connection restored');
    const indicator = document.getElementById('sync-indicator');
    if (indicator) {
        indicator.innerHTML = '<i class="fas fa-cloud"></i> Online';
        indicator.classList.remove('offline');
    }

    // Sync pending changes
    await LandSalesSyncManager.syncAll();
    LandSalesSyncManager.startAutoSync();
});

window.addEventListener('offline', () => {
    console.log('[LandSalesDB] Connection lost');
    const indicator = document.getElementById('sync-indicator');
    if (indicator) {
        indicator.innerHTML = '<i class="fas fa-cloud-slash"></i> Offline';
        indicator.classList.add('offline');
    }

    LandSalesSyncManager.stopAutoSync();
});

// Listen for service worker messages
if ('serviceWorker' in navigator) {
    navigator.serviceWorker.addEventListener('message', async (event) => {
        switch (event.data.type) {
            case 'TRIGGER_SYNC':
                await LandSalesSyncManager.syncAll();
                break;

            case 'SYNC_SALES':
            case 'SYNC_PAYMENTS':
                await LandSalesSyncManager.syncAll();
                break;

            case 'OFFLINE_POST_REQUEST':
                // Store failed POST request for later
                await LandSalesOfflineDB.addToSyncQueue(
                    'request',
                    'post',
                    event.data.data
                );
                break;
        }
    });
}

// Export to window
window.LandSalesOfflineDB = LandSalesOfflineDB;
window.LandSalesSyncManager = LandSalesSyncManager;
