// lib/services/retry_service.dart

import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:path/path.dart' as p;

import 'package:environment_monitoring_app/models/in_situ_sampling_data.dart';
import 'package:environment_monitoring_app/services/marine_in_situ_sampling_service.dart';
import 'package:environment_monitoring_app/models/river_in_situ_sampling_data.dart'; // ADDED
import 'package:environment_monitoring_app/services/river_in_situ_sampling_service.dart'; // ADDED
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';
import 'package:environment_monitoring_app/services/server_config_service.dart';
import 'package:environment_monitoring_app/auth_provider.dart';

/// A dedicated service to manage the queue of failed API, FTP, and complex submission tasks.
class RetryService {
  final DatabaseHelper _dbHelper = DatabaseHelper();
  final BaseApiService _baseApiService = BaseApiService();
  final FtpService _ftpService = FtpService();
  final ServerConfigService _serverConfigService = ServerConfigService();
  bool _isProcessing = false;

  // --- START: MODIFICATION FOR HANDLING COMPLEX TASKS ---
  // These services will be provided after the RetryService is created.
  MarineInSituSamplingService? _marineInSituService;
  RiverInSituSamplingService? _riverInSituService; // ADDED
  AuthProvider? _authProvider;

  // Call this method from your main app setup to provide the necessary services.
  void initialize({
    required MarineInSituSamplingService marineInSituService,
    required RiverInSituSamplingService riverInSituService, // ADDED
    required AuthProvider authProvider,
  }) {
    _marineInSituService = marineInSituService;
    _riverInSituService = riverInSituService; // ADDED
    _authProvider = authProvider;
  }
  // --- END: MODIFICATION FOR HANDLING COMPLEX TASKS ---


  /// Adds a generic, complex task to the queue, to be handled by a background processor.
  Future<void> queueTask({
    required String type,
    required Map<String, dynamic> payload,
  }) async {
    await _dbHelper.queueFailedRequest({
      'type': type,
      'endpoint_or_path': 'N/A',
      'payload': jsonEncode(payload),
      'timestamp': DateTime.now().toIso8601String(),
      'status': 'pending',
    });
    debugPrint("Task of type '$type' has been queued for background processing.");
  }

  /// Adds a failed API request to the local database queue.
  Future<void> addApiToQueue({
    required String endpoint,
    required String method,
    Map<String, dynamic>? body,
    Map<String, String>? fields,
    Map<String, File>? files,
  }) async {
    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<void> 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<List<Map<String, dynamic>>> getPendingTasks() {
    return _dbHelper.getPendingRequests();
  }

  /// Processes the entire queue of pending tasks.
  Future<void> processRetryQueue() async {
    if (_isProcessing) {
      debugPrint("[RetryService] ⏳ Queue is already being processed. Skipping.");
      return;
    }
    _isProcessing = true;
    debugPrint("[RetryService] ▶️ Starting to process main retry queue...");

    final pendingTasks = await getPendingTasks();
    if (pendingTasks.isEmpty) {
      debugPrint("[RetryService] ⏹️ Queue is empty. Nothing to process.");
      _isProcessing = false;
      return;
    }

    if (_authProvider == null || !await _authProvider!.isConnected()) {
      debugPrint("[RetryService] ❌ No internet connection. Aborting queue processing.");
      _isProcessing = false;
      return;
    }

    debugPrint("[RetryService] 🔎 Found ${pendingTasks.length} pending tasks.");
    for (final task in pendingTasks) {
      await retryTask(task['id'] as int);
    }

    debugPrint("[RetryService] ⏹️ Finished processing retry queue.");
    _isProcessing = false;
  }

  /// Attempts to re-execute a single failed task from the queue.
  /// Returns `true` on success, `false` on failure.
  Future<bool> 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 (_authProvider == null) {
        debugPrint("RetryService has not been initialized. Cannot process task.");
        return false;
      }

      if (task['type'] == 'insitu_submission') {
        debugPrint("Retrying complex task 'insitu_submission' with ID $taskId.");
        if (_marineInSituService == null) return false;

        final String logFilePath = payload['localLogPath'];
        final file = File(logFilePath);

        if (!await file.exists()) {
          debugPrint("Retry failed: Source log file no longer exists at $logFilePath");
          await _dbHelper.deleteRequestFromQueue(taskId); // Remove invalid task
          return false;
        }

        final content = await file.readAsString();
        final jsonData = jsonDecode(content) as Map<String, dynamic>;
        final InSituSamplingData dataToResubmit = InSituSamplingData.fromJson(jsonData);
        final String logDirectoryPath = p.dirname(logFilePath);

        final result = await _marineInSituService!.submitInSituSample(
          data: dataToResubmit,
          appSettings: _authProvider!.appSettings,
          authProvider: _authProvider!,
          logDirectory: logDirectoryPath,
        );
        success = result['success'];

        // --- START: ADDED LOGIC FOR RIVER IN-SITU SUBMISSION ---
      } else if (task['type'] == 'river_insitu_submission') {
        debugPrint("Retrying complex task 'river_insitu_submission' with ID $taskId.");
        if (_riverInSituService == null) return false;

        final String logFilePath = payload['localLogPath'];
        final file = File(logFilePath);

        if (!await file.exists()) {
          debugPrint("Retry failed: Source log file no longer exists at $logFilePath");
          await _dbHelper.deleteRequestFromQueue(taskId); // Remove invalid task
          return false;
        }

        final content = await file.readAsString();
        final jsonData = jsonDecode(content) as Map<String, dynamic>;
        final RiverInSituSamplingData dataToResubmit = RiverInSituSamplingData.fromJson(jsonData);
        final String logDirectoryPath = p.dirname(logFilePath);

        final result = await _riverInSituService!.submitData(
          data: dataToResubmit,
          appSettings: _authProvider!.appSettings,
          authProvider: _authProvider!,
          logDirectory: logDirectoryPath,
        );
        success = result['success'];
        // --- END: ADDED LOGIC FOR RIVER IN-SITU SUBMISSION ---

      } else if (task['type'] == 'api') {
        final endpoint = task['endpoint_or_path'] as String;
        final method = payload['method'] as String;
        final baseUrl = await _serverConfigService.getActiveApiUrl();
        debugPrint("Retrying API task $taskId: $method to $baseUrl/$endpoint");
        Map<String, dynamic> result;

        if (method == 'POST_MULTIPART') {
          final Map<String, String> fields = Map<String, String>.from(payload['fields'] ?? {});
          final Map<String, File> files = (payload['files'] as Map<String, dynamic>?)
              ?.map((key, value) => MapEntry(key, File(value as String))) ?? {};
          result = await _baseApiService.postMultipart(baseUrl: baseUrl, endpoint: endpoint, fields: fields, files: files);
        } else {
          final Map<String, dynamic> body = Map<String, dynamic>.from(payload['body'] ?? {});
          result = await _baseApiService.post(baseUrl, 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");

        if (await localFile.exists()) {
          final ftpConfigs = await _dbHelper.loadFtpConfigs() ?? [];
          if (ftpConfigs.isEmpty) {
            debugPrint("Retry failed for FTP task $taskId: No FTP configurations found.");
            return false;
          }
          for (final config in ftpConfigs) {
            final result = await _ftpService.uploadFile(config: config, fileToUpload: localFile, remotePath: remotePath);
            if (result['success']) {
              success = true;
              break;
            }
          }
        } 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;
  }
}