namespace WalletService.API.Application.Commands;

using MediatR;
using WalletService.Domain.AggregatesModel.WalletAggregate;
using WalletService.Domain.Exceptions;
using WalletService.Domain.SeedWork;

/// <summary>
/// EN: Handler for ExchangeCommand.
/// VI: Handler cho ExchangeCommand.
/// </summary>
public class ExchangeCommandHandler : IRequestHandler<ExchangeCommand, ExchangeResult>
{
    private readonly IWalletRepository _walletRepository;
    private readonly ILogger<ExchangeCommandHandler> _logger;

    public ExchangeCommandHandler(
        IWalletRepository walletRepository,
        ILogger<ExchangeCommandHandler> logger)
    {
        _walletRepository = walletRepository;
        _logger = logger;
    }

    public async Task<ExchangeResult> Handle(
        ExchangeCommand request,
        CancellationToken cancellationToken)
    {
        // EN: Get wallet by user ID
        // VI: Lấy ví theo user ID
        var wallet = await _walletRepository.GetByUserIdAsync(request.UserId)
            ?? throw new WalletDomainException($"Wallet not found for user {request.UserId}");

        // EN: Parse currency types from IDs
        // VI: Parse loại tiền tệ từ IDs
        var fromCurrency = Enumeration.FromValue<CurrencyType>(request.FromCurrencyTypeId);
        var toCurrency = Enumeration.FromValue<CurrencyType>(request.ToCurrencyTypeId);

        // EN: Calculate actual rate
        // VI: Tính tỷ giá thực tế
        var rate = request.CustomRate ?? fromCurrency.GetExchangeRateTo(toCurrency);

        // EN: Perform exchange (atomic operation within aggregate)
        // VI: Thực hiện quy đổi (thao tác atomic trong aggregate)
        var receivedAmount = wallet.Exchange(request.FromAmount, fromCurrency, toCurrency, request.CustomRate);

        _walletRepository.Update(wallet);
        await _walletRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken);

        _logger.LogInformation(
            "Exchanged {FromAmount} {FromCurrency} to {ToAmount} {ToCurrency} in wallet {WalletId}",
            request.FromAmount, fromCurrency.Name, receivedAmount, toCurrency.Name, wallet.Id);

        return new ExchangeResult(
            wallet.Id,
            request.FromAmount,
            fromCurrency.Name,
            receivedAmount,
            toCurrency.Name,
            rate,
            DateTime.UtcNow
        );
    }
}
