import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flux/core/enums_and_consts/consts.dart'; import 'package:flux/features/tasks/models/task_reminder_config.dart'; import 'package:flux/features/tasks/models/task_status.dart'; import 'package:get_it/get_it.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; // Sostituisci con i percorsi corretti di FLUX import 'package:flux/features/tasks/models/task_model.dart'; class TasksRepository { final _supabase = GetIt.I.get(); // ========================================================================= // LETTURA REMINDER (Per il form in edit) // ========================================================================= Future> fetchPersonalReminders({ required String taskId, required String staffId, }) async { try { final response = await _supabase .from('task_reminders') .select() .eq('task_id', taskId) .eq('staff_id', staffId) .eq( 'is_forced', false, ); // Peschiamo SOLO quelli modificabili dall'utente return (response as List) .map( (r) => TaskReminderConfig( minutesBefore: r['minutes_before'], channel: r['channel'], ), ) .toList(); } catch (e) { debugPrint('Errore fetch personal reminders: $e'); throw Exception('Errore fetch personal reminders: $e'); } } // --- RECUPERO DEI TASK FILTRATI --- Future> getTasks({ required String companyId, String? storeId, String? staffId, List? statuses, int? limit, }) async { try { // 1. FASE FILTRI: Usa il join esplicito stile "Notes" var filterBuilder = _supabase .from(Tables.tasks) .select(''' *, task_assignments:${Tables.taskAssignments} ( ${Tables.staffMembers} (*) ) ''') .eq('company_id', companyId); if (storeId != null) { filterBuilder = filterBuilder.or( 'store_id.eq.$storeId,store_id.is.null', ); } if (staffId != null) { // Grazie al trigger, hai l'array pronto per il filtro senza impazzire! filterBuilder = filterBuilder.contains('assigned_to_ids', [staffId]); } if (statuses != null && statuses.isNotEmpty) { final statusValues = statuses.map((s) => s.toValue).toList(); filterBuilder = filterBuilder.inFilter('status', statusValues); } // 2. FASE TRASFORMAZIONI var transformBuilder = filterBuilder .order('due_date', ascending: true, nullsFirst: false) .order('created_at', ascending: false, nullsFirst: false); if (limit != null) { transformBuilder = transformBuilder.limit(limit); } // 3. ESECUZIONE DELLA QUERY final response = await transformBuilder; // 4. PARSING DEI DATI return (response as List).map((json) => TaskModel.fromMap(json)).toList(); } catch (e) { throw Exception('Errore nel recupero dei task: $e'); } } Future fetchTaskById(String taskId) async { try { final response = await _supabase .from(Tables.tasks) .select(''' *, task_assignments:${Tables.taskAssignments} ( ${Tables.staffMembers} (*) ) ''') .eq('id', taskId) .single(); return TaskModel.fromMap(response); } catch (e) { debugPrint('Errore fetch task by id: $e'); throw Exception('Errore fetch task by id: $e'); } } // ========================================================================= // REALTIME STREAM (La sentinella per la bacheca) // ========================================================================= Stream> watchCompanyTasks(String companyId) { return _supabase .from('tasks') .stream(primaryKey: ['id']) .eq('company_id', companyId) .map((listOfMaps) { return listOfMaps.map((map) => TaskModel.fromMap(map)).toList(); }); } // ========================================================================= // CREAZIONE (Insert) // ========================================================================= Future createTask({ required TaskModel task, required List assignedStaffIds, required String currentUserId, required List currentUserCustomReminders, TaskReminderConfig? managerForcedOverride, }) async { try { // 1. Inseriamo il Task principale per farci generare l'ID dal DB final taskResponse = await _supabase .from('tasks') .insert(task.toMap()) // Assicurati che toMap() escluda l'id se è null .select('id') .single(); final String taskId = taskResponse['id']; // 2. Inseriamo le Assegnazioni (tabella task_assignments) if (assignedStaffIds.isNotEmpty) { final assignmentsToInsert = assignedStaffIds .map( (staffId) => { 'task_id': taskId, 'staff_id': staffId, 'company_id': task.companyId, }, ) .toList(); await _supabase.from('task_assignments').insert(assignmentsToInsert); } // Se non c'è data di scadenza, niente promemoria a tempo if (task.dueDate == null || assignedStaffIds.isEmpty) return; // 3. Setup Reminder: Peschiamo i default degli ALTRI dipendenti coinvolti final otherStaffIds = assignedStaffIds .where((id) => id != currentUserId) .toList(); List otherDefaults = []; if (otherStaffIds.isNotEmpty) { otherDefaults = await _supabase .from('staff_task_reminder_defaults') .select() .inFilter('staff_id', otherStaffIds); } // 4. Creiamo la lista Bulk Insert per la tabella task_reminders List> remindersToInsert = []; for (var staffId in assignedStaffIds) { // A) Se è l'utente loggato -> usa i reminder configurati nel form if (staffId == currentUserId) { for (var config in currentUserCustomReminders) { final triggerAt = task.dueDate!.subtract( Duration(minutes: config.minutesBefore), ); if (triggerAt.isAfter(DateTime.now())) { remindersToInsert.add( _buildReminderRow( task, taskId, staffId, config, triggerAt, false, ), ); } } } // B) Se è un collega -> eredita i suoi default preimpostati else { final staffRules = otherDefaults.where( (row) => row['staff_id'] == staffId, ); for (var rule in staffRules) { final config = TaskReminderConfig( minutesBefore: rule['minutes_before'], channel: rule['channel'], ); final triggerAt = task.dueDate!.subtract( Duration(minutes: config.minutesBefore), ); if (triggerAt.isAfter(DateTime.now())) { remindersToInsert.add( _buildReminderRow( task, taskId, staffId, config, triggerAt, false, ), ); } } } // C) Override forzato del manager (per tutti) if (managerForcedOverride != null) { final triggerAt = task.dueDate!.subtract( Duration(minutes: managerForcedOverride.minutesBefore), ); if (triggerAt.isAfter(DateTime.now())) { remindersToInsert.add( _buildReminderRow( task, taskId, staffId, managerForcedOverride, triggerAt, true, ), ); } } } // 5. Inserimento massivo finale if (remindersToInsert.isNotEmpty) { await _supabase.from('task_reminders').insert(remindersToInsert); } } catch (e) { throw Exception('Errore durante la creazione del task: $e'); } } // ========================================================================= // AGGIORNAMENTO (Update) // ========================================================================= Future updateTask({ required TaskModel task, required List assignedStaffIds, required String currentUserId, required List currentUserCustomReminders, }) async { try { final taskId = task.id!; // 1. Aggiornamento dati Task Base await _supabase .from('tasks') .update({ 'title': task.title, 'description': task.description, 'due_date': task.dueDate?.toIso8601String(), 'store_id': task.storeId, 'updated_at': DateTime.now().toIso8601String(), }) .eq('id', taskId); // 2. Aggiornamento Assegnazioni: eliminiamo le vecchie, inseriamo le nuove await _supabase.from('task_assignments').delete().eq('task_id', taskId); if (assignedStaffIds.isNotEmpty) { final assignmentsToInsert = assignedStaffIds .map( (staffId) => { 'task_id': taskId, 'staff_id': staffId, 'company_id': task.companyId, }, ) .toList(); await _supabase.from('task_assignments').insert(assignmentsToInsert); } // Se non c'è una data, eliminiamo tutti i vecchi promemoria dell'utente loggato per pulizia if (task.dueDate == null) { await _supabase .from('task_reminders') .delete() .eq('task_id', taskId) .eq('staff_id', currentUserId) .eq('is_forced', false); return; } // 3. GESTIONE REMINDER: Puliamo SOLO quelli modificabili dall'utente loggato await _supabase .from('task_reminders') .delete() .eq('task_id', taskId) .eq('staff_id', currentUserId) .eq('is_forced', false); // NON tocchiamo quelli forzati dal manager! // 4. Inseriamo le nuove configurazioni salvate dal Cubit (solo se è ancora tra gli assegnatari) if (assignedStaffIds.contains(currentUserId) && currentUserCustomReminders.isNotEmpty) { final List> toInsert = []; for (var config in currentUserCustomReminders) { final triggerAt = task.dueDate!.subtract( Duration(minutes: config.minutesBefore), ); if (triggerAt.isAfter(DateTime.now())) { toInsert.add( _buildReminderRow( task, taskId, currentUserId, config, triggerAt, false, ), ); } } if (toInsert.isNotEmpty) { await _supabase.from('task_reminders').insert(toInsert); } } } catch (e) { throw Exception('Errore durante l\'aggiornamento del task: $e'); } } // --- HELPER PRIVATO PER LA MAPPA DEL REMINDER --- Map _buildReminderRow( TaskModel task, String taskId, String staffId, TaskReminderConfig config, DateTime triggerAt, bool isForced, ) { return { 'company_id': task.companyId, 'task_id': taskId, 'staff_id': staffId, 'minutes_before': config.minutesBefore, 'channel': config.channel, 'trigger_at': triggerAt.toIso8601String(), 'is_forced': isForced, 'is_sent': false, }; } }