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<PaymentType> _supportedTypes;

  final Map<PaymentType, List<RecipientMethodDraft>> _methods;
  SeedState _seedState = SeedState.idle;

  AddressBookRecipientFormProvider({
    required List<PaymentType> supportedTypes,
    Recipient? recipient,
  })  : _recipient = recipient,
        _supportedTypes = supportedTypes,
        _methods = {
          for (final type in supportedTypes) type: <RecipientMethodDraft>[],
        };

  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<PaymentType> get supportedTypes => List.unmodifiable(_supportedTypes);
  Map<PaymentType, List<RecipientMethodDraft>> get methods => {
    for (final entry in _methods.entries)
      entry.key: List<RecipientMethodDraft>.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<RecipientMethodDraft> 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<PaymentMethod> existing) {
    if (existing.isEmpty) return;
    final next = <PaymentType, List<RecipientMethodDraft>>{
      for (final type in _supportedTypes) type: <RecipientMethodDraft>[],
    };
    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<Recipient> save({
    required String name,
    required String email,
    required Map<PaymentType, String> 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();
  }
}
