import 'dart:async';

import 'package:flutter/foundation.dart';

import 'package:pshared/models/payment/payment.dart';
import 'package:pshared/provider/payment/multiple/provider.dart';
import 'package:pshared/provider/payment/payments.dart';
import 'package:pshared/provider/payment/provider.dart';

class PaymentsUpdatesProvider extends ChangeNotifier {
  PaymentsProvider? _payments;
  PaymentProvider? _paymentProvider;
  MultiPaymentProvider? _multiPaymentProvider;
  Set<String> _knownKeys = {};

  void update({
    required PaymentsProvider paymentsProvider,
    required PaymentProvider paymentProvider,
    required MultiPaymentProvider multiPaymentProvider,
  }) {
    if (!identical(_payments, paymentsProvider)) {
      _payments = paymentsProvider;
      _syncKnownKeys();
    }
    if (!identical(_paymentProvider, paymentProvider)) {
      _paymentProvider?.removeListener(_onSinglePaymentChanged);
      _paymentProvider = paymentProvider;
      _paymentProvider?.addListener(_onSinglePaymentChanged);
    }
    if (!identical(_multiPaymentProvider, multiPaymentProvider)) {
      _multiPaymentProvider?.removeListener(_onMultiPaymentChanged);
      _multiPaymentProvider = multiPaymentProvider;
      _multiPaymentProvider?.addListener(_onMultiPaymentChanged);
    }
    _syncKnownKeys();
  }

  void _syncKnownKeys() {
    _knownKeys = {
      for (final payment in _payments?.payments ?? const <Payment>[])
        ..._keyFor(payment),
    };
  }

  void _onSinglePaymentChanged() {
    final payment = _paymentProvider?.payment;
    if (payment == null) return;
    final key = _key(payment);
    if (key != null && _knownKeys.contains(key)) return;
    _merge([payment]);
  }

  void _onMultiPaymentChanged() {
    final payments = _multiPaymentProvider?.payments ?? const <Payment>[];
    if (payments.isEmpty) return;
    final incoming = <Payment>[];
    for (final payment in payments) {
      final key = _key(payment);
      if (key == null || !_knownKeys.contains(key)) {
        incoming.add(payment);
      }
    }
    if (incoming.isEmpty) return;
    _merge(incoming);
  }

  void _merge(List<Payment> incoming) {
    if (incoming.isEmpty) return;
    _payments?.mergePayments(incoming);
    unawaited(_payments?.refreshCurrentQuerySilently());
    for (final payment in incoming) {
      final key = _key(payment);
      if (key != null) {
        _knownKeys.add(key);
      }
    }
  }

  String? _key(Payment payment) {
    final ref = payment.paymentRef?.trim();
    if (ref != null && ref.isNotEmpty) return ref;
    return null;
  }

  Iterable<String> _keyFor(Payment payment) sync* {
    final key = _key(payment);
    if (key != null) yield key;
  }

  @override
  void dispose() {
    _paymentProvider?.removeListener(_onSinglePaymentChanged);
    _multiPaymentProvider?.removeListener(_onMultiPaymentChanged);
    super.dispose();
  }
}
