import 'package:file_picker/file_picker.dart'; import 'package:flux/core/blocs/session/session_cubit.dart'; import 'package:flux/core/utils/extensions.dart'; import 'package:flux/features/attachments/models/attachment_model.dart'; import 'package:get_it/get_it.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import '../models/customer_model.dart'; class CustomerRepository { final SupabaseClient _supabase = GetIt.I(); final String companyId = GetIt.I.get().state.company!.id!; // Crea un nuovo cliente Future saveCustomer(CustomerModel customer) async { try { final response = await _supabase .from('customer') .upsert(customer.toJson()) .select() .single(); return CustomerModel.fromMap(response); } catch (e) { throw '$e'; } } Future updateCustomer(CustomerModel customer) async { try { final response = await _supabase .from('customer') .update(customer.toJson()) .eq('id', customer.id!) .select() .single(); return CustomerModel.fromMap(response); } catch (e) { throw '$e'; } } // Recupera tutti i clienti dell'azienda Future> getCustomers(String companyId) async { try { final response = await _supabase .from('customer') .select(''' *, attachment(*) ''') .eq('company_id', companyId) .eq('is_active', true) .order('name'); return (response as List).map((c) => CustomerModel.fromMap(c)).toList(); } catch (e) { throw '$e'; } } // Ricerca clienti per nome o telefono (fondamentale per la UX) Future> searchCustomers( String companyId, String query, ) async { try { final response = await _supabase .from('customer') .select() .eq('company_id', companyId) .or('name.ilike.%$query%,phone_number.ilike.%$query%') .limit(10); return (response as List).map((c) => CustomerModel.fromMap(c)).toList(); } catch (e) { return []; } } /// Ascolta in tempo reale i file caricati per un cliente Stream> getCustomerFilesStream(String customerId) { return _supabase .from('attachment') .stream(primaryKey: ['id']) .eq('customer_id', customerId) .order('created_at', ascending: false) .map( (listOfMaps) => listOfMaps.map((map) => AttachmentModel.fromMap(map)).toList(), ); } /// Recupera i file di un cliente specifico Future> getCustomerFiles(String customerId) async { try { final response = await _supabase .from('attachment') .select() .eq('customer_id', customerId); return (response as List).map((f) => AttachmentModel.fromMap(f)).toList(); } catch (e) { throw '$e'; } } /// Carica un file e salva il riferimento nel database Future uploadAndRegisterFile({ required String customerId, required PlatformFile pickedFile, }) async { final cleanFileName = pickedFile.name.replaceAll( RegExp(r'[^a-zA-Z0-9\.\-]'), '_', ); final storagePath = '$companyId/customers/${DateTime.now().millisecondsSinceEpoch}_$cleanFileName'; final int fileSize = pickedFile.size; final fileToSave = AttachmentModel( companyId: companyId, customerId: customerId, name: cleanFileName.fileNameWithoutExtension(), extension: cleanFileName.fileExtension(), storagePath: storagePath, fileSize: fileSize, ); final String mimeType = fileToSave.extension.toLowerCase() == 'pdf' ? 'application/pdf' : 'image/${fileToSave.extension}'; try { // Usiamo bytes invece del path per massima compatibilità if (pickedFile.bytes == null && pickedFile.path == null) { throw 'File read error'; } // Se siamo su desktop/mobile abbiamo il path, su web abbiamo i bytes if (pickedFile.bytes != null) { await _supabase.storage .from('documents') .uploadBinary( storagePath, pickedFile.bytes!, fileOptions: FileOptions(contentType: mimeType, upsert: true), ); } final response = await _supabase .from('attachment') .insert(fileToSave.toMap()) .select() .single(); return AttachmentModel.fromMap(response); } catch (e) { throw '$e'; } } Future saveFileReference(AttachmentModel file) async { await _supabase.from('attachment').upsert(file.toMap()); } Future deleteDocuments(List files) async { if (files.isEmpty) return; // 1. Prepariamo le liste di ID e di Percorsi final List idsToDelete = []; final List storagePathsToDelete = []; final List idsToEdit = []; for (var file in files) { if (file.operationId == null) { idsToDelete.add(file.id!); storagePathsToDelete.add(file.storagePath); } else { idsToEdit.add(file.id!); } } try { if (idsToDelete.isNotEmpty) { await _supabase.from('attachment').delete().inFilter('id', idsToDelete); // 3. Cancellazione MASSIVA dallo Storage await _supabase.storage.from('documents').remove(storagePathsToDelete); } if (idsToEdit.isNotEmpty) { await _supabase .from('attachment') .update({'customer_id': null}) .inFilter('id', idsToEdit); } } on PostgrestException catch (e) { throw e.message; } catch (e) { throw '$e'; } } }