// lib/services/marine_investigative_sampling_service.dart

import 'dart:async';
import 'dart:io';
import 'dart:convert';
import 'package:flutter/material.dart'; // Needed for debugPrint and BuildContext
import 'package:flutter/services.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as p;
import 'package:image/image.dart' as img;
import 'package:geolocator/geolocator.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
import 'package:usb_serial/usb_serial.dart';
import 'package:connectivity_plus/connectivity_plus.dart';

import '../auth_provider.dart';
import 'location_service.dart';
import '../models/marine_inves_manual_sampling_data.dart';
import '../bluetooth/bluetooth_manager.dart';
import '../serial/serial_manager.dart';
import 'local_storage_service.dart';
import 'server_config_service.dart';
import 'zipping_service.dart';
import 'submission_api_service.dart';
import 'submission_ftp_service.dart';
import 'telegram_service.dart';
import 'retry_service.dart';
import 'base_api_service.dart'; // Import for SessionExpiredException
import 'api_service.dart'; // Import for DatabaseHelper
import 'package:environment_monitoring_app/services/database_helper.dart';


/// A dedicated service for the Marine Investigative Sampling feature.
class MarineInvestigativeSamplingService {
  // Business Logic Services
  final LocationService _locationService = LocationService();
  final BluetoothManager _bluetoothManager = BluetoothManager();
  final SerialManager _serialManager = SerialManager();

  // Submission & Utility Services
  final SubmissionApiService _submissionApiService = SubmissionApiService();
  final SubmissionFtpService _submissionFtpService = SubmissionFtpService();
  final ZippingService _zippingService = ZippingService();
  final LocalStorageService _localStorageService = LocalStorageService();
  final ServerConfigService _serverConfigService = ServerConfigService();
  final DatabaseHelper _dbHelper = DatabaseHelper();
  final RetryService _retryService = RetryService();
  final TelegramService _telegramService;

  MarineInvestigativeSamplingService(this._telegramService);

  static const platform = MethodChannel('com.example.environment_monitoring_app/usb');

  // --- Location Services ---
  Future<Position> getCurrentLocation() => _locationService.getCurrentLocation();
  double calculateDistance(double lat1, double lon1, double lat2, double lon2) => _locationService.calculateDistance(lat1, lon1, lat2, lon2);

  // --- Image Processing ---
  Future<File?> pickAndProcessImage(ImageSource source, {
    required MarineInvesManualSamplingData data,
    required String imageInfo,
    bool isRequired = false,
  }) async {
    final picker = ImagePicker();
    final XFile? photo = await picker.pickImage(source: source, imageQuality: 85, maxWidth: 1024);
    if (photo == null) return null;

    final bytes = await photo.readAsBytes();
    img.Image? originalImage = img.decodeImage(bytes);
    if (originalImage == null) return null;

    if (isRequired && originalImage.height > originalImage.width) {
      debugPrint("Image rejected: Must be in landscape orientation.");
      return null;
    }

    final String watermarkTimestamp = "${data.samplingDate} ${data.samplingTime}";
    final font = img.arial24;
    final textWidth = watermarkTimestamp.length * 12; // Approximate width calculation
    // Ensure overlay box fits the text
    img.fillRect(originalImage, x1: 5, y1: 5, x2: textWidth + 15, y2: 35, color: img.ColorRgb8(255, 255, 255));
    img.drawString(originalImage, watermarkTimestamp, font: font, x: 10, y: 10, color: img.ColorRgb8(0, 0, 0));

    final tempDir = await getTemporaryDirectory();

    String stationCode = 'NA';
    if (data.stationTypeSelection == 'Existing Manual Station') {
      stationCode = data.selectedStation?['man_station_code'] ?? 'MANUAL_NA';
    } else if (data.stationTypeSelection == 'Existing Tarball Station') {
      stationCode = data.selectedTarballStation?['tbl_station_code'] ?? 'TARBALL_NA';
    } else if (data.stationTypeSelection == 'New Location') {
      stationCode = data.newStationCode ?? 'NEW_NA';
    }

    final fileTimestamp = "${data.samplingDate}-${data.samplingTime}".replaceAll(':', '-');
    final newFileName = "${stationCode}_${fileTimestamp}_${imageInfo.replaceAll(' ', '')}.jpg";
    final filePath = p.join(tempDir.path, newFileName);

    File processedFile = File(filePath);
    await processedFile.writeAsBytes(img.encodeJpg(originalImage));
    return processedFile;
  }


