import 'package:collection/collection.dart';
import 'package:flutter/material.dart';

import 'package:pshared/models/payment/wallet.dart';
import 'package:pshared/provider/organizations.dart';
import 'package:pshared/provider/resource.dart';
import 'package:pshared/service/payment/wallets.dart';
import 'package:pshared/utils/exception.dart';



class WalletsProvider with ChangeNotifier {
  final WalletsService _service;
  late OrganizationsProvider _organizations;

  WalletsProvider(this._service);

  Resource<List<Wallet>> _resource = Resource(data: []);
  Resource<List<Wallet>> get resource => _resource;

  List<Wallet> get wallets => _resource.data ?? [];
  bool get isLoading => _resource.isLoading;
  Exception? get error => _resource.error;

  Wallet? _selectedWallet;
  Wallet? get selectedWallet => _selectedWallet;
  final bool _isHidden = true;
  bool get isHidden => _isHidden;

  bool _isRefreshingBalances = false;
  bool get isRefreshingBalances => _isRefreshingBalances;
  final Set<String> _refreshingWallets = <String>{};
  bool isWalletRefreshing(String walletId) => _refreshingWallets.contains(walletId);

  void update(OrganizationsProvider organizations) {
    _organizations = organizations;
    if (_organizations.isOrganizationSet) loadWalletsWithBalances();
  }

  Future<Wallet> updateWallet(Wallet newWallet) {
    throw Exception('update wallet is not implemented');
  }

  void selectWallet(Wallet wallet) => _setSelectedWallet(wallet);

  Future<void> loadWalletsWithBalances() async {
    _setResource(_resource.copyWith(isLoading: true, error: null));
    try {
      final base = await _service.getWallets(_organizations.current.id);
      final withBalances = <Wallet>[];
      for (final wallet in base) {
        try {
          final balance = await _service.getBalance(_organizations.current.id, wallet.id);
          withBalances.add(wallet.copyWith(balance: balance));
        } catch (e) {
          _setResource(_resource.copyWith(error: toException(e)));
          withBalances.add(wallet);
        }
      }
      _setResource(Resource(data: withBalances, isLoading: false, error: _resource.error));
    } catch (e) {
      _setResource(_resource.copyWith(isLoading: false, error: toException(e)));
    }
  }

  Future<void> refreshBalances() async {
    if (wallets.isEmpty) return;
    _isRefreshingBalances = true;
    notifyListeners();

    try {
      final updated = <Wallet>[];
      for (final wallet in wallets) {
        final balance = await _service.getBalance(_organizations.current.id, wallet.id);
        updated.add(wallet.copyWith(balance: balance));
      }
      _setResource(_resource.copyWith(data: updated));
    } catch (e) {
      _setResource(_resource.copyWith(error: toException(e)));
    } finally {
      _isRefreshingBalances = false;
      notifyListeners();
    }
  }

  Future<void> refreshBalance(String walletId) async {
    if (_refreshingWallets.contains(walletId)) return;
    final wallet = wallets.firstWhereOrNull((w) => w.id == walletId);
    if (wallet == null) return;

    _refreshingWallets.add(walletId);
    notifyListeners();

    try {
      final balance = await _service.getBalance(_organizations.current.id, walletId);
      final updatedWallet = wallet.copyWith(balance: balance);
      final next = List<Wallet>.from(wallets);
      final idx = next.indexWhere((w) => w.id == walletId);
      if (idx >= 0) {
        next[idx] = updatedWallet;
        _setResource(_resource.copyWith(data: next));
      }
    } catch (e) {
      _setResource(_resource.copyWith(error: toException(e)));
    } finally {
      _refreshingWallets.remove(walletId);
      notifyListeners();
    }
  }

  void toggleVisibility(String walletId) {
    final index = wallets.indexWhere((w) => w.id == walletId);
    if (index < 0) return;
    final wallet = wallets[index];
    final updated = wallet.copyWith(isHidden: !wallet.isHidden);
    final next = List<Wallet>.from(wallets);
    next[index] = updated;
    _setResource(_resource.copyWith(data: next));
    if (_selectedWallet?.id == walletId) {
      _selectedWallet = updated;
    }
  }

  void _setResource(Resource<List<Wallet>> newResource) {
    _resource = newResource;
    _selectedWallet = _resolveSelectedWallet(_selectedWallet, wallets);
    notifyListeners();
  }

  Wallet? _resolveSelectedWallet(Wallet? current, List<Wallet> available) {
    if (available.isEmpty) return null;
    final currentId = current?.id;
    if (currentId != null) {
      final existing = available.firstWhereOrNull((wallet) => wallet.id == currentId);
      if (existing != null) return existing;
    }
    return available.firstWhereOrNull((wallet) => !wallet.isHidden) ?? available.first;
  }

  void _setSelectedWallet(Wallet wallet) {
    if (_selectedWallet?.id == wallet.id && _selectedWallet?.isHidden == wallet.isHidden) {
      return;
    }
    _selectedWallet = wallet;
    notifyListeners();
  }
}
