import 'dart:convert';
import 'dart:developer';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sn_progress_dialog/sn_progress_dialog.dart';
import 'package:test_sa/controllers/api_routes/api_manager.dart';
import 'package:test_sa/controllers/api_routes/urls.dart';
import 'package:test_sa/extensions/string_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/modules/export_module/models/export_form_model.dart';
import 'package:test_sa/modules/export_module/models/export_module_type.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/extensions/context_extension.dart';

class ExportApiProvider extends ChangeNotifier {
  bool _isExporting = false;

  bool get isExporting => _isExporting;

  int? _stateCode;

  int? get stateCode => _stateCode;

  String? _errorMessage;

  String? get errorMessage => _errorMessage;

  Future<String?> exportData<T>(ExportRequest<T> exportRequest, ExportModuleType moduleType, BuildContext context) async {
    _isExporting = true;
    _errorMessage = null;
    notifyListeners();

    // Capture colors before showing dialog to avoid Provider context issues
    // Use isDarkNotListen to safely access provider without listening
    final backgroundColor = context.isDarkNotListen ? AppColor.containerDark : Colors.white;
    final textColor = context.isDarkNotListen ? AppColor.white10 : AppColor.white936;
    bool isPdCancel = false;
    ProgressDialog pd = ProgressDialog(context: context);
    pd.show(
      max: 100,
      msg: 'Preparing export...',
      progressBgColor: Colors.transparent,
      progressValueColor: AppColor.primary10,
      msgFontWeight: FontWeight.normal,
      msgColor: textColor,
      msgMaxLines: 2,
      valueColor: textColor,
      elevation: 4,
      msgFontSize: 14,
      borderRadius: 14,
      backgroundColor: backgroundColor,
      cancel: Cancel(
        cancelClicked: () {
          isPdCancel = true;
          'Export Canceled'.showToast;
          pd.close();
        },
        cancelImageColor: AppColor.icon2ColorNotListen(context),
      ),
    );

    IOSink? sink;
    File? outputFile;

    try {
      final String apiUrl = _getApiUrlForModule(moduleType);
      final Map<String, dynamic> body = _buildRequestBody(exportRequest, moduleType);
      if (!context.mounted) return null;
      pd.update(msg: 'Sending request to server');
      log('📡 Sending export request...');

      final startTime = DateTime.now();

      final streamedResponse = await ApiManager.instance.postStreaming(apiUrl, body: body);
      _stateCode = streamedResponse.statusCode;

      final responseTime = DateTime.now().difference(startTime).inMilliseconds;
      log('✅ Response received in ${responseTime}ms - Status: ${streamedResponse.statusCode}');

      if (streamedResponse.statusCode >= 200 && streamedResponse.statusCode < 300) {
        final contentLength = streamedResponse.contentLength ?? 0;
        log('📊 Content length: ${contentLength > 0 ? "${(contentLength / 1024 / 1024).toStringAsFixed(2)} MB" : "Unknown"}');

        if (!context.mounted) {
          await streamedResponse.stream.drain();
          return null;
        }
        final String ext = _getFileExtension(exportRequest.format);
        final String dir = (await getApplicationDocumentsDirectory()).path;
        final String filePath = "$dir/${DateTime.now().millisecondsSinceEpoch}.$ext";
        outputFile = File(filePath);
        sink = outputFile.openWrite();
        int received = 0;
        int lastProgress = 15;
        final downloadStartTime = DateTime.now();
        pd.update(value: 15, msg: 'Exporting...');
        await for (var chunk in streamedResponse.stream) {
          sink.add(chunk);
          received += chunk.length;
          if (!context.mounted) {
            await sink.close();
            await outputFile.delete();
            return null;
          }

          if (contentLength > 0) {
            final progress = (15 + ((received / contentLength) * 70)).clamp(15, 85).toInt();
            if (progress > lastProgress) {
              lastProgress = progress;
              final downloadedMB = (received / 1024 / 1024).toStringAsFixed(2);
              final totalMB = (contentLength / 1024 / 1024).toStringAsFixed(2);
              pd.update(value: progress, msg: 'Exporting: $downloadedMB MB / $totalMB MB');
            }
          } else {
            final downloadedMB = (received / 1024 / 1024).toStringAsFixed(2);
            pd.update(msg: 'Exporting: $downloadedMB MB');
          }
        }
        await sink.close();
        sink = null;
        final downloadTime = DateTime.now().difference(downloadStartTime).inMilliseconds;
        final fileSizeMB = (received / 1024 / 1024).toStringAsFixed(2);
        final downloadSpeed = downloadTime > 0 ? ((received / 1024 / 1024) / (downloadTime / 1000)).toStringAsFixed(2) : 'N/A';
        if (!context.mounted) {
          await outputFile.delete();
          return null;
        }
        pd.close();
        final totalTime = DateTime.now().difference(startTime).inMilliseconds;
        if (isPdCancel) {
          return "";
        }
        return filePath;
      } else {
        if (context.mounted) {
          pd.close();
        }
        try {
          final responseBody = await streamedResponse.stream.bytesToString();
          final responseData = json.decode(responseBody);
          _errorMessage = responseData['message'] ?? 'Export failed';
        } catch (e) {
          log('❌ Error parsing error response: $e');
          _errorMessage = 'Export failed with status: ${streamedResponse.statusCode}';
        }
        return null;
      }
    } catch (error, stackTrace) {
      if (sink != null) {
        try {
          await sink.close();
        } catch (e) {
          log('⚠️ Error closing sink: $e');
        }
      }
      if (outputFile != null && await outputFile.exists()) {
        try {
          await outputFile.delete();
          log('🗑️ Deleted incomplete file');
        } catch (e) {
          log('⚠️ Error deleting incomplete file: $e');
        }
      }

      if (context.mounted) {
        pd.close();
      }
      log('❌ Error exporting data: $error');
      _stateCode = -1;
      _errorMessage = 'An error occurred during export: $error';
      return null;
    } finally {
      _isExporting = false;
      notifyListeners();
      log('🔚 Export process finished');
    }
  }

