import 'package:seshat/data/services/api_client.dart'; import 'package:seshat/domain/models/accounting.dart'; import 'package:seshat/domain/models/bal.dart'; import 'package:seshat/domain/models/bal_stats.dart'; import 'package:seshat/domain/models/enums.dart'; import 'package:seshat/utils/result.dart'; /// Repository to manage [Bal] class BalRepository { BalRepository({required ApiClient apiClient}) : _apiClient = apiClient; final ApiClient _apiClient; /// [List] of all the user's [Bal] List? _bals; /// [Accounting] of [Bal], mapped by [Bal] id final Map _accountingMap = {}; /// Gets a list of all [Bal] from cache or remote Future>> getBals() async { if (_bals != null) { return Result.ok(_bals!); } final result = await _apiClient.getBals(); switch (result) { case Ok(): _bals = result.value; return Result.ok(result.value); case Error(): return result; } } /// Gets a list of all [Bal] from remote only Future>> _getBalsNoCache() async { final result = await _apiClient.getBals(); switch (result) { case Ok(): _bals = result.value; return Result.ok(result.value); case Error(): return result; } } /// Gets a [Bal] by [balId], either from cache or remote Future> balById(int balId) async { if (_bals == null) { await getBals(); } Bal? bal = _bals!.where((bal) => bal.id == balId).firstOrNull; if (bal != null) { return Result.ok(bal); } final result = await _apiClient.getBalById(balId); switch (result) { case Ok(): return Result.ok(result.value); case Error(): return result; } } /// Return wether or not a [Bal] is currently [BalState.ongoing] bool isABalOngoing() { return _bals?.where((bal) => bal.state == BalState.ongoing).isNotEmpty ?? false; } /// Gets the [Bal] that is [BalState.ongoing] Future ongoingBal() async { if (_bals == null) { await _getBalsNoCache(); } return _bals!.where((bal) => bal.state == BalState.ongoing).firstOrNull; } /// Stops a [Bal] and refresh cache Future> stopBal(int id) async { final result = await _apiClient.stopBal(id); _getBalsNoCache(); return result; } /// Starts a [Bal] and refresh cache Future> startBal(int id) async { if (isABalOngoing()) { return Result.error( Exception("Cannot have multiple BAL ongoing at the same time !"), ); } final result = await _apiClient.startBal(id); _getBalsNoCache(); return result; } /// Changes a [Bal]'s [name], [startTime] or [endTime] Future> editBal( int id, String name, DateTime startTime, DateTime endTime, ) async { final result = await _apiClient.editBal(id, name, startTime, endTime); await _getBalsNoCache(); return result; } /// Creates a [Bal] from its [name], [startTime] and [endTime] Future> addBal( String name, DateTime startTime, DateTime endTime, ) async { final result = await _apiClient.addBal(name, startTime, endTime); await _getBalsNoCache(); return result; } /// Gets a [BalStats] from its [balId] Future> getBalStats(int balId) async { return _apiClient.getBalStats(balId); } /// Get [Accounting] of a [Bal] from remote only Future> getAccountingNoCache(int balId) async { final result = await _apiClient.getAccounting(balId); switch (result) { case Ok(): _accountingMap[balId] = result.value; break; default: } return result; } /// Get [Accounting] of a [Bal] from cache or remote Future> getAccounting(int balId) async { if (_accountingMap[balId] != null) { return Result.ok(_accountingMap[balId]!); } final result = await _apiClient.getAccounting(balId); switch (result) { case Ok(): _accountingMap[balId] = result.value; break; default: } return result; } /// Manages what returning (of type [ReturnType]) does to cache and notifies remote Future> returnToId( int balId, int ownerId, ReturnType type, ) async { final result = await _apiClient.returnToId(balId, ownerId, type.name); switch (result) { case Ok(): switch (type) { case ReturnType.books: final owner = _accountingMap[balId]?.owners .where((el) => el.ownerId == ownerId) .firstOrNull; if (owner?.owedMoney == 0) { _accountingMap[balId]?.owners.removeWhere( (el) => el.ownerId == ownerId, ); } owner?.owed = []; owner?.owedInstances = []; break; case ReturnType.money: final owner = _accountingMap[balId]?.owners .where((el) => el.ownerId == ownerId) .firstOrNull; if (owner?.owed == null || owner!.owed.isEmpty) { _accountingMap[balId]?.owners.removeWhere( (el) => el.ownerId == ownerId, ); } owner?.owedMoney = 0; break; case ReturnType.all: _accountingMap[balId]?.owners.removeWhere( (el) => el.ownerId == ownerId, ); break; } break; default: } return result; } }