using CMSMicroservice.Protobuf.Protos.UserWallet;
using CMSMicroservice.WebApi.Common.Services;
using CMSMicroservice.Application.UserWalletCQ.Commands.CreateNewUserWallet;
using CMSMicroservice.Application.UserWalletCQ.Commands.UpdateUserWallet;
using CMSMicroservice.Application.UserWalletCQ.Commands.DeleteUserWallet;
using CMSMicroservice.Application.WalletCQ.Commands.ChargeMagicWallet;
using CMSMicroservice.Application.WalletCQ.Commands.ChargeDiscountWallet;
using CMSMicroservice.Application.WalletCQ.Commands.ChargeCreditWallet;
using CMSMicroservice.Application.WalletCQ.Commands.VerifyMagicWalletCharge;
using CMSMicroservice.Application.WalletCQ.Commands.VerifyDiscountWalletCharge;
using CMSMicroservice.Application.WalletCQ.Commands.VerifyCreditWalletCharge;
using CMSMicroservice.Application.WalletCQ.Commands.AdminManualCreditCharge;
using CMSMicroservice.Application.WalletCQ.Queries.GetManualCreditCharges;
using AppManualChargeFilter = CMSMicroservice.Application.WalletCQ.Queries.GetManualCreditCharges.GetManualCreditChargesFilter;
using CMSMicroservice.Application.UserWalletCQ.Queries.GetUserWallet;
using CMSMicroservice.Application.UserWalletCQ.Queries.GetAllUserWalletByFilter;
using CMSMicroservice.Application.UserWalletCQ.Queries.GetCustomerWalletHistory;
using CMSMicroservice.Application.UserWalletCQ.Queries.GetCustomerWithdrawals;
using CMSMicroservice.Application.UserWalletCQ.Queries.GetCustomerWithdrawalSettings;
using CMSMicroservice.Application.Common.Interfaces;
using CMSMicroservice.Application.Common;
using CMSMicroservice.Application.Common.Exceptions;
using CMSMicroservice.Domain.Common;
using CMSMicroservice.Domain.Enums;
using Grpc.Core;
using Google.Protobuf.WellKnownTypes;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System.Linq;

namespace CMSMicroservice.WebApi.Services;
public class UserWalletService : UserWalletContract.UserWalletContractBase
{
    private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS;
    private readonly ISender _sender;
    private readonly IApplicationDbContext _context;
    private readonly ICurrentUserService _currentUserService;
    private readonly ILogger<UserWalletService> _logger;

    public UserWalletService(
        IDispatchRequestToCQRS dispatchRequestToCQRS, 
        ISender sender,
        IApplicationDbContext context,
        ICurrentUserService currentUserService,
        ILogger<UserWalletService> logger)
    {
        _dispatchRequestToCQRS = dispatchRequestToCQRS;
        _sender = sender;
        _context = context;
        _currentUserService = currentUserService;
        _logger = logger;
    }
    public override async Task<CreateNewUserWalletResponse> CreateNewUserWallet(CreateNewUserWalletRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<CreateNewUserWalletRequest, CreateNewUserWalletCommand, CreateNewUserWalletResponse>(request, context);
    }
    public override async Task<Empty> UpdateUserWallet(UpdateUserWalletRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<UpdateUserWalletRequest, UpdateUserWalletCommand>(request, context);
    }
    public override async Task<Empty> DeleteUserWallet(DeleteUserWalletRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<DeleteUserWalletRequest, DeleteUserWalletCommand>(request, context);
    }
    public override async Task<GetUserWalletResponse> GetUserWallet(GetUserWalletRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<GetUserWalletRequest, GetUserWalletQuery, GetUserWalletResponse>(request, context);
    }
    public override async Task<GetAllUserWalletByFilterResponse> GetAllUserWalletByFilter(GetAllUserWalletByFilterRequest request, ServerCallContext context)
    {
        var response = await _dispatchRequestToCQRS.Handle<GetAllUserWalletByFilterRequest, GetAllUserWalletByFilterQuery, GetAllUserWalletByFilterResponse>(request, context);

        // Enrich response with user names
        if (response?.Models != null && response.Models.Any())
        {
            var userIds = response.Models.Select(m => m.UserId).Distinct().ToList();
            var users = await _context.Users
                .AsNoTracking()
                .Where(u => userIds.Contains(u.Id))
                .Select(u => new { u.Id, u.FirstName, u.LastName })
                .ToDictionaryAsync(u => u.Id, context.CancellationToken);

            foreach (var model in response.Models)
            {
                if (users.TryGetValue(model.UserId, out var user))
                {
                    model.UserName = $"{user.FirstName} {user.LastName}".Trim();
                }
            }
        }

        return response;
    }
    