  String _getApiUrlForModule(ExportModuleType moduleType) {
    switch (moduleType) {
      case ExportModuleType.asset:
        return URLs.exportAsset;
      case ExportModuleType.callRequest:
        return URLs.exportAllWorkOrder;
      default:
        return URLs.exportAsset;
    }
  }

  String _getFileExtension(String format) {
    final formatLower = format.toLowerCase();
    switch (formatLower) {
      case 'excel':
        return 'xlsx';
      case 'pdf':
        return 'pdf';
      case 'word':
      case 'doc':
        return 'docx';
      case 'text':
      case 'txt':
        return 'txt';
      case 'csv':
        return 'csv';
      default:
        return formatLower;
    }
  }

  Map<String, dynamic> _buildRequestBody<T>(ExportRequest<T> exportRequest, ExportModuleType moduleType) {
    final Map<String, dynamic> body = {};

    if (exportRequest.searchModel != null) {
      try {
        final searchModelJson = (exportRequest.searchModel as dynamic).toJson() as Map<String, dynamic>;
        body.addAll(searchModelJson);
      } catch (e) {
        body['searchModel'] = exportRequest.searchModel;
      }
    }

    if (moduleType == ExportModuleType.callRequest) {
      _addCallRequestFields(body);
    }

    body['pageNo'] = exportRequest.pageNo;
    body['pageSize'] = exportRequest.pageSize;

    body['exportRequest'] = {
      'selectedColumns': exportRequest.selectedColumns,
      'format': exportRequest.format,
    };

    return body;
  }

  void _addCallRequestFields(Map<String, dynamic> body) {
    if (!body.containsKey('typeTransaction') || (body['typeTransaction'] as List?)?.isEmpty == true) {
      body['typeTransaction'] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
    }

    if (!body.containsKey('statusTransaction') || (body['statusTransaction'] as List?)?.isEmpty == true) {
      body['statusTransaction'] = [1, 2, 4];
    }

    if (!body.containsKey('priority')) {
      body['priority'] = [];
    }

    if (!body.containsKey('displayData')) {
      body['displayData'] = [];
    }
  }

  void resetError() {
    _errorMessage = null;
    notifyListeners();
  }

