import 'dart:convert'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import '../../../common/domain/metal_type.dart'; import '../../../common/storage/preferences_storage.dart'; import '../../../common/storage/storage_keys.dart'; import 'asset_models.dart'; final mockAssetRepositoryProvider = Provider( (ref) => MockAssetRepository(ref.watch(preferencesStorageProvider)), ); class MockAssetRepository { MockAssetRepository(this._preferences); static const guestAccountScope = 'guest'; final PreferencesStorage _preferences; Future _operationTail = Future.value(); static String accountScope({String? userId, String? phone}) { final normalizedUserId = userId?.trim() ?? ''; if (normalizedUserId.isNotEmpty) return 'id:$normalizedUserId'; final normalizedPhone = phone?.trim() ?? ''; if (normalizedPhone.isNotEmpty) return 'phone:$normalizedPhone'; return guestAccountScope; } static String holdingsStorageKey(String accountScope) => '$storageJinzhiHoldings:${Uri.encodeComponent(accountScope)}'; static String deletedStorageKey(String accountScope) => '$storageJinzhiHoldingsDeleted:${Uri.encodeComponent(accountScope)}'; List loadHoldings(String accountScope) { if (_preferences.getBool(deletedStorageKey(accountScope))) return []; final encoded = _preferences.getString(holdingsStorageKey(accountScope)); if (encoded == null || encoded.isEmpty) return _seedHoldings(); try { final decoded = jsonDecode(encoded); if (decoded is! List) return _seedHoldings(); return [ for (final item in decoded) GoldAssetHolding.fromJson(Map.from(item as Map)), ]; } catch (_) { return _seedHoldings(); } } Future saveHoldings( String accountScope, List holdings, ) async { final encoded = jsonEncode([ for (final holding in holdings) holding.toJson(), ]); await _serialize(() async { await _preferences.setString(holdingsStorageKey(accountScope), encoded); await _preferences.remove(deletedStorageKey(accountScope)); }); } Future deleteAccountHoldings(String accountScope) async { await _serialize(() async { final scopes = {accountScope, guestAccountScope}; await Future.wait([ for (final scope in scopes) ...[ _preferences.remove(holdingsStorageKey(scope)), _preferences.setBool(deletedStorageKey(scope), true), ], // Remove the pre-account-scoping key left by earlier builds. _preferences.remove(storageJinzhiHoldings), ]); }); } Future _serialize(Future Function() operation) { final result = _operationTail.then((_) => operation()); _operationTail = result.then( (_) {}, onError: (Object _, StackTrace _) {}, ); return result; } List _seedHoldings() { final now = DateTime(2026, 6, 18, 16, 11); return [ GoldAssetHolding( id: 'holding_gold_bar_001', name: '投资金条', metal: MetalType.gold, category: AssetCategory.bar, purity: 0.9999, purityLabel: '足金9999', weightGram: 50, costAmount: 43200, purchaseDate: DateTime(2025, 10, 12), channel: '银行', createdAt: now.subtract(const Duration(days: 249)), updatedAt: now, ), GoldAssetHolding( id: 'holding_gold_ring_001', name: '素圈戒指', metal: MetalType.gold, category: AssetCategory.ring, purity: 0.999, purityLabel: '足金999', weightGram: 8.6, costAmount: 7820, purchaseDate: DateTime(2024, 12, 4), channel: '金店', createdAt: now.subtract(const Duration(days: 196)), updatedAt: now, ), GoldAssetHolding( id: 'holding_platinum_001', name: 'PT950 项链', metal: MetalType.platinum, category: AssetCategory.platinumPiece, purity: 0.95, purityLabel: 'PT950', weightGram: 12.3, costAmount: 5200, purchaseDate: DateTime(2023, 8, 21), channel: '金店', createdAt: now.subtract(const Duration(days: 667)), updatedAt: now, ), GoldAssetHolding( id: 'holding_silver_001', name: '银手镯', metal: MetalType.silver, category: AssetCategory.bracelet, purity: 0.999, purityLabel: '999银', weightGram: 31.8, costAmount: 980, purchaseDate: DateTime(2024, 4, 7), channel: '金店', note: '材料价值偏低', createdAt: now.subtract(const Duration(days: 802)), updatedAt: now, ), ]; } }