import 'dart:async';
import 'dart:io';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:path/path.dart' as p;

import '../auth_provider.dart';
import '../models/marine_manual_npe_report_data.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 'api_service.dart';
import 'package:environment_monitoring_app/services/database_helper.dart';


class MarineNpeReportService {
  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;

  MarineNpeReportService(this._telegramService);

  Future<Map<String, dynamic>> submitNpeReport({
    required MarineManualNpeReportData data,
    required AuthProvider authProvider,
    String? logDirectory,
  }) async {
    const String moduleName = 'marine_npe_report';

    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("NPE submission online during 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 NPE submission...");
      return await _performNpeOnlineSubmission(
        data: data,
        moduleName: moduleName,
        authProvider: authProvider,
        logDirectory: logDirectory,
      );
    } else {
      debugPrint("Proceeding with OFFLINE NPE queuing mechanism...");
      return await _performNpeOfflineQueuing(
        data: data,
        moduleName: moduleName,
      );
    }
  }

  Future<Map<String, dynamic>> _performNpeOnlineSubmission({
    required MarineManualNpeReportData data,
    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 = {};

    try {
      apiDataResult = await _submissionApiService.submitPost(
        moduleName: moduleName,
        endpoint: 'marine/npe/report',
        body: data.toApiFormData(),
      );

      if (apiDataResult['success'] == false &&
          (apiDataResult['message'] as String?)?.contains('Unauthorized') == true) {
        final bool reloginSuccess = await authProvider.attemptSilentRelogin();
        if (reloginSuccess) {
          apiDataResult = await _submissionApiService.submitPost(
            moduleName: moduleName,
            endpoint: 'marine/npe/report',
            body: data.toApiFormData(),
          );
        }
      }

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

        if (data.reportId != null) {
          if (finalImageFiles.isNotEmpty) {
            apiImageResult = await _submissionApiService.submitMultipart(
              moduleName: moduleName,
              endpoint: 'marine/npe/images',
              fields: {'npe_id': data.reportId!},
              files: finalImageFiles,
            );
            if (apiImageResult['success'] != true) anyApiSuccess = false;
          }
        } else {
          anyApiSuccess = false;
          apiDataResult['message'] = 'API Error: Submission succeeded but did not return a record ID.';
        }
      }
    } on SocketException catch (e) {
      anyApiSuccess = false;
      apiDataResult = {'success': false, 'message': "API submission failed with network error: $e"};
      await _retryService.addApiToQueue(endpoint: 'marine/npe/report', method: 'POST', body: data.toApiFormData());
      if (finalImageFiles.isNotEmpty && data.reportId != null) {
        await _retryService.addApiToQueue(endpoint: 'marine/npe/images', method: 'POST_MULTIPART', fields: {'npe_id': data.reportId!}, files: finalImageFiles);
      }
    } on TimeoutException catch (e) {
      anyApiSuccess = false;
      apiDataResult = {'success': false, 'message': "API submission timed out: $e"};
      await _retryService.addApiToQueue(endpoint: 'marine/npe/report', method: 'POST', body: data.toApiFormData());
    }

    Map<String, dynamic> ftpResults = {'statuses': []};
    bool anyFtpSuccess = false;
    try {
      ftpResults = await _generateAndUploadFtpFiles(data, finalImageFiles, serverName, moduleName);
      anyFtpSuccess = !(ftpResults['statuses'] as List).any((status) => status['success'] == false && status['status'] != 'Not Configured');
    } 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 = 'NPE Report submitted successfully to all destinations.';
      finalStatus = 'S4';
    } else if (anyApiSuccess && !anyFtpSuccess) {
      finalMessage = 'NPE Report sent to API, but some FTP uploads failed and were queued.';
      finalStatus = 'S3';
    } else if (!anyApiSuccess && anyFtpSuccess) {
      finalMessage = 'API submission for NPE Report failed and was queued, but files sent to FTP.';
      finalStatus = 'L4';
    } else {
      finalMessage = 'All NPE Report 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,
      logDirectory: logDirectory,
    );

    if (overallSuccess) {
      _handleNpeSuccessAlert(data, authProvider);
    }

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

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

    data.submissionStatus = 'L1';
    data.submissionMessage = 'NPE Report queued due to being offline.';

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

    if (localLogPath == null) {
      const message = "Failed to save NPE report 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: 'npe_submission',
      payload: {
        'module': moduleName,
        'localLogPath': localLogPath,
        'serverConfig': serverConfig,
      },
    );

    const successMessage = "No internet connection. NPE Report has been saved and queued for upload.";
    return {'success': true, 'message': successMessage};
  }

  Future<Map<String, dynamic>> _generateAndUploadFtpFiles(MarineManualNpeReportData data, Map<String, File> imageFiles, String serverName, String moduleName) async {
    final stationCode = data.selectedStation?['man_station_code'] ?? data.selectedStation?['tbl_station_code'] ?? 'CUSTOM_LOC';
    final fileTimestamp = "${data.eventDate}_${data.eventTime}".replaceAll(':', '-').replaceAll(' ', '_');
    final baseFileName = '${stationCode}_${fileTimestamp}_NPE';

    final Directory? logDirectory = await _localStorageService.getLogDirectory(
      serverName: serverName,
      module: 'marine',
      subModule: 'marine_npe_report',
    );
    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: {'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)}',
      );
    }

    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<dynamic>? ?? []),
        ...(ftpImageResult['statuses'] as List<dynamic>? ?? []),
      ],
    };
  }

  Future<void> _logAndSave({
    required MarineManualNpeReportData 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,
    String? logDirectory,
  }) async {
    data.submissionStatus = status;
    data.submissionMessage = message;
    final fileTimestamp = "${data.eventDate}_${data.eventTime}".replaceAll(':', '-').replaceAll(' ', '_');

    if (logDirectory != null) {
      final Map<String, dynamic> updatedLogData = data.toDbJson();
      updatedLogData['submissionStatus'] = status;
      updatedLogData['submissionMessage'] = message;
      updatedLogData['logDirectory'] = logDirectory;
      updatedLogData['serverConfigName'] = serverName;
      updatedLogData['api_status'] = jsonEncode(apiResults.where((r) => r.isNotEmpty).toList());
      updatedLogData['ftp_status'] = jsonEncode(ftpStatuses);

      final imageFilePaths = data.toApiImageFiles();
      imageFilePaths.forEach((key, file) {
        if (file != null) updatedLogData[key] = file.path;
      });

      await _localStorageService.updateNpeLog(updatedLogData);
    } else {
      await _localStorageService.saveNpeReportData(data, serverName: serverName);
    }

    final logData = {
      'submission_id': data.reportId ?? fileTimestamp,
      'module': 'marine',
      'type': 'NPE',
      'status': data.submissionStatus,
      'message': data.submissionMessage,
      '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> _handleNpeSuccessAlert(MarineManualNpeReportData data, AuthProvider authProvider) async {
    try {
      final message = data.generateTelegramAlertMessage();
      final bool wasSent = await _telegramService.sendAlertImmediately('marine_npe_report', message, authProvider.appSettings);
      if (!wasSent) {
        await _telegramService.queueMessage('marine_npe_report', message, authProvider.appSettings);
      }
    } catch (e) {
      debugPrint("Failed to handle NPE Telegram alert: $e");
    }
  }
}