import 'package:flutter/widgets.dart'; import 'package:provider/provider.dart'; import '../../connection/connection.dart'; import '../../connection/connection_registry.dart'; import '../../database/app_database.dart'; import '../../profiles/active_profile_provider.dart'; import '../../profiles/profile.dart'; import '../../profiles/profile_connection.dart'; import '../../profiles/profile_connection_registry.dart'; import '../../profiles/profile_registry.dart'; import '../../services/storage_service.dart'; import '../../utils/app_logger.dart'; /// Durably provision a freshly-authenticated [connection] and its optional /// [bindToProfile] ownership row. /// /// [firstRunProfile], [connection], and [bindToProfile] are committed in one /// shared database transaction. The new profile is activated only after that /// relational commit. If activation rejects or throws, the relational bundle /// and the exact prior active-profile marker are restored before the original /// error is rethrown. /// /// All durable collaborators are captured before the first await, so a route /// unmount cannot interrupt the command between artifacts. Runtime pickup is /// left to the active-profile binder on the next switch or rebind. The helper /// itself does not navigate. Future persistAndBindConnection({ required BuildContext context, required Connection connection, required ProfileConnection? bindToProfile, Profile? firstRunProfile, }) async { final db = context.read(); final profiles = context.read(); final connections = context.read(); final profileConnections = context.read(); final activeProfiles = context.read(); final storage = context.read(); final priorActiveProfileId = storage.getActiveProfileId(); final priorConnection = await connections.get(connection.id); await db.runIdentityMutation( () => db.transaction(() async { if (firstRunProfile != null) { await profiles.upsert(firstRunProfile); } await connections.upsert(connection); if (bindToProfile != null) { await profileConnections.upsert(bindToProfile); } }), ); if (firstRunProfile != null) { try { final activated = await activeProfiles.activate(firstRunProfile); if (!activated) { throw StateError('The first-run profile could not be activated'); } } catch (error, stackTrace) { await _compensateFailedActivation( db: db, profiles: profiles, connections: connections, profileConnections: profileConnections, activeProfiles: activeProfiles, storage: storage, firstRunProfile: firstRunProfile, bindToProfile: bindToProfile, attemptedConnection: connection, priorConnection: priorConnection, priorActiveProfileId: priorActiveProfileId, ); Error.throwWithStackTrace(error, stackTrace); } } } Future _compensateFailedActivation({ required AppDatabase db, required ProfileRegistry profiles, required ConnectionRegistry connections, required ProfileConnectionRegistry profileConnections, required ActiveProfileProvider activeProfiles, required StorageService storage, required Profile firstRunProfile, required ProfileConnection? bindToProfile, required Connection attemptedConnection, required Connection? priorConnection, required String? priorActiveProfileId, }) async { try { await db.runIdentityMutation( () => db.transaction(() async { if (bindToProfile != null) { await profileConnections.remove(bindToProfile.profileId, bindToProfile.connectionId); } await profiles.remove(firstRunProfile.id); if (priorConnection == null) { await connections.remove(attemptedConnection.id); } else { await connections.upsert(priorConnection); } }), ); } catch (error, stackTrace) { appLogger.e('First-run relational compensation failed', error: error, stackTrace: stackTrace); } try { await storage.clearProfileLastUsed(firstRunProfile.id); } catch (error, stackTrace) { appLogger.e('First-run recency compensation failed', error: error, stackTrace: stackTrace); } try { if (priorActiveProfileId == null) { await storage.clearActiveProfileId(); } else { await storage.setActiveProfileId(priorActiveProfileId); } } catch (error, stackTrace) { appLogger.e('First-run active marker compensation failed', error: error, stackTrace: stackTrace); } try { await activeProfiles.reloadFromStorage(); } catch (error, stackTrace) { appLogger.e('First-run active profile reload failed', error: error, stackTrace: stackTrace); } }