import 'package:equatable/equatable.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/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( operation: 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? providerDisplayName, String? subtype, DateTime? expirationDate, int? quantity, String? modelId, String? modelDisplayName, // Aggiungiamo questi flag per forzare la pulizia dei campi quando cambi tipo bool clearProvider = false, bool clearType = false, bool clearSubtype = false, bool clearExpiration = false, bool clearQuantity = false, bool clearModel = 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! int? newQuantity; if (clearQuantity) { newQuantity = 1; } if (quantity != null && quantity <= 0) { newQuantity = 0; } if (quantity != null && quantity > 0) { newQuantity = quantity; } final updated = current.copyWith( customerId: customerId, customerDisplayName: customerDisplayName, // Se clearProvider è true, forziamo una stringa vuota (o null se il tuo modello lo supporta) providerId: clearProvider ? null : (providerId ?? current.providerId), providerDisplayName: clearProvider ? null : (providerDisplayName ?? current.providerDisplayName), quantity: newQuantity, type: clearType ? null : (type ?? current.type), subtype: clearSubtype ? null : (subtype ?? current.subtype), expirationDate: clearExpiration ? null : (expirationDate ?? current.expirationDate), modelId: clearModel ? null : (modelId ?? current.modelId), modelDisplayName: clearModel ? null : (modelDisplayName ?? current.modelDisplayName), ); emit(state.copyWith(currentOperation: updated)); } // Metodo di utilità per calcolare la data X mesi da oggi DateTime _calculateMonths(int months) { final now = DateTime.now(); return DateTime(now.year, now.month + months, now.day); } // Quando l'utente seleziona un tipo, impostiamo il default void setTypeWithSmartDefault(String type) { DateTime? defaultDate; if (type == 'Energy') defaultDate = _calculateMonths(24); if (type == 'Fin') defaultDate = _calculateMonths(30); if (type == 'Entertainment') defaultDate = _calculateMonths(12); updateOperationFields( type: type, expirationDate: defaultDate, clearProvider: true, clearSubtype: true, clearModel: true, clearQuantity: true, ); } }