MotorSignal-App/lib/services/sync/cloud_restore_controller.dart

137 lines
4.2 KiB
Dart

// ignore_for_file: prefer_initializing_formals
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../auth/auth_service.dart';
import 'cloud_restore_service.dart';
enum CloudRestoreStatus {
idle,
restoring,
success,
partialFailure,
authRequired,
unavailable,
failure,
}
class CloudRestoreUiState {
final CloudRestoreStatus status;
final CloudRestoreResult? result;
final String? message;
const CloudRestoreUiState({
this.status = CloudRestoreStatus.idle,
this.result,
this.message,
});
bool get isRestoring => status == CloudRestoreStatus.restoring;
}
class CloudRestoreController extends StateNotifier<CloudRestoreUiState> {
final CloudRestoreService _restoreService;
final AuthService _authService;
CloudRestoreController({
required CloudRestoreService restoreService,
required AuthService authService,
}) : _restoreService = restoreService,
_authService = authService,
super(const CloudRestoreUiState());
Future<CloudRestoreResult?> restoreFromMotorCloud() async {
if (state.isRestoring) return state.result;
final user = _authService.currentUser;
if (user == null || user.isAnonymous) {
state = const CloudRestoreUiState(
status: CloudRestoreStatus.authRequired,
message: 'Sign in with a MotorCloud account to restore ride history.',
);
return null;
}
if (!_authService.isConfigured) {
state = const CloudRestoreUiState(
status: CloudRestoreStatus.unavailable,
message:
'MotorCloud is unavailable. Try again when Firebase services are available.',
);
return null;
}
state = const CloudRestoreUiState(status: CloudRestoreStatus.restoring);
try {
final result = await _restoreService.restore();
final hasFailures = result.failures > 0;
final hasRestoredOrMatched =
result.hasAnyRestoredData ||
result.existingRidesMatched > 0 ||
result.telemetryAlreadyPresent > 0 ||
result.telemetrySkipped > 0;
final status = result.requiresAuthentication && !hasRestoredOrMatched
? CloudRestoreStatus.authRequired
: (hasFailures && !hasRestoredOrMatched
? CloudRestoreStatus.failure
: (hasFailures
? CloudRestoreStatus.partialFailure
: CloudRestoreStatus.success));
state = CloudRestoreUiState(
status: status,
result: result,
message: _formatResult(result),
);
return result;
} on StateError catch (e) {
final message = e.message;
state = CloudRestoreUiState(
status: message.toLowerCase().contains('auth')
? CloudRestoreStatus.authRequired
: CloudRestoreStatus.unavailable,
message: message,
);
return null;
} catch (e) {
state = CloudRestoreUiState(
status: CloudRestoreStatus.failure,
message: 'MotorCloud restore failed: $e',
);
return null;
}
}
String _formatResult(CloudRestoreResult result) {
final parts = <String>[];
if (result.metadataRowsAdded > 0) {
parts.add('${result.metadataRowsAdded} ride(s) added');
}
if (result.telemetrySamplesRestored > 0) {
parts.add(
'${result.telemetrySamplesRestored} telemetry sample(s) restored',
);
}
if (result.performanceRunsRestored > 0) {
parts.add(
'${result.performanceRunsRestored} performance run(s) restored',
);
}
if (result.rideEventsRestored > 0) {
parts.add('${result.rideEventsRestored} ride event(s) restored');
}
if (result.existingRidesMatched > 0 || result.telemetryAlreadyPresent > 0) {
final alreadyPresent =
result.existingRidesMatched > result.telemetryAlreadyPresent
? result.existingRidesMatched
: result.telemetryAlreadyPresent;
parts.add('$alreadyPresent ride(s) already on this device');
}
if (result.failures > 0) {
parts.add('${result.failures} item(s) need attention');
}
if (parts.isEmpty) {
return 'No restorable MotorCloud rides were found for this account.';
}
return 'MotorCloud restore complete: ${parts.join(', ')}.';
}
}