  // --- Device Connection (Delegated to Managers) ---
  ValueNotifier<BluetoothConnectionState> get bluetoothConnectionState => _bluetoothManager.connectionState;
  ValueNotifier<SerialConnectionState> get serialConnectionState => _serialManager.connectionState;
  ValueNotifier<String?> get sondeId => _bluetoothManager.connectionState.value != BluetoothConnectionState.disconnected ? _bluetoothManager.sondeId : _serialManager.sondeId;
  Stream<Map<String, double>> get bluetoothDataStream => _bluetoothManager.dataStream;
  Stream<Map<String, double>> get serialDataStream => _serialManager.dataStream;
  String? get connectedBluetoothDeviceName => _bluetoothManager.connectedDeviceName.value;
  String? get connectedSerialDeviceName => _serialManager.connectedDeviceName.value;

  // --- Permissions ---
  Future<bool> requestDevicePermissions() async {
    Map<Permission, PermissionStatus> statuses = await [
      Permission.bluetoothScan,
      Permission.bluetoothConnect,
      Permission.locationWhenInUse,
    ].request();

    if (statuses[Permission.bluetoothScan] == PermissionStatus.granted &&
        statuses[Permission.bluetoothConnect] == PermissionStatus.granted &&
        statuses[Permission.locationWhenInUse] == PermissionStatus.granted) {
      return true;
    } else {
      debugPrint("Bluetooth or Location permissions denied.");
      return false;
    }
  }


  // --- Bluetooth Methods ---
  Future<List<BluetoothDevice>> getPairedBluetoothDevices() => _bluetoothManager.getPairedDevices();
  Future<void> connectToBluetoothDevice(BluetoothDevice device) => _bluetoothManager.connect(device);
  void disconnectFromBluetooth() => _bluetoothManager.disconnect();
  void startBluetoothAutoReading({Duration? interval}) => _bluetoothManager.startAutoReading(interval: interval ?? const Duration(seconds: 2));
  void stopBluetoothAutoReading() => _bluetoothManager.stopAutoReading();

  // --- USB Serial Methods ---
  Future<List<UsbDevice>> getAvailableSerialDevices() => _serialManager.getAvailableDevices();
  Future<bool> requestUsbPermission(UsbDevice device) async {
    try {
      final bool? granted = await platform.invokeMethod('requestUsbPermission', {'vid': device.vid, 'pid': device.pid});
      return granted ?? false;
    } on PlatformException catch (e) {
      debugPrint("Failed to request USB permission: '${e.message}'.");
      return false;
    }
  }

  Future<void> connectToSerialDevice(UsbDevice device) async {
    final bool permissionGranted = await requestUsbPermission(device);
    if (permissionGranted) {
      await _serialManager.connect(device);
    } else {
      throw Exception("USB permission was not granted.");
    }
  }

  void disconnectFromSerial() => _serialManager.disconnect();
  void startSerialAutoReading({Duration? interval}) => _serialManager.startAutoReading(interval: interval ?? const Duration(seconds: 2));
  void stopSerialAutoReading() => _serialManager.stopAutoReading();

  void dispose() {
    _bluetoothManager.dispose();
    _serialManager.dispose();
  }

  Future<Map<String, dynamic>> submitInvestigativeSample({
    required MarineInvesManualSamplingData data,
    required List<Map<String, dynamic>>? appSettings,
    required AuthProvider authProvider,
    BuildContext? context,
    String? logDirectory,
  }) async {
    const String moduleName = 'marine_investigative';

    final connectivityResult = await Connectivity().checkConnectivity();
    bool isOnline = !connectivityResult.contains(ConnectivityResult.none);
    bool isOfflineSession = authProvider.isLoggedIn && (authProvider.profileData?['token']?.startsWith("offline-session-") ?? false);

    if (isOnline && isOfflineSession) {
      debugPrint("Investigative submission online during offline session. Attempting auto-relogin...");
      try {
        final bool transitionSuccess = await authProvider.checkAndTransitionToOnlineSession();
        if (transitionSuccess) {
          isOfflineSession = false;
        } else {
          isOnline = false; // Auto-relogin failed, treat as offline
        }
      } on SessionExpiredException catch (_) {
        debugPrint("Session expired during auto-relogin check. Treating as offline.");
        isOnline = false;
      }
    }

    if (isOnline && !isOfflineSession) {
      debugPrint("Proceeding with direct ONLINE Investigative submission...");
      return await _performOnlineSubmission(
        data: data,
        appSettings: appSettings,
        moduleName: moduleName,
        authProvider: authProvider,
        logDirectory: logDirectory,
      );
    } else {
      debugPrint("Proceeding with OFFLINE Investigative queuing mechanism...");
      return await _performOfflineQueuing(
        data: data,
        moduleName: moduleName,
        logDirectory: logDirectory, // Pass for potential update
      );
    }
  }

