import 'package:equatable/equatable.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.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:flux/features/operations/data/operations_repository.dart'; import 'package:flux/features/operations/models/operation_model.dart'; import 'package:get_it/get_it.dart'; import 'package:collection/collection.dart'; import 'package:uuid/uuid.dart'; part 'operations_state.dart'; class OperationsCubit extends Cubit { final OperationsRepository _repository = GetIt.I(); final SessionCubit _sessionCubit = GetIt.I(); final Uuid _uuid = const Uuid(); // Generatore di UUID per il batch OperationsCubit() : super(const OperationsState(status: OperationsStatus.initial)); // --- CARICAMENTO E PAGINAZIONE --- Future loadOperations({bool refresh = false}) async { if (state.status == OperationsStatus.loading) return; if (!refresh && state.hasReachedMax) return; emit( state.copyWith( status: OperationsStatus.loading, errorMessage: null, allOperations: refresh ? [] : state.allOperations, hasReachedMax: refresh ? false : state.hasReachedMax, ), ); try { final currentOffset = refresh ? 0 : state.allOperations.length; final companyId = _sessionCubit.state.company?.id; if (companyId == null) { throw Exception("Company ID non trovato nella sessione"); } final newOperations = await _repository.fetchOperations( companyId: companyId, offset: currentOffset, limit: 50, searchTerm: state.query, dateRange: state.dateRange, ); final bool reachedMax = newOperations.length < 50; emit( state.copyWith( status: OperationsStatus.ready, allOperations: refresh ? newOperations : [...state.allOperations, ...newOperations], hasReachedMax: reachedMax, ), ); } catch (e) { emit( state.copyWith( status: OperationsStatus.failure, errorMessage: "Errore nel caricamento operazioni: $e", ), ); } } // --- GESTIONE FILTRI --- void updateFilters({String? query, DateTimeRange? range}) { emit( state.copyWith( query: query ?? state.query, dateRange: range ?? state.dateRange, ), ); loadOperations(refresh: true); } void clearFilters() { emit(state.copyWith(query: '', dateRange: null)); loadOperations(refresh: true); } void initOperationForm({ OperationModel? existingOperation, String? operationId, }) async { if (existingOperation != null) { emit( state.copyWith( currentOperation: existingOperation, status: OperationsStatus.ready, ), ); } else if (operationId != null) { OperationModel? operationModel = state.allOperations.firstWhereOrNull( (s) => s.id == operationId, ); operationModel ??= await _repository.fetchOperationById(operationId); emit( state.copyWith( currentOperation: operationModel, status: OperationsStatus.ready, ), ); } else { // NUOVA PRATICA: Creiamo un nuovo Batch UUID emit( state.copyWith( currentOperation: OperationModel( storeId: _sessionCubit.state.currentStore?.id ?? '', reference: '', createdAt: DateTime.now(), companyId: _sessionCubit.state.company!.id!, status: OperationStatus.draft, batchUuid: _uuid.v4(), // <-- GENERIAMO IL BATCH UNIVOCO ), status: OperationsStatus.ready, ), ); } } /// MAGIA PURA: Prepara il form per inserire un altro servizio nella stessa pratica. /// Mantiene il Cliente, il Batch e lo Store, ma svuota il resto. void prepareNextOperationInBatch() { if (state.currentOperation == null) return; final current = state.currentOperation!; emit( state.copyWith( status: OperationsStatus.ready, currentOperation: OperationModel( companyId: current.companyId, storeId: current.storeId, storeDisplayName: current.storeDisplayName, batchUuid: current.batchUuid, // <-- MANTIENE IL COLLEGAMENTO customerId: current.customerId, // <-- MANTIENE IL CLIENTE customerDisplayName: current.customerDisplayName, status: OperationStatus.draft, createdAt: DateTime.now(), ), ), ); } // --- PERSISTENZA --- Future saveCurrentOperation({ required OperationStatus targetStatus, bool shouldPop = true, }) async { if (state.currentOperation == null) return; emit(state.copyWith(status: OperationsStatus.saving, errorMessage: null)); try { final operationToSave = state.currentOperation!.copyWith( status: targetStatus, ); final updatedOperation = await _repository.saveFullOperation( operationToSave, ); emit( state.copyWith( // Se non facciamo Pop (es. l'utente vuole aggiungere un altro servizio), non killiamo l'operazione corrente status: shouldPop ? OperationsStatus.saved : OperationsStatus.savedNoPop, currentOperation: shouldPop ? null : updatedOperation, ), ); // Ricarica in background per la dashboard loadOperations(refresh: true); } catch (e) { emit( state.copyWith( status: OperationsStatus.failure, errorMessage: e.toString(), ), ); } } // --- RECUPERO OPERAZIONI DELLO STESSO BATCH (Per UI di riepilogo) --- /// Puoi usare questa funzione se nella UI vuoi mostrare "Hai inserito 3 servizi in questa pratica" List getOperationsInCurrentBatch() { if (state.currentOperation == null) return []; final currentBatch = state.currentOperation!.batchUuid; // Filtriamo dalla lista caricata (o potresti fare una query diretta a Supabase se preferisci) return state.allOperations .where( (op) => op.batchUuid == currentBatch && op.id != state.currentOperation!.id, ) .toList(); } // --- GESTIONE DELLO STATO DEL FORM IN TEMPO REALE --- void updateOperationFields({ String? customerId, String? customerDisplayName, String? type, String? providerId, String? subtype, DateTime? expirationDate, int? quantity, // Aggiungiamo questi flag per forzare la pulizia dei campi quando cambi tipo bool clearProvider = false, bool clearType = false, bool clearSubtype = false, bool clearExpiration = false, }) { if (state.currentOperation == null) return; final current = state.currentOperation!; // Creiamo il modello aggiornato // ATTENZIONE: adatta questa logica in base a come è scritto il tuo copyWith! final updated = current.copyWith( customerId: customerId, customerDisplayName: customerDisplayName, type: clearType ? null : type, subtype: clearSubtype ? null : subtype, expirationDate: clearExpiration ? null : expirationDate, // Se clearProvider è true, forziamo una stringa vuota (o null se il tuo modello lo supporta) providerId: clearProvider ? null : (providerId ?? current.providerId), // Idem per subtype e date. // Se expirationDate è nullabile nel copyWith, dovresti poterlo gestire quantity: quantity ?? current.quantity, ); emit(state.copyWith(currentOperation: updated)); } }