// lib/features/routes/data/repositories/routes_repository.dart import '../datasources/routes_local_datasource.dart'; import '../../domain/entities/route_entities.dart'; import '../../domain/entities/haversine.dart'; class RoutesRepository { final RoutesLocalDatasource _datasource; RoutesRepository({RoutesLocalDatasource? datasource}) : _datasource = datasource ?? RoutesLocalDatasource(); /// Obtiene todas las rutas disponibles Future> obtenerRutas() => _datasource.cargarRutas(); /// Obtiene una ruta por su ID Future obtenerRutaPorId(String routeId) async { final rutas = await obtenerRutas(); try { return rutas.firstWhere((r) => r.routeId == routeId); } catch (_) { return null; } } /// Asigna la ruta más cercana a un domicilio usando Haversine. /// Cero Google Maps — motor espacial propio. Future asignarRuta( double userLat, double userLng, ) async { final centroides = await _datasource.generarCentroides(); return Haversine.findNearestRoute(userLat, userLng, centroides); } /// Calcula ETA en minutos desde la posición actual del camión /// hasta el punto más cercano al domicilio del usuario. Future calcularETA( String routeId, double userLat, double userLng, ) async { final ruta = await obtenerRutaPorId(routeId); if (ruta == null) throw Exception('Ruta $routeId no encontrada'); // Encontrar posición más cercana al usuario en la ruta double minDist = double.infinity; int nearestIndex = 0; for (int i = 0; i < ruta.positions.length - 1; i++) { final pos = ruta.positions[i]; final d = Haversine.distanceKm(userLat, userLng, pos.lat, pos.lng); if (d < minDist) { minDist = d; nearestIndex = i; } } final posicionCercana = ruta.positions[nearestIndex]; final ahora = DateTime.now(); // Simular cuándo llegará el camión a esa posición final horaLlegada = posicionCercana.timestamp; final etaMinutos = horaLlegada.difference(ahora).inMinutes; return ETAResult( routeId: routeId, routeName: ruta.name, etaMinutos: etaMinutos.clamp(0, 999), distanciaKm: minDist, posicionActual: posicionCercana, ventanaInicio: _formatHora(horaLlegada), ventanaFin: _formatHora(horaLlegada.add(const Duration(minutes: 15))), ); } String _formatHora(DateTime dt) { final hora = dt.hour > 12 ? dt.hour - 12 : dt.hour; final minutos = dt.minute.toString().padLeft(2, '0'); final periodo = dt.hour >= 12 ? 'pm' : 'am'; return '$hora:$minutos $periodo'; } } class ETAResult { final String routeId; final String routeName; final int etaMinutos; final double distanciaKm; final RoutePosition posicionActual; final String ventanaInicio; final String ventanaFin; const ETAResult({ required this.routeId, required this.routeName, required this.etaMinutos, required this.distanciaKm, required this.posicionActual, required this.ventanaInicio, required this.ventanaFin, }); String get ventana => '$ventanaInicio – $ventanaFin'; String get mensaje { if (etaMinutos <= 0) return 'El camión está pasando ahora. ¡Saca tu basura!'; if (etaMinutos <= 10) return 'El camión llega en ~$etaMinutos min. ¡Saca tu basura!'; return 'El camión llega entre $ventana.'; } }