import 'dart:io'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:path_provider/path_provider.dart'; import 'package:path/path.dart' as path; import '../../../core/utils/logger.dart'; import '../../../core/utils/app_snackbar.dart'; import '../../../core/services/image_processing_service.dart'; import '../../../core/services/invoice_upload_service.dart'; import '../../../core/network/dio_client.dart'; class ScannerController extends GetxController { var capturedImages = [].obs; var isProcessing = false.obs; var uploadProgress = 0.0.obs; var companies = >[].obs; var isLoadingCompanies = false.obs; final InvoiceUploadService _uploadService = InvoiceUploadService(); @override void onInit() { super.onInit(); fetchCompanies(); } Future fetchCompanies() async { isLoadingCompanies.value = true; try { final res = await DioClient().client.get('companies'); if (res.data['success'] == true && res.data['data'] != null) { companies.value = List>.from(res.data['data']); } } catch (e) { AppLogger.error('Failed to fetch companies', e); } finally { isLoadingCompanies.value = false; } } Future addImage(String imagePath) async { File originalFile = File(imagePath); // Add to UI immediately so the user doesn't wait capturedImages.add(originalFile); int index = capturedImages.length - 1; // Process in background without showing full-screen loader ImageProcessingService.processInvoiceImage(originalFile).then((processedFile) { if (processedFile != null && index < capturedImages.length) { capturedImages[index] = processedFile; AppLogger.print('Finished processing image in background. Replaced in batch.'); } }).catchError((e) { AppLogger.error('Failed to process image in background', e); }); } void removeImage(int index) { if (index >= 0 && index < capturedImages.length) { capturedImages.removeAt(index); } } Future uploadBatch(String fallbackCompanyId) async { if (capturedImages.isEmpty) { AppSnackbar.showWarning('تنبيه', 'الرجاء تصوير فاتورة واحدة على الأقل'); return; } try { isProcessing.value = true; uploadProgress.value = 0.0; // Fetch a valid company ID dynamically to prevent 403 Forbidden String companyId = fallbackCompanyId; if (companyId == 'mock_company_id_123' || companyId.isEmpty) { final res = await DioClient().client.get('companies'); if (res.data['success'] == true && res.data['data'] != null && res.data['data'].isNotEmpty) { companyId = res.data['data'][0]['id']; AppLogger.print('Dynamically fetched company: $companyId'); } else { AppSnackbar.showError('خطأ', 'لا توجد شركات مسجلة في حسابك'); isProcessing.value = false; return; } } AppLogger.print('Uploading batch of ${capturedImages.length} images to company $companyId...'); final batchId = await _uploadService.uploadBatch( companyId: companyId, images: capturedImages, onProgress: (current, total) { uploadProgress.value = current / total; }, ); if (batchId != null) { capturedImages.clear(); uploadProgress.value = 0.0; Get.defaultDialog( title: 'جاري المعالجة ⏳', middleText: 'تم استلام الفواتير بنجاح وسيتم إشعارك فور الانتهاء من تدقيقها عبر الذكاء الاصطناعي.', textConfirm: 'حسناً', confirmTextColor: Colors.white, buttonColor: const Color(0xFF0F4C81), onConfirm: () { if (Get.isDialogOpen ?? false) Get.back(); // close dialog Get.back(); // go back to dashboard }, barrierDismissible: false, titleStyle: const TextStyle(fontFamily: 'El Messiri', fontWeight: FontWeight.bold, fontSize: 18), middleTextStyle: const TextStyle(fontFamily: 'El Messiri', fontSize: 14), radius: 12, ); } else { AppSnackbar.showError('خطأ', 'فشل رفع الفواتير، يرجى المحاولة لاحقاً'); } } catch (e) { AppLogger.error('Failed to upload batch', e); AppSnackbar.showError('خطأ', 'حدث خطأ غير متوقع أثناء الرفع'); } finally { isProcessing.value = false; } } Future getSavePath() async { final directory = await getTemporaryDirectory(); final fileName = 'invoice_${DateTime.now().millisecondsSinceEpoch}.jpg'; return path.join(directory.path, fileName); } }