// lib/services/marine_tarball_sampling_service.dart

import 'dart:io';
import 'dart:convert';
import 'dart:async'; // Added for TimeoutException
import 'package:flutter/foundation.dart';
import 'package:path/path.dart' as p;
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:provider/provider.dart';
import 'package:flutter/material.dart';

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';
import 'package:environment_monitoring_app/services/retry_service.dart';
import 'package:environment_monitoring_app/auth_provider.dart';
import 'package:environment_monitoring_app/services/base_api_service.dart'; // Import for SessionExpiredException

/// 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();
  final RetryService _retryService = RetryService();
  final TelegramService _telegramService;

  MarineTarballSamplingService(this._telegramService);

  Future<Map<String, dynamic>> submitTarballSample({
    required TarballSamplingData data,
    required List<Map<String, dynamic>>? appSettings,
    required BuildContext context,
  }) async {
    const String moduleName = 'marine_tarball';
    final authProvider = Provider.of<AuthProvider>(context, listen: false);

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

    if (isOnline && isOfflineSession) {
      debugPrint("Submission initiated online during an offline session. Attempting auto-relogin...");
      final bool transitionSuccess = await authProvider.checkAndTransitionToOnlineSession();
      if (transitionSuccess) {
        isOfflineSession = false;
      } else {
        isOnline = false;
      }
    }

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

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

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

    try {
      apiDataResult = await _submissionApiService.submitPost(
        moduleName: moduleName,
        endpoint: 'marine/tarball/sample',
        body: data.toFormData(),
      );

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

        if (data.reportId != null) {
          apiImageResult = await _submissionApiService.submitMultipart(
            moduleName: moduleName,
            endpoint: 'marine/tarball/images',
            fields: {'autoid': data.reportId!},
            files: finalImageFiles,
          );
          if (apiImageResult['success'] != true) {
            anyApiSuccess = false; // Downgrade success if images fail
          }
        } else {
          anyApiSuccess = false;
          apiDataResult['message'] = 'API Error: Submission succeeded but did not return a record ID.';
        }
      }
    } on SessionExpiredException catch (_) {
      debugPrint("API submission failed with SessionExpiredException. Attempting silent relogin...");
      final bool reloginSuccess = await authProvider.attemptSilentRelogin();

      if (reloginSuccess) {
        debugPrint("Silent relogin successful. Retrying entire online submission process...");
        return await _performOnlineSubmission(
          data: data,
          appSettings: appSettings,
          moduleName: moduleName,
          authProvider: authProvider,
        );
      } else {
        debugPrint("Silent relogin failed. API part will be queued, proceeding with FTP.");
        isSessionKnownToBeExpired = true;
        anyApiSuccess = false;
        apiDataResult = {'success': false, 'message': 'Session expired and re-login failed. API submission queued.'};
        await _retryService.addApiToQueue(endpoint: 'marine/tarball/sample', method: 'POST', body: data.toFormData());
      }
    } on SocketException catch (e) {
      final errorMessage = "API submission failed with network error: $e";
      debugPrint(errorMessage);
      anyApiSuccess = false;
      apiDataResult = {'success': false, 'message': errorMessage};
      await _retryService.addApiToQueue(endpoint: 'marine/tarball/sample', method: 'POST', body: data.toFormData());
      if(finalImageFiles.isNotEmpty && data.reportId != null) {
        await _retryService.addApiToQueue(endpoint: 'marine/tarball/images', method: 'POST_MULTIPART', fields: {'autoid': data.reportId!}, files: finalImageFiles);
      }
    } on TimeoutException catch (e) {
      final errorMessage = "API submission timed out: $e";
      debugPrint(errorMessage);
      anyApiSuccess = false;
      apiDataResult = {'success': false, 'message': errorMessage};
      await _retryService.addApiToQueue(endpoint: 'marine/tarball/sample', method: 'POST', body: data.toFormData());
    }

    Map<String, dynamic> ftpResults = {'statuses': []};
    bool anyFtpSuccess = false;
    try {
      ftpResults = await _generateAndUploadFtpFiles(data, finalImageFiles, serverName, moduleName);
      anyFtpSuccess = !ftpResults['statuses'].any((status) => status['success'] == false);
    } on SocketException catch (e) {
      debugPrint("FTP submission failed with network error: $e");
      anyFtpSuccess = false;
    } on TimeoutException catch (e) {
      debugPrint("FTP submission timed out: $e");
      anyFtpSuccess = false;
    }

    final bool overallSuccess = anyApiSuccess || anyFtpSuccess;
    String finalMessage;
    String finalStatus;

    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 and 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';
    }

    await _logAndSave(
      data: data,
      status: finalStatus,
      message: finalMessage,
      apiResults: [apiDataResult, apiImageResult],
      ftpStatuses: ftpResults['statuses'],
      serverName: serverName,
      finalImageFiles: finalImageFiles,
    );

    if (overallSuccess) {
      _handleTarballSuccessAlert(data, appSettings, isDataOnly: finalImageFiles.isEmpty, isSessionExpired: isSessionKnownToBeExpired);
    }

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

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

    final String? localLogPath = await _localStorageService.saveTarballSamplingData(data, serverName: serverName);

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

    await _retryService.queueTask(
      type: 'tarball_submission',
      payload: {
        'module': moduleName,
        'localLogPath': localLogPath,
        'serverConfig': serverConfig,
      },
    );

    const successMessage = "Submission failed to send and has been queued for later retry.";
    await _logAndSave(data: data, status: 'Queued', message: successMessage, apiResults: [], ftpStatuses: [], serverName: serverName, finalImageFiles: {});

    return {'success': true, 'message': successMessage};
  }

  Future<Map<String, dynamic>> _generateAndUploadFtpFiles(TarballSamplingData data, Map<String, File> imageFiles, String serverName, String moduleName) async {
    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<String, dynamic> 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: 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)}',
      );
    }

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

  Future<void> _logAndSave({
    required TarballSamplingData 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,
  }) 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<void> _handleTarballSuccessAlert(TarballSamplingData data, List<Map<String, dynamic>>? appSettings, {required bool isDataOnly, bool isSessionExpired = false}) async {
    try {
      final message = data.generateTelegramAlertMessage(isDataOnly: isDataOnly);
      if (isSessionExpired) {
        debugPrint("Session is expired; queuing Telegram alert directly.");
        await _telegramService.queueMessage('marine_tarball', message, appSettings);
      } else {
        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");
    }
  }
}