  Future<Map<String, dynamic>> _performOnlineSubmission({
    required MarineInvesManualSamplingData data,
    required List<Map<String, dynamic>>? appSettings,
    required String moduleName,
    required AuthProvider authProvider,
    String? logDirectory,
  }) async {
    final serverName = (await _serverConfigService.getActiveApiConfig())?['config_name'] as String? ?? 'Default';
    final imageFilesWithNulls = data.toApiImageFiles();
    imageFilesWithNulls.removeWhere((key, value) => value == null);
    final Map<String, File> finalImageFiles = imageFilesWithNulls.cast<String, File>();

    bool anyApiSuccess = false;
    Map<String, dynamic> apiDataResult = {};
    Map<String, dynamic> apiImageResult = {};
    String finalMessage = '';
    String finalStatus = '';
    bool isSessionKnownToBeExpired = false;

    try {
      // 1. Submit Form Data
      apiDataResult = await _submissionApiService.submitPost(
        moduleName: moduleName,
        endpoint: 'marine/investigative/sample',
        body: data.toApiFormData(),
      );

      if (apiDataResult['success'] == true) {
        anyApiSuccess = true;
        data.reportId = apiDataResult['data']?['man_inves_id']?.toString();

        if (data.reportId != null) {
          if (finalImageFiles.isNotEmpty) {
            // 2. Submit Images
            apiImageResult = await _submissionApiService.submitMultipart(
              moduleName: moduleName,
              endpoint: 'marine/investigative/images',
              fields: {'man_inves_id': data.reportId!},
              files: finalImageFiles,
            );
            if (apiImageResult['success'] != true) {
              anyApiSuccess = false; // Mark as failed if images fail
            }
          }
        } else {
          anyApiSuccess = false;
          apiDataResult['message'] = 'API Error: Submission succeeded but did not return a record ID.';
        }
      }
    } on SessionExpiredException catch (_) {
      debugPrint("Online submission failed due to session expiry that could not be refreshed.");
      isSessionKnownToBeExpired = true; // Mark session as expired
      anyApiSuccess = false;
      apiDataResult = {'success': false, 'message': 'Session expired and re-login failed. API submission queued.'};
      // Manually queue the API call since SubmissionApiService was never called or failed internally due to session
      await _retryService.addApiToQueue(endpoint: 'marine/investigative/sample', method: 'POST', body: data.toApiFormData());
      if (finalImageFiles.isNotEmpty && data.reportId != null) {
        // Also queue images if data call might have partially succeeded before expiry
        await _retryService.addApiToQueue(endpoint: 'marine/investigative/images', method: 'POST_MULTIPART', fields: {'man_inves_id': data.reportId!}, files: finalImageFiles);
      }
    }
    // We no longer catch SocketException or TimeoutException here.

    // 3. Submit FTP Files
    Map<String, dynamic> ftpResults = {'statuses': []};
    bool anyFtpSuccess = false;

    if (isSessionKnownToBeExpired) {
      debugPrint("Skipping FTP attempt due to known expired session. Manually queuing FTP tasks.");
      final baseFileNameForQueue = _generateBaseFileName(data);

      // --- START FIX: Add ftpConfigId when queuing ---
      final ftpConfigs = await _dbHelper.loadFtpConfigs() ?? [];

      final dataZip = await _zippingService.createDataZip(
        jsonDataMap: {'db.json': jsonEncode(data.toDbJson())},
        baseFileName: baseFileNameForQueue,
        destinationDir: null, // Use temp dir
      );
      if (dataZip != null) {
        // Queue for each config separately
        for (final config in ftpConfigs) {
          final configId = config['ftp_config_id'];
          if (configId != null) {
            await _retryService.addFtpToQueue(
                localFilePath: dataZip.path,
                remotePath: '/${p.basename(dataZip.path)}',
                ftpConfigId: configId // Provide the specific config ID
            );
          }
        }
      }

      if (finalImageFiles.isNotEmpty) {
        final imageZip = await _zippingService.createImageZip(
          imageFiles: finalImageFiles.values.toList(),
          baseFileName: baseFileNameForQueue,
          destinationDir: null, // Use temp dir
        );
        if (imageZip != null) {
          // Queue for each config separately
          for (final config in ftpConfigs) {
            final configId = config['ftp_config_id'];
            if (configId != null) {
              await _retryService.addFtpToQueue(
                  localFilePath: imageZip.path,
                  remotePath: '/${p.basename(imageZip.path)}',
                  ftpConfigId: configId // Provide the specific config ID
              );
            }
          }
        }
      }
      // --- END FIX ---
      ftpResults = {'statuses': [{'status': 'Queued', 'message': 'FTP upload queued due to API session issue.', 'success': false}]};
      anyFtpSuccess = false;

    } else {
      // Session is OK, proceed with normal FTP attempt
      try {
        // _generateAndUploadFtpFiles already uses the generic SubmissionFtpService
        ftpResults = await _generateAndUploadFtpFiles(data, finalImageFiles, serverName, moduleName);
        // Check if *any* configured FTP target succeeded (excluding 'Not Configured')
        anyFtpSuccess = !(ftpResults['statuses'] as List).any((status) => status['success'] == false && status['status'] != 'Not Configured');
      } catch (e) {
        debugPrint("Unexpected FTP submission error: $e");
        anyFtpSuccess = false; // FTP failures are auto-queued by SubmissionFtpService
      }
    }

    // 4. Determine Final Status
    final bool overallSuccess = anyApiSuccess || anyFtpSuccess;

    if (anyApiSuccess && anyFtpSuccess) {
      finalMessage = 'Data submitted successfully to all destinations.';
      finalStatus = 'S4';
    } else if (anyApiSuccess && !anyFtpSuccess) {
      finalMessage = 'Data sent to API, but some FTP uploads failed or were queued.';
      finalStatus = 'S3';
    } else if (!anyApiSuccess && anyFtpSuccess) {
      finalMessage = 'API submission failed and was queued, but files were sent to FTP successfully.';
      finalStatus = 'L4';
    } else {
      finalMessage = apiDataResult['message'] ?? 'All submission attempts failed and have been queued for retry.';
      finalStatus = 'L1';
    }

    // 5. Log Locally
    await _logAndSave(
      data: data,
      status: finalStatus,
      message: finalMessage,
      apiResults: [apiDataResult, apiImageResult],
      ftpStatuses: ftpResults['statuses'],
      serverName: serverName,
      finalImageFiles: finalImageFiles,
      logDirectory: logDirectory,
    );

    // 6. Send Alert
    if (overallSuccess) {
      _handleInvestigativeSuccessAlert(data, appSettings, isDataOnly: finalImageFiles.isEmpty, isSessionExpired: isSessionKnownToBeExpired);
    }

    return {'success': overallSuccess, 'message': finalMessage, 'reportId': data.reportId};
  }


