private async Task <PaginationResponse <List <LedgerLog> > > GetDataFromB2C2(LedgersRequest request)
        {
            while (true)
            {
                try
                {
                    var data = await _b2C2RestClient.GetLedgerHistoryAsync(request);

                    foreach (var item in data.Data)
                    {
                        foreach (var assetMapping in _assetMappings)
                        {
                            item.Currency = item.Currency.Replace(assetMapping.Key, assetMapping.Value);
                        }
                    }

                    return(data);
                }
                catch (Exception ex)
                {
                    if (ex.ToString().Contains("Request was throttled"))
                    {
                        _log.Debug($"Request was throttled, wait 60 second");
                        await Task.Delay(60000);
                    }
                    else
                    {
                        throw;
                    }
                }
            }
        }
        private async Task DoTimer(ITimerTrigger timer, TimerTriggeredHandlerArgs args, CancellationToken ct)
        {
            if (!StartWork())
            {
                return;
            }

            try
            {
                using (var context = CreateContext())
                {
                    var ledgerRequest = new LedgersRequest {
                        Limit = 10
                    };
                    var data = await _b2C2RestClient.GetLedgerHistoryAsync(ledgerRequest, ct);

                    var added = 0;
                    do
                    {
                        added = 0;
                        foreach (var log in data.Data)
                        {
                            foreach (var assetMapping in _assetMappings)
                            {
                                log.Currency = log.Currency.Replace(assetMapping.Key, assetMapping.Value);
                            }

                            var item = await context.Ledgers.FirstOrDefaultAsync(
                                e => e.TransactionId == log.TransactionId, ct);

                            if (item != null)
                            {
                                continue;
                            }

                            item = new LedgerEntity(log);
                            context.Ledgers.Add(item);
                            added++;
                        }

                        await context.SaveChangesAsync(ct);

                        ledgerRequest.Cursor = data.Next;

                        data = await _b2C2RestClient.GetLedgerHistoryAsync(ledgerRequest, ct);
                    } while (added > 0);
                }
            }
            finally
            {
                StopWork();
            }
        }
        public async Task <int> ReloadLedgerHistoryAsync()
        {
            while (!StartWork())
            {
                await Task.Delay(1000);
            }

            try
            {
                using (var context = CreateContext())
                {
                    var ledgerRequest = new LedgersRequest {
                        Limit = 100
                    };
                    string transactionId = null;

                    if (_enableAutoUpdate)
                    {
                        var last = context.Ledgers.OrderByDescending(x => x.Created).FirstOrDefault();
                        if (last != null)
                        {
                            ledgerRequest.CreatedAfter = last.Created.AddHours(-1);
                            transactionId = last.TransactionId;
                        }
                    }

                    var data = await _b2C2RestClient.GetLedgerHistoryAsync(ledgerRequest);

                    _log.Debug($"Current cursor = null; get data after transactionId = {(transactionId ?? "null")}; load more {data.Data.Count}");

                    int  totalCount = 0;
                    bool finish     = false;

                    while (!finish || data.Data.Count > 0)
                    {
                        var items = new List <LedgerEntity>();

                        foreach (var item in data.Data)
                        {
                            foreach (var assetMapping in _assetMappings)
                            {
                                item.Currency = item.Currency.Replace(assetMapping.Key, assetMapping.Value);
                            }

                            items.Add(new LedgerEntity(item));
                        }

                        foreach (var item in items)
                        {
                            if (!string.IsNullOrEmpty(transactionId) && item.TransactionId == transactionId)
                            {
                                finish = true;
                                break;
                            }

                            totalCount++;
                            context.Ledgers.Add(item);
                        }

                        await context.SaveChangesAsync();

                        if (finish)
                        {
                            _log.Debug($"Finish loading to transactionId = {transactionId}. Loaded {totalCount} records");
                            break;
                        }

                        ledgerRequest.Cursor = data.Next;
                        data = await GetDataFromB2C2(ledgerRequest);

                        if (string.IsNullOrEmpty(data.Next))
                        {
                            finish = true;
                        }

                        _log.Debug($"Current cursor = {ledgerRequest.Cursor}; next cursor: {ledgerRequest.Cursor}; load more {data.Data.Count}");
                    }

                    return(totalCount);
                }
            }
            finally
            {
                StopWork();
            }
        }
        public async Task <PaginationResponse <List <LedgerLog> > > GetLedgerHistoryAsync(LedgersRequest request, CancellationToken ct = default(CancellationToken))
        {
            var requestId = Guid.NewGuid();

            _log.Debug("ledger history - request", context: $"requestId: {requestId}, request: {request?.ToJson()}");

            try
            {
                var param = new Dictionary <string, string>();

                if (request != null)
                {
                    if (request.CreatedAfter.HasValue)
                    {
                        param.Add("created__gte", request.CreatedAfter.Value.ToString("yyyy-MM-ddThh:mm:ss"));
                    }

                    if (request.CreatedBefore.HasValue)
                    {
                        param.Add("created__lt", request.CreatedBefore.Value.ToString("yyyy-MM-ddThh:mm:ss"));
                    }

                    if (!string.IsNullOrEmpty(request.Currency))
                    {
                        param.Add("currency", request.Currency);
                    }

                    if (request.Type.HasValue)
                    {
                        param.Add("type", request.Type.ToString());
                    }

                    if (request.Since.HasValue)
                    {
                        param.Add("since", request.Since.Value.ToString("yyyy-MM-ddThh:mm:ss"));
                    }

                    if (!string.IsNullOrEmpty(request.Cursor))
                    {
                        param.Add("cursor", request.Cursor);
                    }
                }

                param.Add("limit", Math.Max(100, request?.Limit ?? 50).ToString());

                var ledgerUrl = new Uri(QueryHelpers.AddQueryString("ledger/", param), UriKind.Relative).ToString();

                using var response = await _httpClient.GetAsync(ledgerUrl, ct);

                var status = response.StatusCode;

                var responseStr = await response.Content.ReadAsStringAsync();

                CheckForError(responseStr, status, requestId);

                var data = JsonConvert.DeserializeObject <List <LedgerLog> >(responseStr);

                var result = new PaginationResponse <List <LedgerLog> > {
                    Data = data
                };

                if (response.Headers.TryGetValues("link", out var links))
                {
                    (result.Next, result.Previous) = GetCursors(links);
                }

                return(result);
            }
            catch (Exception e)
            {
                _log.Error(e, "ledger history - response exception", requestId);
                throw;
            }
        }