  /// Print work order data as PDF
  Future<String?> printWorkOrder(int workOrderId, int moduleId, BuildContext context) async {
    _isExporting = true;
    _errorMessage = null;
    notifyListeners();

    // Capture colors before showing dialog to avoid Provider context issues
    // Use isDarkNotListen to safely access provider without listening
    final backgroundColor = context.isDarkNotListen ? AppColor.containerDark : Colors.white;
    final textColor = context.isDarkNotListen ? AppColor.white10 : AppColor.white936;

    ProgressDialog pd = ProgressDialog(context: context);
    pd.show(
      max: 100,
      msg: 'Preparing print',
      progressBgColor: Colors.transparent,
      progressValueColor: AppColor.primary10,
      msgFontWeight: FontWeight.normal,
      msgFontSize: 18,
      elevation: 2,
      valueColor: textColor,
      backgroundColor: backgroundColor,
      msgColor: textColor,
    );

    IOSink? sink;
    File? outputFile;

    try {
      if (!context.mounted) return null;
      pd.update(msg: 'Sending print request');
      log('📡 Sending print request...');

      final startTime = DateTime.now();

      // Prepare form data
      final Map<String, String> formData = {
        'properties': json.encode({
          'workOrderId': workOrderId,
          'moduleId': moduleId,
        }),
      };

      final streamedResponse = await ApiManager.instance.postFormDataStreaming(URLs.generateReport, body: formData);

      _stateCode = streamedResponse.statusCode;

      final responseTime = DateTime.now().difference(startTime).inMilliseconds;
      log('✅ Print response received in ${responseTime}ms - Status: ${streamedResponse.statusCode}');

      if (streamedResponse.statusCode >= 200 && streamedResponse.statusCode < 300) {
        final contentLength = streamedResponse.contentLength ?? 0;
        log('📊 Content length: ${contentLength > 0 ? "${(contentLength / 1024 / 1024).toStringAsFixed(2)} MB" : "Unknown"}');

        if (!context.mounted) {
          await streamedResponse.stream.drain();
          return null;
        }

        final String dir = (await getApplicationDocumentsDirectory()).path;
        final String filePath = "$dir/print_${DateTime.now().millisecondsSinceEpoch}.pdf";
        outputFile = File(filePath);
        sink = outputFile.openWrite();

        int received = 0;
        int lastProgress = 15;
        final downloadStartTime = DateTime.now();
        pd.update(value: 15, msg: 'Generating PDF...');

        await for (var chunk in streamedResponse.stream) {
          sink.add(chunk);
          received += chunk.length;

          if (!context.mounted) {
            await sink.close();
            await outputFile.delete();
            return null;
          }

          if (contentLength > 0) {
            final progress = (15 + ((received / contentLength) * 70)).clamp(15, 85).toInt();
            if (progress > lastProgress) {
              lastProgress = progress;
              final downloadedMB = (received / 1024 / 1024).toStringAsFixed(2);
              final totalMB = (contentLength / 1024 / 1024).toStringAsFixed(2);
              pd.update(
                value: progress,
                msg: 'Generating PDF: $downloadedMB MB / $totalMB MB',
              );
            }
          } else {
            final downloadedMB = (received / 1024 / 1024).toStringAsFixed(2);
            pd.update(
              msg: 'Generating PDF: $downloadedMB MB',
            );
          }
        }

        await sink.close();
        sink = null;

        final downloadTime = DateTime.now().difference(downloadStartTime).inMilliseconds;
        final fileSizeMB = (received / 1024 / 1024).toStringAsFixed(2);
        log('✅ PDF generated successfully - Size: ${fileSizeMB} MB');

        if (!context.mounted) {
          await outputFile.delete();
          return null;
        }

        pd.close();
        return filePath;
      } else {
        if (context.mounted) {
          pd.close();
        }
        try {
          final responseBody = await streamedResponse.stream.bytesToString();
          final responseData = json.decode(responseBody);
          _errorMessage = responseData['message'] ?? 'Print failed';
        } catch (e) {
          log('❌ Error parsing print error response: $e');
          _errorMessage = 'Print failed with status: ${streamedResponse.statusCode}';
        }
        return null;
      }
    } catch (error, stackTrace) {
      if (sink != null) {
        try {
          await sink.close();
        } catch (e) {
          log('⚠️ Error closing sink: $e');
        }
      }
      if (outputFile != null && await outputFile.exists()) {
        try {
          await outputFile.delete();
          log('🗑️ Deleted incomplete file');
        } catch (e) {
          log('⚠️ Error deleting incomplete file: $e');
        }
      }

      if (context.mounted) {
        pd.close();
      }
      log('❌ Error printing data: $error');
      _stateCode = -1;
      _errorMessage = 'An error occurred during print: $error';
      return null;
    } finally {
      _isExporting = false;
      notifyListeners();
      log('🔚 Print process finished');
    }
  }
}
