/**
 * Offline Storage Module using IndexedDB
 * Handles offline data storage and synchronization
 */

const OfflineStorage = {
    DB_NAME: 'LandSurveyDB',
    DB_VERSION: 1,
    db: null,

    // Store names
    STORES: {
        LANDS: 'lands',
        BEACON_POINTS: 'beacon_points',
        CLIENTS: 'clients',
        PENDING_SYNC: 'pending_sync',
        CACHE_DATA: 'cache_data'
    },

    /**
     * Initialize the IndexedDB 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('[OfflineStorage] Database error:', event.target.error);
                reject(event.target.error);
            };

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

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

                // Lands store
                if (!db.objectStoreNames.contains(this.STORES.LANDS)) {
                    const landsStore = db.createObjectStore(this.STORES.LANDS, { keyPath: 'id', autoIncrement: true });
                    landsStore.createIndex('server_id', 'server_id', { unique: false });
                    landsStore.createIndex('synced', 'synced', { unique: false });
                    landsStore.createIndex('user_id', 'user_id', { unique: false });
                }

                // Beacon points store
                if (!db.objectStoreNames.contains(this.STORES.BEACON_POINTS)) {
                    const pointsStore = db.createObjectStore(this.STORES.BEACON_POINTS, { keyPath: 'id', autoIncrement: true });
                    pointsStore.createIndex('land_id', 'land_id', { unique: false });
                    pointsStore.createIndex('synced', 'synced', { unique: false });
                }

                // Clients store
                if (!db.objectStoreNames.contains(this.STORES.CLIENTS)) {
                    const clientsStore = db.createObjectStore(this.STORES.CLIENTS, { keyPath: 'id', autoIncrement: true });
                    clientsStore.createIndex('server_id', 'server_id', { unique: false });
                    clientsStore.createIndex('synced', 'synced', { unique: false });
                }

                // Pending sync queue
                if (!db.objectStoreNames.contains(this.STORES.PENDING_SYNC)) {
                    const syncStore = db.createObjectStore(this.STORES.PENDING_SYNC, { keyPath: 'id', autoIncrement: true });
                    syncStore.createIndex('type', 'type', { unique: false });
                    syncStore.createIndex('created_at', 'created_at', { unique: false });
                }

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

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

    /**
     * Add or update a land record
     */
    async saveLand(land) {
        await this.init();
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction([this.STORES.LANDS], 'readwrite');
            const store = transaction.objectStore(this.STORES.LANDS);

            const data = {
                ...land,
                synced: navigator.onLine ? true : false,
                updated_at: new Date().toISOString(),
                local_id: land.id || `local_${Date.now()}`
            };

            if (land.id && typeof land.id === 'number') {
                data.server_id = land.id;
            }

            const request = store.put(data);

            request.onsuccess = () => {
                console.log('[OfflineStorage] Land saved:', data.local_id);

                // If offline, add to sync queue
                if (!navigator.onLine) {
                    this.addToSyncQueue('land', data);
                }

                resolve(data);
            };

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

    /**
     * Get all lands from local storage
     */
    async getLands(userId = null) {
        await this.init();
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction([this.STORES.LANDS], 'readonly');
            const store = transaction.objectStore(this.STORES.LANDS);

            let request;
            if (userId) {
                const index = store.index('user_id');
                request = index.getAll(userId);
            } else {
                request = store.getAll();
            }

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

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

    /**
     * Get a single land by ID
     */
    async getLand(id) {
        await this.init();
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction([this.STORES.LANDS], 'readonly');
            const store = transaction.objectStore(this.STORES.LANDS);
            const request = store.get(id);

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

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

    /**
     * Save beacon points for a land
     */
    async saveBeaconPoints(landId, points) {
        await this.init();
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction([this.STORES.BEACON_POINTS], 'readwrite');
            const store = transaction.objectStore(this.STORES.BEACON_POINTS);

            const savedPoints = [];
            points.forEach((point, index) => {
                const data = {
                    ...point,
                    land_id: landId,
                    point_order: index + 1,
                    synced: navigator.onLine ? true : false,
                    local_id: point.id || `local_point_${Date.now()}_${index}`
                };

                store.put(data);
                savedPoints.push(data);

                if (!navigator.onLine) {
                    this.addToSyncQueue('beacon_point', data);
                }
            });

            transaction.oncomplete = () => {
                console.log('[OfflineStorage] Beacon points saved:', savedPoints.length);
                resolve(savedPoints);
            };

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

    /**
     * Get beacon points for a land
     */
    async getBeaconPoints(landId) {
        await this.init();
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction([this.STORES.BEACON_POINTS], 'readonly');
            const store = transaction.objectStore(this.STORES.BEACON_POINTS);
            const index = store.index('land_id');
            const request = index.getAll(landId);

            request.onsuccess = () => {
                const points = request.result.sort((a, b) => a.point_order - b.point_order);
                resolve(points);
            };

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

    /**
     * Save a client record
     */
    async saveClient(client) {
        await this.init();
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction([this.STORES.CLIENTS], 'readwrite');
            const store = transaction.objectStore(this.STORES.CLIENTS);

            const data = {
                ...client,
                synced: navigator.onLine ? true : false,
                updated_at: new Date().toISOString(),
                local_id: client.id || `local_client_${Date.now()}`
            };

            if (client.id && typeof client.id === 'number') {
                data.server_id = client.id;
            }

            const request = store.put(data);

            request.onsuccess = () => {
                console.log('[OfflineStorage] Client saved:', data.local_id);

                if (!navigator.onLine) {
                    this.addToSyncQueue('client', data);
                }

                resolve(data);
            };

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

    /**
     * Get all clients
     */
    async getClients() {
        await this.init();
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction([this.STORES.CLIENTS], 'readonly');
            const store = transaction.objectStore(this.STORES.CLIENTS);
            const request = store.getAll();

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

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

    /**
     * Add item to sync queue
     */
    async addToSyncQueue(type, data) {
        await this.init();
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction([this.STORES.PENDING_SYNC], 'readwrite');
            const store = transaction.objectStore(this.STORES.PENDING_SYNC);

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

            const request = store.add(syncItem);

            request.onsuccess = () => {
                console.log('[OfflineStorage] Added to sync queue:', type);
                resolve(syncItem);
            };

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

    /**
     * Get all pending sync items
     */
    async getPendingSyncItems() {
        await this.init();
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction([this.STORES.PENDING_SYNC], 'readonly');
            const store = transaction.objectStore(this.STORES.PENDING_SYNC);
            const request = store.getAll();

            request.onsuccess = () => {
                const items = request.result.filter(item => item.status === 'pending');
                resolve(items);
            };

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

    /**
     * Remove item from sync queue
     */
    async removeSyncItem(id) {
        await this.init();
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction([this.STORES.PENDING_SYNC], 'readwrite');
            const store = transaction.objectStore(this.STORES.PENDING_SYNC);
            const request = store.delete(id);

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

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

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

            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);
            };
        });
    },

    /**
     * Get cached data
     */
    async getCachedData(key) {
        await this.init();
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction([this.STORES.CACHE_DATA], 'readonly');
            const store = transaction.objectStore(this.STORES.CACHE_DATA);
            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);
            };
        });
    },

    /**
     * Clear expired cache
     */
    async clearExpiredCache() {
        await this.init();
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction([this.STORES.CACHE_DATA], 'readwrite');
            const store = transaction.objectStore(this.STORES.CACHE_DATA);
            const index = store.index('expires_at');
            const now = new Date().toISOString();

            const range = IDBKeyRange.upperBound(now);
            const request = index.openCursor(range);

            request.onsuccess = (event) => {
                const cursor = event.target.result;
                if (cursor) {
                    store.delete(cursor.primaryKey);
                    cursor.continue();
                }
            };

            transaction.oncomplete = () => {
                console.log('[OfflineStorage] Expired cache cleared');
                resolve(true);
            };

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

    /**
     * Get sync status
     */
    async getSyncStatus() {
        const pendingItems = await this.getPendingSyncItems();
        return {
            pendingCount: pendingItems.length,
            isOnline: navigator.onLine,
            lastSync: localStorage.getItem('lastSyncTime') || null,
            hasPendingChanges: pendingItems.length > 0
        };
    },

    /**
     * Clear all offline 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);
            });
        }));
    }
};

/**
 * Sync Manager - Handles synchronization with server
 */
const SyncManager = {
    BASE_URL: '/uhakikimadalali/api',
    isSyncing: false,

    /**
     * Sync all pending changes to server
     */
    async syncAll() {
        if (this.isSyncing || !navigator.onLine) {
            console.log('[SyncManager] Cannot sync:', this.isSyncing ? 'Already syncing' : 'Offline');
            return { success: false, reason: this.isSyncing ? 'syncing' : 'offline' };
        }

        this.isSyncing = true;
        console.log('[SyncManager] Starting sync...');

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

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

            localStorage.setItem('lastSyncTime', new Date().toISOString());
            console.log('[SyncManager] Sync complete:', results);

            // Update UI
            this.updateSyncUI(results);

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

    /**
     * Sync a single item
     */
    async syncItem(item) {
        const endpoints = {
            'land': '/sync_land.php',
            'beacon_point': '/sync_beacon_point.php',
            'client': '/sync_client.php'
        };

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

        const response = await fetch(this.BASE_URL + endpoint, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(item.data)
        });

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

        const result = await response.json();

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

        // Update local record with server ID
        if (result.server_id && item.data.local_id) {
            await this.updateLocalRecord(item.type, item.data.local_id, result.server_id);
        }

        return result;
    },

    /**
     * Update local record with server ID after sync
     */
    async updateLocalRecord(type, localId, serverId) {
        const stores = {
            'land': OfflineStorage.STORES.LANDS,
            'beacon_point': OfflineStorage.STORES.BEACON_POINTS,
            'client': OfflineStorage.STORES.CLIENTS
        };

        const storeName = stores[type];
        if (!storeName) return;

        await OfflineStorage.init();
        const transaction = OfflineStorage.db.transaction([storeName], 'readwrite');
        const store = transaction.objectStore(storeName);
        const index = store.index('synced');

        // Find and update the record
        const request = store.getAll();
        request.onsuccess = () => {
            const records = request.result;
            const record = records.find(r => r.local_id === localId);

            if (record) {
                record.server_id = serverId;
                record.synced = true;
                store.put(record);
            }
        };
    },

    /**
     * Update sync UI elements
     */
    updateSyncUI(results) {
        const syncIcon = document.querySelector('.sync-icon');
        const syncLabel = document.querySelector('.sync-label');
        const syncTime = document.getElementById('lastSyncTime');

        if (syncIcon && syncLabel) {
            if (results.failed === 0) {
                syncIcon.className = 'sync-icon synced';
                syncIcon.innerHTML = '<i class="fas fa-cloud-upload-alt"></i>';
                syncLabel.textContent = 'Data Synced';
            } else {
                syncIcon.className = 'sync-icon offline';
                syncIcon.innerHTML = '<i class="fas fa-exclamation-triangle"></i>';
                syncLabel.textContent = `${results.failed} items failed`;
            }
        }

        if (syncTime) {
            syncTime.textContent = 'Just now';
        }
    },

    /**
     * Register for background sync
     */
    async registerBackgroundSync() {
        if ('serviceWorker' in navigator && 'sync' in self.registration) {
            try {
                await self.registration.sync.register('sync-data');
                console.log('[SyncManager] Background sync registered');
            } catch (error) {
                console.log('[SyncManager] Background sync not supported');
            }
        }
    }
};

// Global sync function (called from sidebar)
async function syncOfflineData() {
    return await SyncManager.syncAll();
}

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

    // Check for pending sync items
    const status = await OfflineStorage.getSyncStatus();
    console.log('[OfflineStorage] Status:', status);

    // Auto-sync when coming online
    window.addEventListener('online', async () => {
        console.log('[OfflineStorage] Connection restored, starting sync...');
        await SyncManager.syncAll();
    });
});

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

        if (event.data.type === 'OFFLINE_POST_REQUEST') {
            // Store the request for later sync
            await OfflineStorage.addToSyncQueue('request', event.data.data);
        }
    });
}

// Export for use in other scripts
window.OfflineStorage = OfflineStorage;
window.SyncManager = SyncManager;
