import 'dart:convert'; import 'dart:typed_data'; import '../datasources/local_database.dart'; import '../datasources/backend_api.dart'; import '../../core/constants/app_constants.dart'; import '../../core/errors/exceptions.dart'; import '../../core/utils/retry_utils.dart'; import '../../domain/entities/contenedor.dart'; import '../../domain/entities/destino.dart'; import '../../domain/entities/pendiente.dart'; class ContenedorRepository { final BackendApi _api = BackendApi(); Future> enviar(Contenedor contenedor, Destino destino) async { try { final result = await _api.enviarContenedor(contenedor, destino); await _registrarEnvio(contenedor, destino, result); return result; } catch (e) { await _agregarAPendientes(contenedor, destino); rethrow; } } Future _registrarEnvio( Contenedor contenedor, Destino destino, Map response, ) async { final db = await LocalDatabase.database; await db.insert('registro', { 'hash': contenedor.hash, 'titulo': contenedor.titulo, 'hora_envio': DateTime.now().toIso8601String(), 'primera_conf': response['received_at'], 'ultima_conf': response['received_at'], 'destino_id': destino.id, }); } // Pendientes Future _agregarAPendientes(Contenedor contenedor, Destino destino) async { final count = await getPendientesCount(); if (count >= AppConstants.maxPendientes) { throw QueueFullException(); } final db = await LocalDatabase.database; final contenidoJson = jsonEncode(contenedor.toJson()); await db.insert('pendientes', { 'hash': contenedor.hash, 'titulo': contenedor.titulo, 'contenido': Uint8List.fromList(utf8.encode(contenidoJson)), 'intentos': 1, 'ultimo_intento': DateTime.now().toIso8601String(), 'proximo_intento': RetryUtils.calculateNextRetry(1).toIso8601String(), 'destino_id': destino.id, }); } Future getPendientesCount() async { final db = await LocalDatabase.database; final result = await db.rawQuery('SELECT COUNT(*) as count FROM pendientes'); return result.first['count'] as int; } Future> getPendientes() async { final db = await LocalDatabase.database; final results = await db.query('pendientes', orderBy: 'ultimo_intento DESC'); return results.map((m) => Pendiente.fromMap(m)).toList(); } Future reintentar(Pendiente pendiente, Destino destino) async { if (!pendiente.puedeReintentar) return; final db = await LocalDatabase.database; final contenidoStr = utf8.decode(pendiente.contenido); final contenidoJson = jsonDecode(contenidoStr) as Map; // Reconstruct basic contenedor for retry final contenedor = Contenedor( hash: contenidoJson['hash'] as String, titulo: contenidoJson['titulo'] as String?, descripcion: contenidoJson['descripcion'] as String?, etiquetas: (contenidoJson['etiquetas'] as List?) ?.map((e) => e as String) .toList() ?? [], ); try { await _api.enviarContenedor(contenedor, destino); await db.delete('pendientes', where: 'hash = ?', whereArgs: [pendiente.hash]); } catch (e) { final nuevoIntento = pendiente.intentos + 1; if (nuevoIntento >= AppConstants.maxReintentos) { // Keep in queue but mark as exhausted await db.update( 'pendientes', { 'intentos': nuevoIntento, 'ultimo_intento': DateTime.now().toIso8601String(), 'proximo_intento': null, }, where: 'hash = ?', whereArgs: [pendiente.hash], ); } else { await db.update( 'pendientes', { 'intentos': nuevoIntento, 'ultimo_intento': DateTime.now().toIso8601String(), 'proximo_intento': RetryUtils.calculateNextRetry(nuevoIntento).toIso8601String(), }, where: 'hash = ?', whereArgs: [pendiente.hash], ); } } } Future eliminarPendiente(String hash) async { final db = await LocalDatabase.database; await db.delete('pendientes', where: 'hash = ?', whereArgs: [hash]); } }