import 'package:flutter/material.dart'; import 'package:flux/core/blocs/session/session_cubit.dart'; import 'package:flux/features/tickets/models/ticket_model.dart'; import 'package:get_it/get_it.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; class TicketRepository { final SupabaseClient _supabase = GetIt.I.get(); TicketRepository(); static const String _tableName = 'ticket'; // --- RECUPERO PAGINATO CON FILTRI E JOIN DEI TICKET DI UNO STORE --- Future> fetchStoreTickets({ required int offset, int limit = 50, String? searchTerm, DateTimeRange? dateRange, TicketStatus? statusFilter, TicketType? ticketTypeFilter, String? staffIdFilter, }) async { try { var query = _supabase .from(_tableName) .select(''' *, customer (*), created_by:staff_member!ticket_staff_id_fkey (*), assigned_to:staff_member!ticket_assigned_to_id_fkey (*), target_model:model!ticket_model_id_1_fkey (*), source_model:model!ticket_model_id_2_fkey (*) ''') .eq('store_id', GetIt.I.get().state.currentStore!.id!); // Filtro Range Date if (dateRange != null) { query = query .gte('created_at', dateRange.start.toIso8601String()) .lte('created_at', dateRange.end.toIso8601String()); } if (statusFilter != null) { query = query.eq('status', statusFilter.value); } if (ticketTypeFilter != null) { query = query.eq('ticket_type', ticketTypeFilter.value); } if (staffIdFilter != null) { query = query.eq('staff_id', staffIdFilter); } if (searchTerm != null && searchTerm.isNotEmpty) { // Filtra sui campi della tabella principale O su quelli della tabella joinata query = query.or('customer.name.ilike.%$searchTerm%'); } final response = await query .order('created_at', ascending: false) .range(offset, offset + limit - 1); return (response as List).map((map) => TicketModel.fromMap(map)).toList(); } catch (e) { throw Exception('$e'); } } // --- RECUPERO PAGINATO CON FILTRI E JOIN DEI TICKET DI TUTTA L'AZIENDA --- Future> fetchCompanyTickets({ required int offset, int limit = 50, String? searchTerm, DateTimeRange? dateRange, TicketStatus? ticketStatusFilter, TicketType? ticketTypeFilter, String? staffIdFilter, }) async { try { var query = _supabase .from(_tableName) .select(''' *, customer (*), created_by:staff_member!ticket_staff_id_fkey (*), assigned_to:staff_member!ticket_assigned_to_id_fkey (*), target_model:model!ticket_model_id_1_fkey (*), source_model:model!ticket_model_id_2_fkey (*) ''') .eq('company_id', GetIt.I.get().state.company!.id!); // Filtro Range Date if (dateRange != null) { query = query .gte('created_at', dateRange.start.toIso8601String()) .lte('created_at', dateRange.end.toIso8601String()); } if (ticketStatusFilter != null) { query = query.eq('status', ticketStatusFilter.value); } if (ticketTypeFilter != null) { query = query.eq('ticket_type', ticketTypeFilter.value); } if (staffIdFilter != null) { query = query.eq('staff_id', staffIdFilter); } if (searchTerm != null && searchTerm.isNotEmpty) { // Filtra sui campi della tabella principale O su quelli della tabella joinata query = query.or('customer.name.ilike.%$searchTerm%'); } final response = await query .order('created_at', ascending: false) .range(offset, offset + limit - 1); return (response as List).map((map) => TicketModel.fromMap(map)).toList(); } catch (e) { throw Exception('$e'); } } /// Stream dei ticket che necessitano attenzione (es. in scadenza oggi o in ritardo) Stream> getAttentionNeededTicketsStream() { return _supabase .from(_tableName) .stream(primaryKey: ['id']) .eq('store_id', GetIt.I.get().state.currentStore!.id!) // Purtroppo lo stream accetta solo filtri base, quindi ci facciamo // mandare i dati e li filtriamo con la potenza di Dart! .limit(300) .map((listOfMaps) { final now = DateTime.now(); final endOfToday = DateTime(now.year, now.month, now.day, 23, 59, 59); // 1. Mappiamo tutto in TicketModel final allStoreTickets = listOfMaps .map((map) => TicketModel.fromMap(map)) .toList(); // 2. Filtriamo in memoria! final urgentTickets = allStoreTickets.where((ticket) { // Escludiamo quelli già chiusi o consegnati if (ticket.ticketStatus == TicketStatus.closed || ticket.ticketStatus == TicketStatus.ready) { return false; } // Se c'è una data di consegna stimata ed è <= a stasera, è urgente! if (ticket.estimatedDeliveryAt != null) { return ticket.estimatedDeliveryAt!.isBefore(endOfToday); } return false; }).toList(); // 3. Li ordiniamo mettendo i più vecchi/urgenti in cima urgentTickets.sort( (a, b) => a.estimatedDeliveryAt!.compareTo(b.estimatedDeliveryAt!), ); return urgentTickets; }); } /// Recupera un ticket specifico CON TUTTE LE RELAZIONI espanse (Cliente e Modelli) /// Questa è la vera magia di Supabase! Future getTicketById(String ticketId) async { try { // Usiamo i nomi esatti delle Foreign Key che hai definito nell'SQL! final response = await _supabase .from(_tableName) .select(''' *, customer (*), target_model:model!ticket_model_id_1_fkey (*), source_model:model!ticket_model_id_2_fkey (*), created_by:staff_member!ticket_staff_id_fkey (*), assigned_to:staff_member!ticket_assigned_to_id_fkey (*), ''') .eq('id', ticketId) .single(); return TicketModel.fromMap(response); } catch (e) { throw Exception('Errore nel recupero del dettaglio ticket: $e'); } } /// Salva il ticket con upsert Future saveTicket(TicketModel ticket) async { try { final response = await _supabase .from(_tableName) .upsert(ticket.toMap()) .select() .single(); return TicketModel.fromMap(response); } catch (e) { throw Exception('Errore nella creazione del ticket: $e'); } } /// Aggiorna un ticket esistente Future updateTicket(TicketModel ticket) async { if (ticket.id == null) { throw Exception('Impossibile aggiornare un ticket senza ID'); } try { final response = await _supabase .from(_tableName) .update(ticket.toMap()) .eq('id', ticket.id!) .select() .single(); return TicketModel.fromMap(response); } catch (e) { throw Exception('Errore nell\'aggiornamento del ticket: $e'); } } /// Elimina (o annulla) un ticket Future deleteTicket(String ticketId) async { try { await _supabase.from(_tableName).delete().eq('id', ticketId); } catch (e) { throw Exception('Errore nell\'eliminazione del ticket: $e'); } } }