    // ============= Customer-specific Methods =============
    
    public override async Task<GetCustomerWalletResponse> GetCustomerWallet(Google.Protobuf.WellKnownTypes.Empty request, ServerCallContext context)
    {
        // Customer endpoint: resolve userId from JWT
        if (!long.TryParse(_currentUserService.UserId, out var userId) || userId <= 0)
            throw new RpcException(new Status(StatusCode.Unauthenticated, "کاربر احراز هویت نشده است"));

        var walletQuery = new GetUserWalletQuery { Id = userId };
        var wallet = await _sender.Send(walletQuery, context.CancellationToken);
        
        var walletEntity = await _context.UserWallets
            .FirstOrDefaultAsync(w => w.UserId == userId, context.CancellationToken);

        return new GetCustomerWalletResponse
        {
            Balance = wallet.Balance,
            NetworkBalance = wallet.NetworkBalance,
            DiscountBalance = wallet.DiscountBalance,
            WalletMode = (int)(walletEntity?.WalletMode ?? WalletMode.Normal)
        };
    }
    
    public override async Task<GetCustomerWalletHistoryResponse> GetCustomerWalletHistory(GetCustomerWalletHistoryRequest request, ServerCallContext context)
    {
        var query = new GetCustomerWalletHistoryQuery
        {
            ReferenceId = request.ReferenceId,
            IsIncrease = request.IsIncrease
        };
        
        var changeLogs = await _sender.Send(query, context.CancellationToken);
        
        var response = new GetCustomerWalletHistoryResponse
        {
            MetaData = new CMSMicroservice.Protobuf.Protos.MetaData
            {
                CurrentPage = 1,
                TotalPage = 1,
                PageSize = changeLogs.Count,
                TotalCount = changeLogs.Count,
                HasPrevious = false,
                HasNext = false
            }
        };
        
        foreach (var log in changeLogs)
        {
            response.Models.Add(new CustomerWalletHistoryModel
            {
                CurrentBalance = log.CurrentBalance,
                ChangeValue = log.ChangeValue,
                CurrentNetworkBalance = log.CurrentNetworkBalance,
                ChangeNerworkValue = log.ChangeNerworkValue,
                CurrentDiscountBalance = log.CurrentDiscountBalance,
                ChangeDiscountValue = log.ChangeDiscountValue,
                IsIncrease = log.IsIncrease,
                RefrenceId = log.RefrenceId,
                CreatedAt = Google.Protobuf.WellKnownTypes.Timestamp.FromDateTime(DateTime.SpecifyKind(log.Created, DateTimeKind.Utc))
            });
        }
        
        return response;
    }
    
    public override async Task<Google.Protobuf.WellKnownTypes.Empty> CustomerWithdrawBalance(CustomerWithdrawBalanceRequest request, ServerCallContext context)
    {
        var userId = GetCurrentUserId();
        
        // Find the commission payout record
        var payout = await _context.UserCommissionPayouts
            .Where(p => p.Id == request.PayoutId && p.UserId == userId && !p.IsDeleted)
            .FirstOrDefaultAsync(context.CancellationToken);
        
        if (payout == null)
            throw new RpcException(new Status(StatusCode.NotFound, "رکورد پرداخت کمیسیون یافت نشد"));
        
        // Validate status - can only withdraw if already paid to wallet
        if (payout.Status != Domain.Enums.CommissionPayoutStatus.Paid)
            throw new RpcException(new Status(StatusCode.FailedPrecondition, 
                "فقط کمیسیون‌های واریز شده به کیف پول قابل برداشت هستند"));
        
        // Update payout with withdrawal info
        payout.WithdrawalMethod = (Domain.Enums.WithdrawalMethod)request.WithdrawalMethod;
        if (payout.WithdrawalMethod == Domain.Enums.WithdrawalMethod.Cash)
        {
            var normalizedIban = IbanNormalizer.TryNormalize(request.IbanNumber);
            if (normalizedIban is null)
                throw new RpcException(new Status(StatusCode.InvalidArgument,
                    "فرمت شماره شبا معتبر نیست. باید IR و ۲۴ رقم باشد."));
            payout.IbanNumber = normalizedIban;
        }
        else
        {
            payout.IbanNumber = null;
        }
        payout.Status = Domain.Enums.CommissionPayoutStatus.WithdrawRequested;
        
        await _context.SaveChangesAsync(context.CancellationToken);
        
        return new Google.Protobuf.WellKnownTypes.Empty();
    }
    
