import 'package:flutter/foundation.dart';

import 'package:pshared/controllers/balance_mask/wallets.dart';
import 'package:pshared/models/ledger/account.dart';
import 'package:pshared/models/payment/source_type.dart';
import 'package:pshared/models/payment/wallet.dart';
import 'package:pshared/provider/ledger.dart';
import 'package:pshared/utils/currency.dart';


class PaymentSourceController with ChangeNotifier {
  WalletsController? _walletsController;

  List<Wallet> _wallets = const <Wallet>[];
  Map<String, Wallet> _walletsById = const <String, Wallet>{};

  List<LedgerAccount> _ledgerAccounts = const <LedgerAccount>[];
  Map<String, LedgerAccount> _ledgerByRef = const <String, LedgerAccount>{};

  PaymentSourceType? _selectedType;
  String? _selectedRef;

  List<Wallet> get wallets => _wallets;
  List<LedgerAccount> get ledgerAccounts => _ledgerAccounts;
  bool get hasSources => _wallets.isNotEmpty || _ledgerAccounts.isNotEmpty;

  PaymentSourceType? get selectedType => _selectedType;
  String? get selectedRef => _selectedRef;

  Wallet? get selectedWallet {
    if (_selectedType != PaymentSourceType.wallet) return null;
    final ref = _selectedRef;
    if (ref == null) return null;
    return _walletsById[ref];
  }

  LedgerAccount? get selectedLedgerAccount {
    if (_selectedType != PaymentSourceType.ledger) return null;
    final ref = _selectedRef;
    if (ref == null) return null;
    return _ledgerByRef[ref];
  }

  String? get selectedCurrencyCode {
    final wallet = selectedWallet;
    if (wallet != null) {
      return currencyCodeToString(wallet.currency);
    }

    final ledger = selectedLedgerAccount;
    if (ledger != null) {
      final code = ledger.currency.trim().toUpperCase();
      return code.isEmpty ? null : code;
    }

    return null;
  }

  void update(WalletsController wallets, LedgerAccountsProvider ledger) {
    _walletsController = wallets;

    _walletsById = _uniqueWalletsById(wallets.wallets);
    _wallets = _walletsById.values.toList(growable: false);

    _ledgerByRef = _uniqueLedgerByRef(ledger.accounts);
    _ledgerAccounts = _ledgerByRef.values.toList(growable: false);

    _syncSelection();
    notifyListeners();
  }

  void selectWallet(Wallet wallet) {
    selectWalletByRef(wallet.id);
  }

  void selectWalletByRef(String walletRef) {
    if (!_walletsById.containsKey(walletRef)) return;
    _walletsController?.selectWalletByRef(walletRef);
    _setSelection(PaymentSourceType.wallet, walletRef);
  }

  void selectLedgerByRef(String ledgerAccountRef) {
    if (!_ledgerByRef.containsKey(ledgerAccountRef)) return;
    _setSelection(PaymentSourceType.ledger, ledgerAccountRef);
  }

  bool isWalletSelected(String walletRef) {
    return _selectedType == PaymentSourceType.wallet &&
        _selectedRef == walletRef;
  }

  bool isLedgerSelected(String ledgerAccountRef) {
    return _selectedType == PaymentSourceType.ledger &&
        _selectedRef == ledgerAccountRef;
  }

  void _syncSelection() {
    final currentType = _selectedType;
    final currentRef = _selectedRef;

    if (currentType == PaymentSourceType.wallet &&
        currentRef != null &&
        _walletsById.containsKey(currentRef)) {
      return;
    }

    if (currentType == PaymentSourceType.ledger &&
        currentRef != null &&
        _ledgerByRef.containsKey(currentRef)) {
      return;
    }

    final selectedWalletRef = _walletsController?.selectedWalletRef;
    if (selectedWalletRef != null &&
        _walletsById.containsKey(selectedWalletRef)) {
      _selectedType = PaymentSourceType.wallet;
      _selectedRef = selectedWalletRef;
      return;
    }

    if (_wallets.isNotEmpty) {
      _selectedType = PaymentSourceType.wallet;
      _selectedRef = _wallets.first.id;
      return;
    }

    if (_ledgerAccounts.isNotEmpty) {
      _selectedType = PaymentSourceType.ledger;
      _selectedRef = _ledgerAccounts.first.ledgerAccountRef;
      return;
    }

    _selectedType = null;
    _selectedRef = null;
  }

  void _setSelection(PaymentSourceType type, String ref) {
    if (_selectedType == type && _selectedRef == ref) return;
    _selectedType = type;
    _selectedRef = ref;
    notifyListeners();
  }

  Map<String, Wallet> _uniqueWalletsById(List<Wallet> wallets) {
    final result = <String, Wallet>{};
    for (final wallet in wallets) {
      result.putIfAbsent(wallet.id, () => wallet);
    }
    return result;
  }

  Map<String, LedgerAccount> _uniqueLedgerByRef(List<LedgerAccount> accounts) {
    final result = <String, LedgerAccount>{};
    for (final account in accounts) {
      result.putIfAbsent(account.ledgerAccountRef, () => account);
    }
    return result;
  }
}
