import 'package:clock/clock.dart'; import 'package:relationship_saver/integrations/backend/backend_gateway.dart'; import 'package:relationship_saver/integrations/backend/models/backend_models.dart'; import 'package:uuid/uuid.dart'; /// Deterministic fake backend for local development and tests. class BackendGatewayFake implements BackendGateway { BackendGatewayFake({Clock? clock, Uuid? uuid}) : _clock = clock ?? const Clock(), _uuid = uuid ?? const Uuid(); final Clock _clock; final Uuid _uuid; static final DateTime _seedTime = DateTime.utc(2026, 1, 1, 8); @override Future acknowledgeSignal({ required String signalId, required SignalAction action, DateTime? at, }) async { return; } @override Future getDownloadUrl({required String fileId}) async { return DownloadUrl( downloadUrl: 'https://files.example.com/download/$fileId', expiresAt: _seedTime.add(const Duration(hours: 1)), ); } @override Future getSignalsFeed({ String? cursor, DateTime? since, int limit = 50, Set? personIds, }) async { final List seeded = [ SignalItem( id: 'sig-1', type: 'deal', title: 'Weekend coffee promo nearby', description: 'Save this for your next date idea.', personId: personIds?.isNotEmpty == true ? personIds!.first : 'person-1', createdAt: _seedTime, metadata: const {'merchant': 'Cafe Bloom'}, ), SignalItem( id: 'sig-2', type: 'trend', title: 'Books are trending in gift ideas', description: 'Reading-themed gifts up 24% this week.', personId: personIds?.isNotEmpty == true ? personIds!.first : 'person-1', createdAt: _seedTime.add(const Duration(hours: 3)), ), SignalItem( id: 'sig-3', type: 'recommendation', title: 'Try a sunset walk reminder', description: 'Low-effort ritual with high emotional impact.', personId: personIds?.isNotEmpty == true ? personIds!.first : 'person-2', createdAt: _seedTime.add(const Duration(hours: 6)), ), ]; return SignalsFeed( cursor: 'fake-cursor-1', items: seeded.take(limit).toList(), ); } @override Future initUpload({ required UploadPurpose purpose, required String contentType, required int sizeBytes, }) async { final String fileId = 'file-${_uuid.v4()}'; return UploadInitResult( uploadUrl: 'https://uploads.example.com/$fileId', fileId: fileId, expiresAt: _seedTime.add(const Duration(minutes: 15)), ); } @override Future me() async { return const UserProfile( id: 'user-fake', email: 'demo@example.com', displayName: 'Demo User', ); } @override Future pullChanges({String? cursor, int limit = 200}) async { return SyncPullResult( cursor: cursor ?? 'fake-pull-cursor-empty', changes: const [], ); } @override Future pushChanges({ required List changes, String? cursor, }) async { final DateTime now = _clock.now().toUtc(); final List accepted = changes .map( (ChangeEnvelope change) => MutationAck( clientMutationId: change.clientMutationId, serverMutationId: 'srv-${change.clientMutationId}', acceptedAt: now, ), ) .toList(growable: false); return SyncPushResult( cursor: 'fake-push-cursor-1', accepted: accepted, rejected: const [], ); } @override Future signIn(SignInRequest request) async { final DateTime now = _clock.now().toUtc(); return AuthSession( accessToken: 'fake-access-token', refreshToken: 'fake-refresh-token', expiresAt: now.add(const Duration(hours: 1)), user: UserProfile( id: 'user-fake', email: request.email ?? 'demo@example.com', displayName: 'Demo User', ), ); } @override Future signOut() async { return; } }