  Future<Map<String, dynamic>> _performOfflineQueuing({
    required MarineInvesManualSamplingData data,
    required String moduleName,
    String? logDirectory, // Added for potential update
  }) async {
    final serverConfig = await _serverConfigService.getActiveApiConfig();
    final serverName = serverConfig?['config_name'] as String? ?? 'Default';

    data.submissionStatus = 'L1';
    data.submissionMessage = 'Submission queued for later retry.';

    String? savedLogPath = logDirectory; // Use existing path if provided

    // Save/Update local log first
    if (savedLogPath != null && savedLogPath.isNotEmpty) {
      // Prepare map with file paths for update
      Map<String, dynamic> logUpdateData = data.toDbJson();
      final imageFiles = data.toApiImageFiles();
      imageFiles.forEach((key, file) {
        logUpdateData[key] = file?.path; // Add paths back
      });
      logUpdateData['logDirectory'] = savedLogPath;
      await _localStorageService.updateInvestigativeLog(logUpdateData);
      debugPrint("Updated existing Investigative log for queuing: $savedLogPath");
    } else {
      savedLogPath = await _localStorageService.saveInvestigativeSamplingData(data, serverName: serverName);
      debugPrint("Saved new Investigative log for queuing: $savedLogPath");
    }

    if (savedLogPath == null) {
      const message = "Failed to save submission to local device storage.";
      await _logAndSave(data: data, status: 'Error', message: message, apiResults: [], ftpStatuses: [], serverName: serverName, finalImageFiles: {}, logDirectory: logDirectory);
      return {'success': false, 'message': message};
    }

    await _retryService.queueTask(
      type: 'investigative_submission',
      payload: {
        'module': moduleName,
        'localLogPath': savedLogPath, // Pass directory path
        'serverConfig': serverConfig,
      },
    );

    const successMessage = "Device offline. Submission has been saved locally and queued for automatic retry when connection is restored.";
    // Log final queued state to central DB
    // await _logAndSave(data: data, status: 'Queued', message: successMessage, apiResults: [], ftpStatuses: [], serverName: serverName, finalImageFiles: {}, logDirectory: savedLogPath);

    return {'success': true, 'message': successMessage, 'reportId': null}; // No report ID yet
  }

