import React from 'react' import { SDKState } from './types' import { SDKApiClient, getSDKApiClient, resetSDKApiClient } from './api-client' import { StateSyncManager, createStateSyncManager, SyncState, SyncCallbacks } from './sync' import { ExtendedSDKAction } from './context-types' // ============================================================================= // SYNC CALLBACK BUILDER // ============================================================================= /** * Builds the SyncCallbacks object used by the StateSyncManager. * Keeps the provider component cleaner by extracting this factory. */ export function buildSyncCallbacks( setSyncState: React.Dispatch>, setIsOnline: React.Dispatch>, dispatch: React.Dispatch, stateRef: React.MutableRefObject ): SyncCallbacks { return { onSyncStart: () => { setSyncState(prev => ({ ...prev, status: 'syncing' })) }, onSyncComplete: (syncedState) => { setSyncState(prev => ({ ...prev, status: 'idle', lastSyncedAt: new Date(), pendingChanges: 0, })) if (syncedState.lastModified > stateRef.current.lastModified) { dispatch({ type: 'SET_STATE', payload: syncedState }) } }, onSyncError: (error) => { setSyncState(prev => ({ ...prev, status: 'error', error: error.message, })) }, onConflict: () => { setSyncState(prev => ({ ...prev, status: 'conflict' })) }, onOffline: () => { setIsOnline(false) setSyncState(prev => ({ ...prev, status: 'offline' })) }, onOnline: () => { setIsOnline(true) setSyncState(prev => ({ ...prev, status: 'idle' })) }, } } // ============================================================================= // INITIAL STATE LOADER // ============================================================================= /** * Loads SDK state from localStorage and optionally from the server, * dispatching SET_STATE as appropriate. */ export async function loadInitialState(params: { storageKey: string enableBackendSync: boolean projectId?: string syncManager: StateSyncManager | null apiClient: SDKApiClient | null dispatch: React.Dispatch }): Promise { const { storageKey, enableBackendSync, projectId, syncManager, apiClient, dispatch } = params // First, try loading from localStorage const stored = localStorage.getItem(storageKey) if (stored) { const parsed = JSON.parse(stored) if (parsed.lastModified) { parsed.lastModified = new Date(parsed.lastModified) } dispatch({ type: 'SET_STATE', payload: parsed }) } // Then, try loading from server if backend sync is enabled if (enableBackendSync && syncManager) { const serverState = await syncManager.loadFromServer() if (serverState) { const localTime = stored ? new Date(JSON.parse(stored).lastModified).getTime() : 0 const serverTime = new Date(serverState.lastModified).getTime() if (serverTime > localTime) { dispatch({ type: 'SET_STATE', payload: serverState }) } } } // Load project metadata (name, status, etc.) from backend if (enableBackendSync && projectId && apiClient) { try { const info = await apiClient.getProject(projectId) dispatch({ type: 'SET_STATE', payload: { projectInfo: info } }) } catch (err) { console.warn('Failed to load project info:', err) } } } // ============================================================================= // INIT / CLEANUP HELPERS // ============================================================================= export function initSyncInfra( enableBackendSync: boolean, tenantId: string, projectId: string | undefined, apiClientRef: React.MutableRefObject, syncManagerRef: React.MutableRefObject, callbacks: SyncCallbacks ): void { if (!enableBackendSync || typeof window === 'undefined') return apiClientRef.current = getSDKApiClient(tenantId, projectId) syncManagerRef.current = createStateSyncManager( apiClientRef.current, tenantId, { debounceMs: 2000, maxRetries: 3 }, callbacks, projectId ) } export function cleanupSyncInfra( enableBackendSync: boolean, syncManagerRef: React.MutableRefObject, apiClientRef: React.MutableRefObject ): void { if (syncManagerRef.current) { syncManagerRef.current.destroy() syncManagerRef.current = null } if (enableBackendSync) { resetSDKApiClient() apiClientRef.current = null } }