import { BibleHighlight, HighlightSyncQueueItem } from '@/types' import { initHighlightsDatabase, updateHighlight, getHighlight } from './highlight-manager' const SYNC_QUEUE_STORE = 'highlight_sync_queue' export class HighlightSyncManager { private db: IDBDatabase | null = null private syncInterval: NodeJS.Timeout | null = null async init() { this.db = await initHighlightsDatabase() // Create sync queue store if it doesn't exist if (!this.db.objectStoreNames.contains(SYNC_QUEUE_STORE)) { // Note: In real app, this would be done in onupgradeneeded // For this implementation, assume schema is managed separately } } async queueHighlight(highlight: BibleHighlight): Promise { if (!this.db) await this.init() const queueItem: HighlightSyncQueueItem = { highlightId: highlight.id, action: highlight.syncStatus === 'synced' ? 'update' : 'create', highlight, retryCount: 0 } await updateHighlight({ ...highlight, syncStatus: 'pending' }) } async getPendingSyncItems(): Promise { if (!this.db) await this.init() return new Promise((resolve, reject) => { const tx = this.db!.transaction('highlights', 'readonly') const store = tx.objectStore('highlights') const index = store.index('syncStatus') const request = index.getAll('pending') request.onsuccess = () => resolve(request.result || []) request.onerror = () => reject(new Error('Failed to get pending items')) }) } async getSyncingItems(): Promise { if (!this.db) await this.init() return new Promise((resolve, reject) => { const tx = this.db!.transaction('highlights', 'readonly') const store = tx.objectStore('highlights') const index = store.index('syncStatus') const request = index.getAll('syncing') request.onsuccess = () => resolve(request.result || []) request.onerror = () => reject(new Error('Failed to get syncing items')) }) } async markSyncing(highlightIds: string[]): Promise { if (!this.db) await this.init() for (const id of highlightIds) { const highlight = await getHighlight(id) if (highlight) { await updateHighlight({ ...highlight, syncStatus: 'syncing' }) } } } async markSynced(highlightIds: string[]): Promise { if (!this.db) await this.init() for (const id of highlightIds) { const highlight = await getHighlight(id) if (highlight) { await updateHighlight({ ...highlight, syncStatus: 'synced' }) } } } async markError(highlightIds: string[], errorMsg: string): Promise { if (!this.db) await this.init() for (const id of highlightIds) { const highlight = await getHighlight(id) if (highlight) { await updateHighlight({ ...highlight, syncStatus: 'error', syncErrorMsg: errorMsg }) } } } async performSync(): Promise<{ synced: number; errors: number }> { if (!this.db) await this.init() try { const pending = await this.getPendingSyncItems() if (pending.length === 0) return { synced: 0, errors: 0 } // Mark as syncing await this.markSyncing(pending.map(h => h.id)) // POST to backend const response = await fetch('/api/highlights/bulk', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ highlights: pending }) }) if (!response.ok) { // Mark all as error const errorIds = pending.map(h => h.id) await this.markError(errorIds, `HTTP ${response.status}`) return { synced: 0, errors: pending.length } } const result = await response.json() // Mark successfully synced items if (result.synced > 0) { const syncedIds = pending .filter(h => !result.errors.some((e: any) => e.verseId === h.verseId)) .map(h => h.id) await this.markSynced(syncedIds) } // Mark errored items if (result.errors && result.errors.length > 0) { for (const error of result.errors) { const h = pending.find(item => item.verseId === error.verseId) if (h) { await this.markError([h.id], error.error) } } } return { synced: result.synced, errors: result.errors?.length || 0 } } catch (error) { console.error('Sync failed:', error) const pending = await this.getPendingSyncItems() if (pending.length > 0) { await this.markError( pending.map(h => h.id), 'Network error' ) } return { synced: 0, errors: pending.length } } } startAutoSync(intervalMs: number = 30000, onSyncNeeded?: (result: { synced: number; errors: number }) => void) { this.syncInterval = setInterval(async () => { const result = await this.performSync() if (result.synced > 0 || result.errors > 0) { onSyncNeeded?.(result) } }, intervalMs) } stopAutoSync() { if (this.syncInterval) { clearInterval(this.syncInterval) this.syncInterval = null } } }