// lib/services/retry_service.dart import 'dart:convert'; import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:environment_monitoring_app/services/api_service.dart'; import 'package:environment_monitoring_app/services/base_api_service.dart'; import 'package:environment_monitoring_app/services/ftp_service.dart'; /// A dedicated service to manage the queue of failed API and FTP requests /// for manual resubmission. class RetryService { // Use singleton instances to avoid re-creating services unnecessarily. final DatabaseHelper _dbHelper = DatabaseHelper(); final BaseApiService _baseApiService = BaseApiService(); final FtpService _ftpService = FtpService(); /// Adds a failed API request to the local database queue. Future addApiToQueue({ required String endpoint, required String method, // e.g., 'POST' or 'POST_MULTIPART' Map? body, Map? fields, Map? files, }) async { // We must convert File objects to their string paths before saving to JSON. final serializableFiles = files?.map((key, value) => MapEntry(key, value.path)); final payload = { 'method': method, 'body': body, 'fields': fields, 'files': serializableFiles, }; await _dbHelper.queueFailedRequest({ 'type': 'api', 'endpoint_or_path': endpoint, 'payload': jsonEncode(payload), 'timestamp': DateTime.now().toIso8601String(), 'status': 'pending', }); debugPrint("API request for endpoint '$endpoint' has been queued for retry."); } /// Adds a failed FTP upload to the local database queue. Future addFtpToQueue({ required String localFilePath, required String remotePath, }) async { final payload = {'localFilePath': localFilePath}; await _dbHelper.queueFailedRequest({ 'type': 'ftp', 'endpoint_or_path': remotePath, 'payload': jsonEncode(payload), 'timestamp': DateTime.now().toIso8601String(), 'status': 'pending', }); debugPrint("FTP upload for file '$localFilePath' has been queued for retry."); } /// Retrieves all tasks currently in the 'pending' state from the queue. Future>> getPendingTasks() { return _dbHelper.getPendingRequests(); } /// Attempts to re-execute a single failed task from the queue. /// Returns `true` on success, `false` on failure. Future retryTask(int taskId) async { final task = await _dbHelper.getRequestById(taskId); if (task == null) { debugPrint("Retry failed: Task with ID $taskId not found in the queue."); return false; } bool success = false; final payload = jsonDecode(task['payload'] as String); try { if (task['type'] == 'api') { final endpoint = task['endpoint_or_path'] as String; final method = payload['method'] as String; debugPrint("Retrying API task $taskId: $method to $endpoint"); Map result; if (method == 'POST_MULTIPART') { // Reconstruct fields and files from the stored payload final Map fields = Map.from(payload['fields'] ?? {}); final Map files = (payload['files'] as Map?) ?.map((key, value) => MapEntry(key, File(value as String))) ?? {}; result = await _baseApiService.postMultipart(endpoint: endpoint, fields: fields, files: files); } else { // Assume 'POST' final Map body = Map.from(payload['body'] ?? {}); result = await _baseApiService.post(endpoint, body); } success = result['success']; } else if (task['type'] == 'ftp') { final remotePath = task['endpoint_or_path'] as String; final localFile = File(payload['localFilePath'] as String); debugPrint("Retrying FTP task $taskId: Uploading ${localFile.path} to $remotePath"); // Ensure the file still exists before attempting to re-upload if (await localFile.exists()) { final result = await _ftpService.uploadFile(localFile, remotePath); // The FTP service already queues on failure, so we only care about success here. success = result['success']; } else { debugPrint("Retry failed for FTP task $taskId: Source file no longer exists at ${localFile.path}"); success = false; } } } catch (e) { debugPrint("A critical error occurred while retrying task $taskId: $e"); success = false; } if (success) { debugPrint("Task $taskId completed successfully. Removing from queue."); await _dbHelper.deleteRequestFromQueue(taskId); } else { debugPrint("Retry attempt for task $taskId failed. It will remain in the queue."); } return success; } }