  String _generateBaseFileName(MarineInvesManualSamplingData data) {
    String stationCode = 'NA';
    if (data.stationTypeSelection == 'Existing Manual Station') {
      stationCode = data.selectedStation?['man_station_code'] ?? 'MANUAL_NA';
    } else if (data.stationTypeSelection == 'Existing Tarball Station') {
      stationCode = data.selectedTarballStation?['tbl_station_code'] ?? 'TARBALL_NA';
    } else if (data.stationTypeSelection == 'New Location') {
      stationCode = data.newStationCode ?? 'NEW_NA';
    }
    final datePart = data.samplingDate ?? 'NODATE';
    final timePart = (data.samplingTime ?? 'NOTIME').replaceAll(':', '-');
    final fileTimestamp = "${datePart}_${timePart}".replaceAll(' ', '_');
    return '${stationCode}_$fileTimestamp';
  }


  Future<Map<String, dynamic>> _generateAndUploadFtpFiles(MarineInvesManualSamplingData data, Map<String, File> imageFiles, String serverName, String moduleName) async {
    final baseFileName = _generateBaseFileName(data);

    final Directory? logDirectory = await _localStorageService.getLogDirectory(
      serverName: serverName,
      module: 'marine',
      subModule: 'marine_investigative_sampling',
    );
    final folderName = data.reportId ?? baseFileName;
    final Directory? localSubmissionDir = logDirectory != null ? Directory(p.join(logDirectory.path, folderName)) : null;

    if (localSubmissionDir != null && !await localSubmissionDir.exists()) {
      try {
        await localSubmissionDir.create(recursive: true);
      } catch (e) {
        debugPrint("Error creating local submission directory ${localSubmissionDir.path}: $e");
      }
    }

    final dataZip = await _zippingService.createDataZip(
      jsonDataMap: {'db.json': jsonEncode(data.toDbJson())},
      baseFileName: baseFileName,
      destinationDir: localSubmissionDir,
    );
    Map<String, dynamic> ftpDataResult = {'success': true, 'statuses': []};
    if (dataZip != null) {
      ftpDataResult = await _submissionFtpService.submit(
        moduleName: moduleName,
        fileToUpload: dataZip,
        remotePath: '/${p.basename(dataZip.path)}',
      );
    } else {
      debugPrint("Data ZIP file was null, skipping FTP upload for data.");
    }

    final imageZip = await _zippingService.createImageZip(
      imageFiles: imageFiles.values.toList(),
      baseFileName: baseFileName,
      destinationDir: localSubmissionDir,
    );
    Map<String, dynamic> ftpImageResult = {'success': true, 'statuses': []};
    if (imageZip != null) {
      ftpImageResult = await _submissionFtpService.submit(
        moduleName: moduleName,
        fileToUpload: imageZip,
        remotePath: '/${p.basename(imageZip.path)}',
      );
    } else {
      debugPrint("Image ZIP file was null, skipping FTP upload for images.");
    }

    return {
      'statuses': <Map<String, dynamic>>[
        ...(ftpDataResult['statuses'] as List? ?? []),
        ...(ftpImageResult['statuses'] as List? ?? []),
      ],
    };
  }