    // ============= Magic Wallet Methods =============

    public override async Task<InitiateMagicChargeResponse> InitiateMagicCharge(
        InitiateMagicChargeRequest request, ServerCallContext context)
    {
        var userId = GetCurrentUserId();

        try
        {
            var result = await _sender.Send(new ChargeMagicWalletCommand
            {
                UserId = userId,
                Amount = request.Amount
            }, context.CancellationToken);

            return new InitiateMagicChargeResponse
            {
                IsSuccess = result.IsSuccess,
                GatewayUrl = result.GatewayUrl ?? "",
                ErrorMessage = result.ErrorMessage ?? ""
            };
        }
        catch (PaymentInProgressException ex)
        {
            return new InitiateMagicChargeResponse
            {
                IsSuccess = false,
                ErrorMessage = ex.Message
            };
        }
    }

    // ============= Discount Wallet Methods =============

    public override async Task<InitiateDiscountChargeResponse> InitiateDiscountCharge(
        InitiateDiscountChargeRequest request, ServerCallContext context)
    {
        var userId = GetCurrentUserId();

        try
        {
            var result = await _sender.Send(new ChargeDiscountWalletCommand
            {
                UserId = userId,
                Amount = request.Amount
            }, context.CancellationToken);

            return new InitiateDiscountChargeResponse
            {
                IsSuccess = result.IsSuccess,
                GatewayUrl = result.GatewayUrl ?? "",
                ErrorMessage = result.ErrorMessage ?? ""
            };
        }
        catch (PaymentInProgressException ex)
        {
            return new InitiateDiscountChargeResponse
            {
                IsSuccess = false,
                ErrorMessage = ex.Message
            };
        }
    }

    // ============= Credit (Main) Wallet Methods =============

    public override async Task<InitiateCreditChargeResponse> InitiateCreditCharge(
        InitiateCreditChargeRequest request, ServerCallContext context)
    {
        var userId = GetCurrentUserId();

        try
        {
            var result = await _sender.Send(new ChargeCreditWalletCommand
            {
                UserId = userId,
                Amount = request.Amount
            }, context.CancellationToken);

            return new InitiateCreditChargeResponse
            {
                IsSuccess = result.IsSuccess,
                GatewayUrl = result.GatewayUrl ?? "",
                ErrorMessage = result.ErrorMessage ?? ""
            };
        }
        catch (PaymentInProgressException ex)
        {
            return new InitiateCreditChargeResponse
            {
                IsSuccess = false,
                ErrorMessage = ex.Message
            };
        }
        catch (BadRequestException ex)
        {
            return new InitiateCreditChargeResponse
            {
                IsSuccess = false,
                ErrorMessage = ex.Message
            };
        }
    }

    // ============= Wallet Verify Methods =============

