import { Measurement } from '../types' import { initDatabase, getAllMeasurementsForSync, upsertMeasurement, getPendingRecords, updateSyncStatus, removeMeasurement, } from '../database/db' import { createApi, ApiError } from './api' import * as SecureStore from 'expo-secure-store' const LAST_SYNC_KEY = 'weegschaal_last_sync' type SyncResult = { pushed: number deleted: number pulled: number errors: string[] } export function mergeMeasurements(local: Measurement[], remote: any[]): Measurement[] { const localMap = new Map(local.map(m => [m.id, m])) const resultMap = new Map(local.map(m => [m.id, m])) for (const r of remote) { const localRec = localMap.get(r.id) const isDeleted = !!(r as any).deleted const localPending = localRec?.syncStatus === 'pending' || localRec?.syncStatus === 'pending-delete' if (isDeleted) { if (!localRec || localPending) continue if (new Date(r.updatedAt) > new Date(localRec.updatedAt)) { resultMap.set(r.id, { ...r, syncStatus: 'synced', deletedAt: (r as any).deletedAt || r.updatedAt, } as Measurement) } continue } if (localPending) continue if (localRec) { if (new Date(r.updatedAt) > new Date(localRec.updatedAt)) { resultMap.set(r.id, { ...r, syncStatus: 'synced' } as Measurement) } } else { resultMap.set(r.id, { ...r, syncStatus: 'synced' } as Measurement) } } return Array.from(resultMap.values()) } export async function uploadMeasurement(m: Measurement): Promise { const { getApiConfig } = await import('../screens/SettingsScreen') const config = await getApiConfig() if (!config) throw new Error('Geen API configuratie. Stel deze eerst in in Instellingen.') const api = createApi(config) await api.createMeasurement(m) await updateSyncStatus(m.id, 'synced') } export async function syncAll(): Promise { const { getApiConfig } = await import('../screens/SettingsScreen') const config = await getApiConfig() if (!config) throw new Error('Geen API configuratie. Stel deze eerst in in Instellingen.') const api = createApi(config) await initDatabase() const errors: string[] = [] let pushed = 0 let deleted = 0 let pulled = 0 // === PUSH phase — outbox first === const pending = await getPendingRecords() for (const m of pending) { if (m.deletedAt) { try { await api.deleteMeasurement(m.id) await removeMeasurement(m.id) deleted++ } catch (e: any) { if (e instanceof ApiError && e.status === 404) { await removeMeasurement(m.id) deleted++ } else { errors.push(e?.message ?? 'Delete failed') } } } else { try { await api.createMeasurement(m) await updateSyncStatus(m.id, 'synced') pushed++ } catch (e: any) { errors.push(e?.message ?? 'Create failed') } } } // === PULL phase — fetch remote changes === const lastSyncAt = await SecureStore.getItemAsync(LAST_SYNC_KEY) const remote = await api.getMeasurements(lastSyncAt || undefined) if (remote.length > 0) { const local = await getAllMeasurementsForSync() const localMap = new Map(local.map(m => [m.id, m])) for (const r of remote) { const localRec = localMap.get(r.id) const isDeleted = !!(r as any).deleted if (isDeleted) { if (localRec) { if ( localRec.syncStatus === 'pending' || localRec.syncStatus === 'pending-delete' ) { continue } await removeMeasurement(r.id) deleted++ } continue } const localIsPending = localRec?.syncStatus === 'pending' || localRec?.syncStatus === 'pending-delete' if (localIsPending) { continue } if (localRec) { if (new Date(r.updatedAt) > new Date(localRec.updatedAt)) { const record = { ...r, deletedAt: (r as any).deletedAt || undefined } as Measurement await upsertMeasurement(record) await updateSyncStatus(r.id, 'synced') pulled++ } continue } const record = { ...r, deletedAt: (r as any).deletedAt || undefined } as Measurement await upsertMeasurement(record) await updateSyncStatus(r.id, 'synced') pulled++ } } await SecureStore.setItemAsync(LAST_SYNC_KEY, new Date().toISOString()) return { pushed, deleted, pulled, errors } }