// lib/services/marine_tarball_sampling_service.dart import 'dart:io'; import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:path/path.dart' as p; import 'package:environment_monitoring_app/models/tarball_data.dart'; import 'package:environment_monitoring_app/services/local_storage_service.dart'; import 'package:environment_monitoring_app/services/server_config_service.dart'; import 'package:environment_monitoring_app/services/zipping_service.dart'; import 'package:environment_monitoring_app/services/api_service.dart'; import 'package:environment_monitoring_app/services/submission_api_service.dart'; import 'package:environment_monitoring_app/services/submission_ftp_service.dart'; import 'package:environment_monitoring_app/services/telegram_service.dart'; /// A dedicated service to handle all business logic for the Marine Tarball Sampling feature. class MarineTarballSamplingService { final SubmissionApiService _submissionApiService = SubmissionApiService(); final SubmissionFtpService _submissionFtpService = SubmissionFtpService(); final ZippingService _zippingService = ZippingService(); final LocalStorageService _localStorageService = LocalStorageService(); final ServerConfigService _serverConfigService = ServerConfigService(); final DatabaseHelper _dbHelper = DatabaseHelper(); // MODIFIED: Declare the service, but do not initialize it here. final TelegramService _telegramService; // ADDED: A constructor to accept the global TelegramService instance. MarineTarballSamplingService(this._telegramService); Future> submitTarballSample({ required TarballSamplingData data, required List>? appSettings, }) async { const String moduleName = 'marine_tarball'; final serverName = (await _serverConfigService.getActiveApiConfig())?['config_name'] as String? ?? 'Default'; final imageFilesWithNulls = data.toImageFiles(); imageFilesWithNulls.removeWhere((key, value) => value == null); final Map finalImageFiles = imageFilesWithNulls.cast(); // START CHANGE: Revert to the correct two-step API submission process // --- Step 1A: API Data Submission --- debugPrint("Step 1A: Submitting Tarball form data..."); final apiDataResult = await _submissionApiService.submitPost( moduleName: moduleName, endpoint: 'marine/tarball/sample', body: data.toFormData(), ); if (apiDataResult['success'] != true) { // If the initial data submission fails, log and exit early. await _logAndSave(data: data, status: 'L1', message: apiDataResult['message']!, apiResults: [apiDataResult], ftpStatuses: [], serverName: serverName, finalImageFiles: finalImageFiles); return {'success': false, 'message': apiDataResult['message']}; } final recordId = apiDataResult['data']?['autoid']?.toString(); if (recordId == null) { await _logAndSave(data: data, status: 'L1', message: 'API Error: Missing record ID.', apiResults: [apiDataResult], ftpStatuses: [], serverName: serverName, finalImageFiles: finalImageFiles); return {'success': false, 'message': 'API Error: Missing record ID.'}; } data.reportId = recordId; // --- Step 1B: API Image Submission --- debugPrint("Step 1B: Submitting Tarball images..."); final apiImageResult = await _submissionApiService.submitMultipart( moduleName: moduleName, endpoint: 'marine/tarball/images', fields: {'autoid': recordId}, files: finalImageFiles, ); final bool apiSuccess = apiImageResult['success'] == true; // END CHANGE // --- Step 2: FTP Submission --- final stationCode = data.selectedStation?['tbl_station_code'] ?? 'NA'; final fileTimestamp = "${data.samplingDate}_${data.samplingTime}".replaceAll(':', '-').replaceAll(' ', '_'); final baseFileName = '${stationCode}_$fileTimestamp'; final Directory? logDirectory = await _localStorageService.getLogDirectory( serverName: serverName, module: 'marine', subModule: 'marine_tarball_sampling', ); final Directory? localSubmissionDir = logDirectory != null ? Directory(p.join(logDirectory.path, data.reportId ?? baseFileName)) : null; if (localSubmissionDir != null && !await localSubmissionDir.exists()) { await localSubmissionDir.create(recursive: true); } final dataZip = await _zippingService.createDataZip( jsonDataMap: {'data.json': jsonEncode(data.toDbJson())}, baseFileName: baseFileName, destinationDir: localSubmissionDir, ); Map ftpDataResult = {'success': true, 'statuses': []}; if (dataZip != null) { ftpDataResult = await _submissionFtpService.submit( moduleName: moduleName, fileToUpload: dataZip, remotePath: '/${p.basename(dataZip.path)}', ); } final imageZip = await _zippingService.createImageZip( imageFiles: finalImageFiles.values.toList(), baseFileName: baseFileName, destinationDir: localSubmissionDir, ); Map ftpImageResult = {'success': true, 'statuses': []}; if (imageZip != null) { ftpImageResult = await _submissionFtpService.submit( moduleName: moduleName, fileToUpload: imageZip, remotePath: '/${p.basename(imageZip.path)}', ); } final bool ftpSuccess = (ftpDataResult['success'] == true && ftpImageResult['success'] == true); // --- Step 3: Finalize and Log --- String finalStatus; String finalMessage; if (apiSuccess) { finalStatus = ftpSuccess ? 'S4' : 'S3'; finalMessage = ftpSuccess ? 'Data submitted successfully.' : 'Data sent to API. FTP upload failed/queued.'; } else { finalStatus = ftpSuccess ? 'L4' : 'L1'; finalMessage = ftpSuccess ? 'API failed, but files sent to FTP.' : 'All submission attempts failed.'; } await _logAndSave( data: data, status: finalStatus, message: finalMessage, apiResults: [apiDataResult, apiImageResult], // Log results from both API steps ftpStatuses: [...ftpDataResult['statuses'], ...ftpImageResult['statuses']], serverName: serverName, finalImageFiles: finalImageFiles ); if (apiSuccess || ftpSuccess) { _handleTarballSuccessAlert(data, appSettings, isDataOnly: !apiSuccess); } return {'success': apiSuccess || ftpSuccess, 'message': finalMessage, 'reportId': data.reportId}; } // Added a helper to reduce code duplication in the main submit method Future _logAndSave({ required TarballSamplingData data, required String status, required String message, required List> apiResults, required List> ftpStatuses, required String serverName, required Map finalImageFiles, }) async { data.submissionStatus = status; data.submissionMessage = message; final fileTimestamp = "${data.samplingDate}_${data.samplingTime}".replaceAll(':', '-').replaceAll(' ', '_'); await _localStorageService.saveTarballSamplingData(data, serverName: serverName); final logData = { 'submission_id': data.reportId ?? fileTimestamp, 'module': 'marine', 'type': 'Tarball', 'status': status, 'message': message, 'report_id': data.reportId, 'created_at': DateTime.now().toIso8601String(), 'form_data': jsonEncode(data.toDbJson()), 'image_data': jsonEncode(finalImageFiles.values.map((f) => f.path).toList()), 'server_name': serverName, 'api_status': jsonEncode(apiResults), 'ftp_status': jsonEncode(ftpStatuses), }; await _dbHelper.saveSubmissionLog(logData); } Future _handleTarballSuccessAlert(TarballSamplingData data, List>? appSettings, {required bool isDataOnly}) async { try { final message = data.generateTelegramAlertMessage(isDataOnly: isDataOnly); final bool wasSent = await _telegramService.sendAlertImmediately('marine_tarball', message, appSettings); if (!wasSent) { await _telegramService.queueMessage('marine_tarball', message, appSettings); } } catch (e) { debugPrint("Failed to handle Tarball Telegram alert: $e"); } } }