    public override async Task<VerifyWalletChargeResponse> VerifyMagicCharge(
        VerifyWalletChargeRequest request, ServerCallContext context)
    {
        _logger.LogInformation("VerifyMagicCharge called: Authority={Authority}, Status={Status}",
            request.Authority, request.Status);

        try
        {
            if (string.IsNullOrEmpty(request.Authority))
                return new VerifyWalletChargeResponse { Success = false, Message = "کد Authority نامعتبر است" };

            var result = await _sender.Send(new VerifyMagicWalletChargeCommand
            {
                Authority = request.Authority,
                Status = request.Status ?? "NOK"
            }, context.CancellationToken);

            _logger.LogInformation("VerifyMagicCharge result: {Result}, Authority={Authority}", result, request.Authority);

            return new VerifyWalletChargeResponse
            {
                Success = result,
                Message = result
                    ? "شارژ کیف‌پول جادویی با موفقیت انجام شد"
                    : "شارژ کیف‌پول جادویی ناموفق بود"
            };
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "VerifyMagicCharge error: Authority={Authority}", request.Authority);
            return new VerifyWalletChargeResponse { Success = false, Message = ex.Message };
        }
    }

    public override async Task<VerifyWalletChargeResponse> VerifyDiscountCharge(
        VerifyWalletChargeRequest request, ServerCallContext context)
    {
        _logger.LogInformation("VerifyDiscountCharge called: Authority={Authority}, Status={Status}",
            request.Authority, request.Status);

        try
        {
            if (string.IsNullOrEmpty(request.Authority))
                return new VerifyWalletChargeResponse { Success = false, Message = "کد Authority نامعتبر است" };

            // پیدا کردن PaymentTransaction برای استخراج UserId و Amount
            var paymentTx = await _context.PaymentTransactions
                .FirstOrDefaultAsync(pt => pt.Authority == request.Authority, context.CancellationToken);

            if (paymentTx == null || !paymentTx.UserId.HasValue)
            {
                _logger.LogError("VerifyDiscountCharge: PaymentTransaction not found for Authority={Authority}", request.Authority);
                return new VerifyWalletChargeResponse { Success = false, Message = "تراکنش یافت نشد" };
            }

            if (!string.Equals(request.Status, "OK", StringComparison.OrdinalIgnoreCase))
            {
                _logger.LogWarning("VerifyDiscountCharge: Payment cancelled by user. Authority={Authority}", request.Authority);
                return new VerifyWalletChargeResponse { Success = false, Message = "پرداخت توسط کاربر لغو شد" };
            }

            var result = await _sender.Send(new VerifyDiscountWalletChargeCommand
            {
                UserId = paymentTx.UserId.Value,
                Amount = paymentTx.Amount,
                Authority = request.Authority
            }, context.CancellationToken);

            _logger.LogInformation("VerifyDiscountCharge result: {Result}, Authority={Authority}, UserId={UserId}",
                result, request.Authority, paymentTx.UserId.Value);

            return new VerifyWalletChargeResponse
            {
                Success = result,
                Message = result
                    ? "شارژ کیف پول تخفیفی با موفقیت انجام شد"
                    : "شارژ کیف پول تخفیفی ناموفق بود"
            };
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "VerifyDiscountCharge error: Authority={Authority}", request.Authority);
            return new VerifyWalletChargeResponse { Success = false, Message = ex.Message };
        }
    }

    public override async Task<VerifyWalletChargeResponse> VerifyCreditCharge(
        VerifyWalletChargeRequest request, ServerCallContext context)
    {
        _logger.LogInformation("VerifyCreditCharge called: Authority={Authority}, Status={Status}",
            request.Authority, request.Status);

        try
        {
            if (string.IsNullOrEmpty(request.Authority))
                return new VerifyWalletChargeResponse { Success = false, Message = "کد Authority نامعتبر است" };

            var paymentTx = await _context.PaymentTransactions
                .FirstOrDefaultAsync(pt => pt.Authority == request.Authority, context.CancellationToken);

            if (paymentTx == null || !paymentTx.UserId.HasValue)
            {
                _logger.LogError("VerifyCreditCharge: PaymentTransaction not found for Authority={Authority}", request.Authority);
                return new VerifyWalletChargeResponse { Success = false, Message = "تراکنش یافت نشد" };
            }

            if (!string.Equals(request.Status, "OK", StringComparison.OrdinalIgnoreCase))
            {
                _logger.LogWarning("VerifyCreditCharge: Payment cancelled by user. Authority={Authority}", request.Authority);
                return new VerifyWalletChargeResponse { Success = false, Message = "پرداخت توسط کاربر لغو شد" };
            }

            var result = await _sender.Send(new VerifyCreditWalletChargeCommand
            {
                UserId = paymentTx.UserId.Value,
                Amount = paymentTx.Amount,
                Authority = request.Authority
            }, context.CancellationToken);

            _logger.LogInformation("VerifyCreditCharge result: {Result}, Authority={Authority}, UserId={UserId}",
                result, request.Authority, paymentTx.UserId.Value);

            return new VerifyWalletChargeResponse
            {
                Success = result,
                Message = result
                    ? "شارژ کیف پول اصلی با موفقیت انجام شد"
                    : "شارژ کیف پول اصلی ناموفق بود"
            };
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "VerifyCreditCharge error: Authority={Authority}", request.Authority);
            return new VerifyWalletChargeResponse { Success = false, Message = ex.Message };
        }
    }

    public override async Task<GetMagicWalletStatusResponse> GetMagicWalletStatus(
        Google.Protobuf.WellKnownTypes.Empty request, ServerCallContext context)
    {
        var userId = GetCurrentUserId();

        var wallet = await _context.UserWallets
            .FirstOrDefaultAsync(w => w.UserId == userId, context.CancellationToken);

        if (wallet == null)
            throw new RpcException(new Status(StatusCode.NotFound, "کیف پول یافت نشد"));

        var cycleCount = await _context.ClubMembershipCycles
            .CountAsync(c => c.UserId == userId, context.CancellationToken);

        // بارگذاری پکیج کاربر برای سقف کیف‌پول جادویی
        var currentCycle = await _context.ClubMembershipCycles
            .FirstOrDefaultAsync(c => c.UserId == userId && c.IsCurrentCycle, context.CancellationToken);
        var package = currentCycle != null
            ? await _context.Packages.FirstOrDefaultAsync(p => p.Id == currentCycle.PackageId, context.CancellationToken)
            : await _context.Packages.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, context.CancellationToken);
        var magicMaxDeposit = package?.MagicWalletMaxDeposit ?? SystemConstants.MagicWalletDefaultMaxDeposit;
        var magicMultiplier = package != null ? (double)package.MagicWalletMultiplier : SystemConstants.MagicWalletDefaultMultiplier;
        var magicMaxCredit = package?.MagicWalletMaxCredit ?? SystemConstants.MagicWalletDefaultMaxCredit;

        var response = new GetMagicWalletStatusResponse
        {
            WalletMode = (int)wallet.WalletMode,
            MagicTotalDeposited = wallet.MagicTotalDeposited,
            MagicTotalCredited = wallet.MagicTotalCredited,
            MagicMaxDeposit = magicMaxDeposit,
            MagicRemainingDeposit = Math.Max(0, magicMaxDeposit - wallet.MagicTotalDeposited),
            Balance = wallet.Balance,
            PurchaseCycleCount = cycleCount,
            MagicMultiplier = magicMultiplier,
            MagicMaxCredit = magicMaxCredit
        };

        if (wallet.MagicActivatedAt.HasValue)
        {
            response.MagicActivatedAt = Timestamp.FromDateTime(
                DateTime.SpecifyKind(wallet.MagicActivatedAt.Value, DateTimeKind.Utc));
        }

        return response;
    }

    private long GetCurrentUserId()
    {
        if (long.TryParse(_currentUserService.UserId, out var userId) && userId > 0)
            return userId;
        throw new RpcException(new Status(StatusCode.Unauthenticated, "لطفاً وارد حساب کاربری خود شوید"));
    }
    
    public override async Task<GetCustomerWithdrawalsResponse> GetCustomerWithdrawals(GetCustomerWithdrawalsRequest request, ServerCallContext context)
    {
        var query = new GetCustomerWithdrawalsQuery
        {
            Status = request.Status
        };
        
        var withdrawals = await _sender.Send(query, context.CancellationToken);
        
        var response = new GetCustomerWithdrawalsResponse
        {
            MetaData = new CMSMicroservice.Protobuf.Protos.MetaData
            {
                CurrentPage = 1,
                TotalPage = 1,
                PageSize = withdrawals.Count,
                TotalCount = withdrawals.Count,
                HasPrevious = false,
                HasNext = false
            }
        };
        
        foreach (var withdrawal in withdrawals)
        {
            response.Models.Add(new CustomerWithdrawalModel
            {
                Id = withdrawal.Id,
                WeekDefinitionId = withdrawal.WeekDefinitionId,
                WeekDisplayName = withdrawal.WeekDisplayName,
                TotalAmount = withdrawal.TotalAmount,
                Status = withdrawal.Status,
                WithdrawalMethod = withdrawal.WithdrawalMethod,
                IbanNumber = withdrawal.IbanNumber,
                Created = Google.Protobuf.WellKnownTypes.Timestamp.FromDateTime(DateTime.SpecifyKind(withdrawal.Created, DateTimeKind.Utc))
            });
        }
        
        return response;
    }
    
    public override async Task<GetCustomerWithdrawalSettingsResponse> GetCustomerWithdrawalSettings(Google.Protobuf.WellKnownTypes.Empty request, ServerCallContext context)
    {
        var query = new GetCustomerWithdrawalSettingsQuery();
        var settings = await _sender.Send(query, context.CancellationToken);
        
        return new GetCustomerWithdrawalSettingsResponse
        {
            MinWithdrawalAmount = settings.MinWithdrawalAmount
        };
    }

    // ============= Admin Manual Credit Charge =============

    public override async Task<AdminManualCreditChargeResponse> AdminManualCreditCharge(
        AdminManualCreditChargeRequest request, ServerCallContext context)
    {
        try
        {
            var result = await _sender.Send(new AdminManualCreditChargeCommand
            {
                UserId = request.UserId,
                Amount = request.Amount,
                Note = request.Note,
                PerformedByAdminId = _currentUserService.GetPerformedBy()
            }, context.CancellationToken);

            return new AdminManualCreditChargeResponse
            {
                Success = result.Success,
                Message = result.Message ?? string.Empty,
                TransactionId = result.TransactionId,
                NewBalance = result.NewBalance
            };
        }
        catch (PaymentInProgressException ex)
        {
            return new AdminManualCreditChargeResponse
            {
                Success = false,
                Message = ex.Message
            };
        }
        catch (NotFoundException ex)
        {
            return new AdminManualCreditChargeResponse
            {
                Success = false,
                Message = ex.Message
            };
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "AdminManualCreditCharge failed for UserId={UserId}", request.UserId);
            return new AdminManualCreditChargeResponse
            {
                Success = false,
                Message = ex.Message
            };
        }
    }

    public override async Task<GetManualCreditChargesResponse> GetManualCreditCharges(
        GetManualCreditChargesRequest request, ServerCallContext context)
    {
        var result = await _sender.Send(new GetManualCreditChargesQuery
        {
            SortBy = request.SortBy,
            PaginationState = request.PaginationState != null
                ? new Application.Common.Models.PaginationState
                {
                    PageNumber = request.PaginationState.PageNumber,
                    PageSize = request.PaginationState.PageSize
                }
                : new Application.Common.Models.PaginationState { PageNumber = 1, PageSize = 20 },
            Filter = new AppManualChargeFilter
            {
                UserId = request.Filter?.UserId,
                TransactionId = request.Filter?.TransactionId,
                RefId = request.Filter?.RefId,
                CreatedFrom = request.Filter?.CreatedFrom?.ToDateTime(),
                CreatedTo = request.Filter?.CreatedTo?.ToDateTime()
            }
        }, context.CancellationToken);

        var response = new GetManualCreditChargesResponse
        {
            MetaData = new CMSMicroservice.Protobuf.Protos.MetaData
            {
                CurrentPage = result.MetaData.CurrentPage,
                TotalPage = result.MetaData.TotalPage,
                PageSize = result.MetaData.PageSize,
                TotalCount = result.MetaData.TotalCount,
                HasPrevious = result.MetaData.HasPrevious,
                HasNext = result.MetaData.HasNext
            }
        };

        foreach (var m in result.Models)
        {
            var model = new ManualCreditChargeModel
            {
                TransactionId = m.TransactionId,
                UserId = m.UserId,
                UserName = m.UserName ?? string.Empty,
                Amount = m.Amount,
                Description = m.Description ?? string.Empty,
                RefId = m.RefId ?? string.Empty,
                NewBalance = m.NewBalance,
                Created = Timestamp.FromDateTime(DateTime.SpecifyKind(m.Created, DateTimeKind.Utc))
            };

            if (m.PaymentDate.HasValue)
            {
                model.PaymentDate = Timestamp.FromDateTime(
                    DateTime.SpecifyKind(m.PaymentDate.Value, DateTimeKind.Utc));
            }

            response.Models.Add(model);
        }

        return response;
    }
}
