import 'package:flutter/foundation.dart'; import 'package:pshared/data/mapper/recipient/recipient.dart'; import 'package:pshared/models/describable.dart'; import 'package:pshared/models/payment/methods/data.dart'; import 'package:pshared/models/payment/methods/type.dart'; import 'package:pshared/models/payment/type.dart'; import 'package:pshared/models/recipient/payment_method_draft.dart'; import 'package:pshared/models/recipient/recipient.dart'; import 'package:pshared/provider/recipient/methods_cache.dart'; import 'package:pshared/provider/recipient/provider.dart'; import 'package:pweb/models/seed_state.dart'; class AddressBookRecipientFormProvider extends ChangeNotifier { RecipientMethodsCacheProvider? _methodsCache; RecipientsProvider? _recipientsProvider; final Recipient? _recipient; final List _supportedTypes; final Map> _methods; SeedState _seedState = SeedState.idle; AddressBookRecipientFormProvider({ required List supportedTypes, Recipient? recipient, }) : _recipient = recipient, _supportedTypes = supportedTypes, _methods = { for (final type in supportedTypes) type: [], }; void updateProviders({ required RecipientMethodsCacheProvider methodsCache, required RecipientsProvider recipientsProvider, }) { _recipientsProvider = recipientsProvider; if (identical(_methodsCache, methodsCache)) return; _methodsCache?.removeListener(_handleCacheChange); _methodsCache = methodsCache; _methodsCache?.addListener(_handleCacheChange); _maybeSeedFromCache(); } List get supportedTypes => List.unmodifiable(_supportedTypes); Map> get methods => { for (final entry in _methods.entries) entry.key: List.unmodifiable(entry.value), }; PaymentType? get preferredType => _supportedTypes.firstWhere((type) => _methods[type]?.isNotEmpty == true, orElse: () => _supportedTypes.first); bool get hasAnyMethod => _methods.values.any( (entries) => entries.any((entry) => entry.data != null || entry.existing != null), ); List allDrafts() => _methods.values.expand((entries) => entries).toList(); int? addMethod(PaymentType type) { final entries = _methods[type]; if (entries == null) return null; entries.add(RecipientMethodDraft(type: type)); notifyListeners(); return entries.length - 1; } void removeMethod(PaymentType type, int index) { final entries = _methods[type]; if (entries == null) return; if (index < 0 || index >= entries.length) return; entries.removeAt(index); notifyListeners(); } void updateMethod(PaymentType type, int index, PaymentMethodData data) { final entries = _methods[type]; if (entries == null) return; if (index < 0 || index >= entries.length) return; entries[index].data = data; notifyListeners(); } void _handleCacheChange() { _maybeSeedFromCache(); } void _maybeSeedFromCache() { final recipient = _recipient; final methodsCache = _methodsCache; if (recipient == null || methodsCache == null) return; if (_seedState == SeedState.seeded) return; if (!methodsCache.hasMethodsFor(recipient.id)) return; _seedState = SeedState.seeded; _seedMethodsFromExisting(methodsCache.methodsForRecipient(recipient.id)); } void _seedMethodsFromExisting(List existing) { if (existing.isEmpty) return; final next = >{ for (final type in _supportedTypes) type: [], }; for (final method in existing) { final type = method.type; final entries = next[type]; if (entries == null) continue; entries.add( RecipientMethodDraft( type: type, existing: method, data: method.data, ), ); } _methods ..clear() ..addAll(next); notifyListeners(); } Future save({ required String name, required String email, required Map methodNames, }) async { final recipientsProvider = _recipientsProvider; final methodsCache = _methodsCache; if (recipientsProvider == null || methodsCache == null) { throw StateError('Form provider dependencies are not set'); } final trimmedName = name.trim(); final trimmedEmail = email.trim(); if (_recipient == null) { final created = await recipientsProvider.create( name: trimmedName, email: trimmedEmail, ); await methodsCache.syncRecipientMethods( recipientId: created.id, methods: allDrafts(), names: methodNames, ); return created; } final updated = _recipient.copyWith( describable: newDescribable( name: trimmedName, description: _recipient.description, ), email: trimmedEmail, ); await recipientsProvider.update(updated.toDTO().toJson()); await methodsCache.syncRecipientMethods( recipientId: updated.id, methods: allDrafts(), names: methodNames, ); return updated; } @override void dispose() { _methodsCache?.removeListener(_handleCacheChange); super.dispose(); } }