import 'package:flutter/material.dart'; import 'package:flux/core/blocs/session/session_bloc.dart'; import 'package:flux/features/customers/data/customer_repository.dart'; import 'package:flux/features/customers/models/customer_file_model.dart'; import 'package:flux/features/services/models/service_file_model.dart'; import 'package:get_it/get_it.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import '../models/service_model.dart'; class ServicesRepository { final _supabase = Supabase.instance.client; final companyId = GetIt.I.get().state.company!.id; final CustomerRepository _customerRepository = GetIt.I(); // --- RECUPERO SINGOLO SERVIZIO CON JOIN COMPLETO --- Future fetchServiceById(String id) async { try { final response = await _supabase .from('service') .select(''' *, customer(nome), energy_service(*), fin_service(*), entertainment_service(*), service_file(*) ''') .eq('id', id) .single(); return ServiceModel.fromMap(response); } catch (e) { throw Exception('Errore nel caricamento del servizio: $e'); } } // --- RECUPERO PAGINATO CON FILTRI E JOIN --- Future> fetchServices({ required String companyId, required int offset, int limit = 50, String? searchTerm, DateTimeRange? dateRange, }) async { try { // Nota: 'customer(name, surname)' serve per il display name nella card var query = _supabase .from('service') .select(''' *, customer(nome), energy_service(*), fin_service(*), entertainment_service(*), service_file(*) ''') .eq('company_id', companyId); // Filtro Range Date if (dateRange != null) { query = query .gte('created_at', dateRange.start.toIso8601String()) .lte('created_at', dateRange.end.toIso8601String()); } if (searchTerm != null && searchTerm.isNotEmpty) { // Filtra sui campi della tabella principale O su quelli della tabella joinata query = query.or( 'number.ilike.%$searchTerm%,note.ilike.%$searchTerm%,customer.nome.ilike.%$searchTerm%', ); } final response = await query .order('created_at', ascending: false) .range(offset, offset + limit - 1); return (response as List) .map((map) => ServiceModel.fromMap(map)) .toList(); } catch (e) { throw Exception('Errore nel caricamento servizi: $e'); } } // --- SALVATAGGIO COMPLETO (PRIMA PADRE, POI FIGLI) --- Future saveFullService(ServiceModel service) async { try { // 1. Upsert del record principale final serviceData = await _supabase .from('service') .upsert(service.toMap()) .select() .single(); final String newId = serviceData['id']; // 2. MODIFICA: Pulizia atomica dei figli // Se stiamo modificando (id != null), resettiamo le tabelle collegate if (service.id != null) { await Future.wait([ _supabase.from('energy_service').delete().eq('service_id', newId), _supabase.from('fin_service').delete().eq('service_id', newId), _supabase .from('entertainment_service') .delete() .eq('service_id', newId), // Aggiungi qui eventuali altre tabelle pivot o file ]); } // 3. Inserimento dei moduli in parallelo per velocità final List insertTasks = []; if (service.energyServices.isNotEmpty) { insertTasks.add( _supabase .from('energy_service') .insert( service.energyServices .map((item) => item.copyWith(serviceId: newId).toMap()) .toList(), ), ); } if (service.finServices.isNotEmpty) { insertTasks.add( _supabase .from('fin_service') .insert( service.finServices .map((item) => item.copyWith(serviceId: newId).toMap()) .toList(), ), ); } if (service.entertainmentServices.isNotEmpty) { insertTasks.add( _supabase .from('entertainment_service') .insert( service.entertainmentServices .map((item) => item.copyWith(serviceId: newId).toMap()) .toList(), ), ); } if (insertTasks.isNotEmpty) { await Future.wait(insertTasks); } if (service.files.isNotEmpty) { final List uploadTasks = []; for (var file in service.files) { final storagePath = '$companyId/services/$newId/${DateTime.now().millisecondsSinceEpoch}_${file.name}.${file.extension}'; final String mimeType = file.extension.toLowerCase() == 'pdf' ? 'application/pdf' : 'image/${file.extension}'; final fileToSave = file.copyWith(serviceId: newId, url: storagePath); // Creiamo una funzione asincrona per caricare file e scrivere nel DB Future uploadAndLink() async { // Determiniamo il MIME type corretto in base all'estensione // A. Upload nel Bucket Storage (usiamo i bytes così funziona anche su Web!) await _supabase.storage .from('documents') .uploadBinary( storagePath, fileToSave.localBytes!, fileOptions: FileOptions( contentType: mimeType, // Diciamo a Supabase esattamente cos'è! upsert: true, // Opzionale: sovrascrive se esiste già un file con lo stesso nome ), ); await _supabase.from('service_file').insert(fileToSave.toMap()); } uploadTasks.add(uploadAndLink()); } // Eseguiamo tutti gli upload in parallelo per la massima velocità await Future.wait(uploadTasks); } } catch (e) { // Qui potresti aggiungere una logica di "rollback manuale" se necessario throw Exception('Errore durante il salvataggio corazzato: $e'); } } // --- ELIMINAZIONE --- Future deleteService(String id) async { try { await _supabase.from('service').delete().eq('id', id); } catch (e) { throw Exception('Errore durante l\'eliminazione: $e'); } } // --- RECUPERO TIPI CONTENUTI PIÙ FREQUENTI PER AUTOCOMPLETE --- Future> fetchTopEntertainmentTypes(String companyId) async { try { // Cerchiamo i tipi più frequenti associati ai servizi di questa company // Nota: dobbiamo passare attraverso la tabella 'service' per filtrare per company_id final response = await _supabase .from('entertainment_service') .select('type, service!inner(store!inner(company_id))') .eq('service.store.company_id', companyId) .limit(100); // Prendiamo un campione // Logica rapida per contare le occorrenze e prendere i primi 5 final Map counts = {}; for (var item in (response as List)) { final type = item['type'] as String; counts[type] = (counts[type] ?? 0) + 1; } var sortedKeys = counts.keys.toList() ..sort((a, b) => counts[b]!.compareTo(counts[a]!)); return sortedKeys.take(5).toList(); } catch (e) { return [ "Netflix", "DAZN", "Disney+", "Sky", ]; // Fallback se non c'è ancora storia } } Future copyFileToCustomer({ required ServiceFileModel file, required String customerId, }) async { CustomerFileModel fileToCopy = CustomerFileModel( customerId: customerId, name: file.name, url: file.url, extension: file.extension, fileSize: file.fileSize, ); await _customerRepository.saveCustomerFile(fileToCopy); } }