import 'package:hive_ce/hive.dart'; import '../../../../core/errors/exceptions.dart'; import '../models/scan_item.dart'; /// Abstract local data source for scanner operations abstract class ScannerLocalDataSource { /// Save scan to local storage Future saveScan(ScanItem scan); /// Get all scans from local storage Future> getAllScans(); /// Get scan by barcode from local storage Future getScanByBarcode(String barcode); /// Update scan in local storage Future updateScan(ScanItem scan); /// Delete scan from local storage Future deleteScan(String barcode); /// Clear all scans from local storage Future clearAllScans(); } /// Implementation of ScannerLocalDataSource using Hive class ScannerLocalDataSourceImpl implements ScannerLocalDataSource { static const String _boxName = 'scans'; Box? _box; /// Initialize Hive box Future> _getBox() async { if (_box == null || !_box!.isOpen) { try { _box = await Hive.openBox(_boxName); } catch (e) { throw CacheException('Failed to open Hive box: ${e.toString()}'); } } return _box!; } @override Future saveScan(ScanItem scan) async { try { final box = await _getBox(); // Use barcode as key to avoid duplicates await box.put(scan.barcode, scan); // Optional: Log the save operation // print('Scan saved locally: ${scan.barcode}'); } on CacheException { rethrow; } catch (e) { throw CacheException('Failed to save scan locally: ${e.toString()}'); } } @override Future> getAllScans() async { try { final box = await _getBox(); // Get all values from the box final scans = box.values.toList(); // Sort by timestamp (most recent first) scans.sort((a, b) => b.timestamp.compareTo(a.timestamp)); return scans; } on CacheException { rethrow; } catch (e) { throw CacheException('Failed to get scans from local storage: ${e.toString()}'); } } @override Future getScanByBarcode(String barcode) async { try { if (barcode.trim().isEmpty) { throw const ValidationException('Barcode cannot be empty'); } final box = await _getBox(); // Get scan by barcode key return box.get(barcode); } on ValidationException { rethrow; } on CacheException { rethrow; } catch (e) { throw CacheException('Failed to get scan by barcode: ${e.toString()}'); } } @override Future updateScan(ScanItem scan) async { try { final box = await _getBox(); // Check if scan exists if (!box.containsKey(scan.barcode)) { throw CacheException('Scan with barcode ${scan.barcode} not found'); } // Update the scan await box.put(scan.barcode, scan); // Optional: Log the update operation // print('Scan updated locally: ${scan.barcode}'); } on CacheException { rethrow; } catch (e) { throw CacheException('Failed to update scan locally: ${e.toString()}'); } } @override Future deleteScan(String barcode) async { try { if (barcode.trim().isEmpty) { throw const ValidationException('Barcode cannot be empty'); } final box = await _getBox(); // Check if scan exists if (!box.containsKey(barcode)) { throw CacheException('Scan with barcode $barcode not found'); } // Delete the scan await box.delete(barcode); // Optional: Log the delete operation // print('Scan deleted locally: $barcode'); } on ValidationException { rethrow; } on CacheException { rethrow; } catch (e) { throw CacheException('Failed to delete scan locally: ${e.toString()}'); } } @override Future clearAllScans() async { try { final box = await _getBox(); // Clear all scans await box.clear(); // Optional: Log the clear operation // print('All scans cleared from local storage'); } on CacheException { rethrow; } catch (e) { throw CacheException('Failed to clear all scans: ${e.toString()}'); } } /// Get scans count (utility method) Future getScansCount() async { try { final box = await _getBox(); return box.length; } on CacheException { rethrow; } catch (e) { throw CacheException('Failed to get scans count: ${e.toString()}'); } } /// Check if scan exists (utility method) Future scanExists(String barcode) async { try { if (barcode.trim().isEmpty) { return false; } final box = await _getBox(); return box.containsKey(barcode); } on CacheException { rethrow; } catch (e) { throw CacheException('Failed to check if scan exists: ${e.toString()}'); } } /// Get scans within date range (utility method) Future> getScansByDateRange({ required DateTime startDate, required DateTime endDate, }) async { try { final allScans = await getAllScans(); // Filter by date range final filteredScans = allScans.where((scan) { return scan.timestamp.isAfter(startDate) && scan.timestamp.isBefore(endDate); }).toList(); return filteredScans; } on CacheException { rethrow; } catch (e) { throw CacheException('Failed to get scans by date range: ${e.toString()}'); } } /// Close the Hive box (call this when app is closing) Future dispose() async { if (_box != null && _box!.isOpen) { await _box!.close(); _box = null; } } }