  Future<void> _logAndSave({
    required MarineInvesManualSamplingData data,
    required String status,
    required String message,
    required List<Map<String, dynamic>> apiResults,
    required List<Map<String, dynamic>> ftpStatuses,
    required String serverName,
    required Map<String, File> finalImageFiles, // Use final images map
    String? logDirectory, // Existing log directory path if updating
  }) async {
    data.submissionStatus = status;
    data.submissionMessage = message;
    final baseFileName = _generateBaseFileName(data);

    // Prepare log data map including file paths
    Map<String, dynamic> logMapData = data.toDbJson();
    final imageFileMap = data.toApiImageFiles();
    imageFileMap.forEach((key, file) {
      logMapData[key] = file?.path; // Store path or null
    });
    // Add submission metadata
    logMapData['submissionStatus'] = status;
    logMapData['submissionMessage'] = message;
    logMapData['reportId'] = data.reportId;
    logMapData['serverConfigName'] = serverName;
    logMapData['api_status'] = jsonEncode(apiResults.where((r) => r.isNotEmpty).toList());
    logMapData['ftp_status'] = jsonEncode(ftpStatuses);


    if (logDirectory != null && logDirectory.isNotEmpty) {
      logMapData['logDirectory'] = logDirectory; // Ensure path is in map
      await _localStorageService.updateInvestigativeLog(logMapData); // Use specific update
    } else {
      await _localStorageService.saveInvestigativeSamplingData(data, serverName: serverName); // Use specific save
    }

    final logData = {
      'submission_id': data.reportId ?? baseFileName,
      'module': 'marine',
      'type': 'Investigative',
      'status': status,
      'message': message,
      'report_id': data.reportId,
      'created_at': DateTime.now().toIso8601String(),
      'form_data': jsonEncode(logMapData), // Log comprehensive map
      'image_data': jsonEncode(finalImageFiles.values.map((f) => f.path).toList()),
      'server_name': serverName,
      'api_status': jsonEncode(apiResults),
      'ftp_status': jsonEncode(ftpStatuses),
    };
    try {
      await _dbHelper.saveSubmissionLog(logData);
    } catch (e) {
      debugPrint("Error saving Investigative submission log to DB: $e");
    }
  }


  Future<void> _handleInvestigativeSuccessAlert(MarineInvesManualSamplingData data, List<Map<String, dynamic>>? appSettings, {required bool isDataOnly, bool isSessionExpired = false}) async {

    String generateInvestigativeTelegramAlertMessage(MarineInvesManualSamplingData data, {required bool isDataOnly}) {
      final submissionType = isDataOnly ? "(Data Only)" : "(Data & Images)";

      String stationName = 'N/A';
      String stationCode = 'N/A';

      if (data.stationTypeSelection == 'Existing Manual Station') {
        stationName = data.selectedStation?['man_station_name'] ?? 'N/A';
        stationCode = data.selectedStation?['man_station_code'] ?? 'N/A';
      } else if (data.stationTypeSelection == 'Existing Tarball Station') {
        stationName = data.selectedTarballStation?['tbl_station_name'] ?? 'N/A';
        stationCode = data.selectedTarballStation?['tbl_station_code'] ?? 'N/A';
      } else if (data.stationTypeSelection == 'New Location') {
        stationName = data.newStationName ?? 'New Location';
        stationCode = data.newStationCode ?? 'NEW';
      }

      final buffer = StringBuffer()
        ..writeln('🕵️ *Marine Investigative Sample $submissionType Submitted:*')
        ..writeln()
        ..writeln('*Station Name & Code:* $stationName ($stationCode)')
        ..writeln('*Date of Submitted:* ${data.samplingDate}')
        ..writeln('*Submitted by User:* ${data.firstSamplerName}')
        ..writeln('*Sonde ID:* ${data.sondeId ?? "N/A"}')
        ..writeln('*Status of Submission:* Successful');

      if (data.distanceDifferenceInKm != null && data.distanceDifferenceInKm! * 1000 > 50) {
        buffer
          ..writeln()
          ..writeln('🔔 *Distance Alert:*')
          ..writeln('*Distance from station:* ${(data.distanceDifferenceInKm! * 1000).toStringAsFixed(0)} meters');

        if (data.distanceDifferenceRemarks != null && data.distanceDifferenceRemarks!.isNotEmpty) {
          buffer.writeln('*Remarks for distance:* ${data.distanceDifferenceRemarks}');
        }
      }

      return buffer.toString();
    }

    try {
      final message = generateInvestigativeTelegramAlertMessage(data, isDataOnly: isDataOnly);
      final alertKey = 'marine_investigative';

      if (isSessionExpired) {
        debugPrint("Session is expired; queuing Telegram alert directly for $alertKey.");
        await _telegramService.queueMessage(alertKey, message, appSettings);
      } else {
        final bool wasSent = await _telegramService.sendAlertImmediately(alertKey, message, appSettings);
        if (!wasSent) {
          await _telegramService.queueMessage(alertKey, message, appSettings);
        }
      }
    } catch (e) {
      debugPrint("Failed to handle Investigative Telegram alert: $e");
    }
  }
}