import 'dart:convert'; import 'dart:io'; import 'package:path/path.dart' as p; import 'package:path_provider/path_provider.dart'; import '../models/fuel_entry.dart'; import '../models/vehicle.dart'; /// Owns the local, offline staging area: a `data.json` file holding /// vehicles and fuel entries, plus a `receipts/` subfolder holding receipt /// photos not yet uploaded to Drive. This is always /// `ApplicationDocumentsDirectory/FuelTaxTracker` — not user-configurable — /// since Drive (via [DriveSyncService]) is now the real shared destination /// and this is just a local cache/holding area used while offline. class StorageService { static const _dataFileName = 'fuel_tax_data.json'; static const _receiptsFolderName = 'receipts'; late Directory _saveDirectory; Directory get saveDirectory => _saveDirectory; Directory get receiptsDirectory => Directory(p.join(_saveDirectory.path, _receiptsFolderName)); File get dataFile => File(p.join(_saveDirectory.path, _dataFileName)); /// Must be called once before any other method. Future init() async { final docsDir = await getApplicationDocumentsDirectory(); _saveDirectory = Directory(p.join(docsDir.path, 'FuelTaxTracker')); await _saveDirectory.create(recursive: true); await receiptsDirectory.create(recursive: true); } Future<({List vehicles, List entries})> loadData() async { if (!await dataFile.exists()) { return (vehicles: [], entries: []); } final raw = await dataFile.readAsString(); if (raw.trim().isEmpty) { return (vehicles: [], entries: []); } return decodeData(raw); } ({List vehicles, List entries}) decodeData(String raw) { final json = jsonDecode(raw) as Map; final vehicles = (json['vehicles'] as List? ?? []) .map((v) => Vehicle.fromJson(v as Map)) .toList(); final entries = (json['fuelEntries'] as List? ?? []) .map((e) => FuelEntry.fromJson(e as Map)) .toList(); return (vehicles: vehicles, entries: entries); } String encodeData({ required List vehicles, required List entries, }) { final json = { 'vehicles': vehicles.map((v) => v.toJson()).toList(), 'fuelEntries': entries.map((e) => e.toJson()).toList(), }; return const JsonEncoder.withIndent(' ').convert(json); } Future saveData({ required List vehicles, required List entries, }) async { await dataFile.writeAsString(encodeData(vehicles: vehicles, entries: entries)); } /// Copies a captured receipt image into the local receipts folder and /// returns the path it was stored at, pending upload to Drive. Future storeReceiptImage(File sourceImage, String fuelEntryId) async { final ext = p.extension(sourceImage.path); final destPath = p.join(receiptsDirectory.path, '$fuelEntryId$ext'); final copied = await sourceImage.copy(destPath); return copied.path; } Future deleteReceiptImage(String? path) async { if (path == null) return; final file = File(path); if (await file.exists()) { await file.delete(); } } }