namespace WalletService.API.Application.Commands;

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

/// <summary>
/// EN: Handler for AdminFreezeWalletCommand.
/// VI: Handler cho AdminFreezeWalletCommand.
/// </summary>
public class AdminFreezeWalletCommandHandler : IRequestHandler<AdminFreezeWalletCommand, AdminWalletActionResult>
{
    private readonly IWalletRepository _walletRepository;
    private readonly ILogger<AdminFreezeWalletCommandHandler> _logger;

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

    public async Task<AdminWalletActionResult> Handle(
        AdminFreezeWalletCommand request, 
        CancellationToken cancellationToken)
    {
        var wallet = await _walletRepository.GetByIdAsync(request.WalletId)
            ?? throw new WalletDomainException($"Wallet {request.WalletId} not found");

        wallet.Freeze();

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

        _logger.LogInformation(
            "Wallet {WalletId} frozen by Admin {AdminId}. Reason: {Reason}",
            request.WalletId, request.AdminId, request.Reason);

        return new AdminWalletActionResult(
            wallet.Id,
            wallet.Status.Name,
            request.AdminId.ToString(),
            DateTime.UtcNow);
    }
}

/// <summary>
/// EN: Handler for AdminUnfreezeWalletCommand.
/// VI: Handler cho AdminUnfreezeWalletCommand.
/// </summary>
public class AdminUnfreezeWalletCommandHandler : IRequestHandler<AdminUnfreezeWalletCommand, AdminWalletActionResult>
{
    private readonly IWalletRepository _walletRepository;
    private readonly ILogger<AdminUnfreezeWalletCommandHandler> _logger;

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

    public async Task<AdminWalletActionResult> Handle(
        AdminUnfreezeWalletCommand request, 
        CancellationToken cancellationToken)
    {
        var wallet = await _walletRepository.GetByIdAsync(request.WalletId)
            ?? throw new WalletDomainException($"Wallet {request.WalletId} not found");

        wallet.Unfreeze();

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

        _logger.LogInformation(
            "Wallet {WalletId} unfrozen by Admin {AdminId}. Reason: {Reason}",
            request.WalletId, request.AdminId, request.Reason);

        return new AdminWalletActionResult(
            wallet.Id,
            wallet.Status.Name,
            request.AdminId.ToString(),
            DateTime.UtcNow);
    }
}

/// <summary>
/// EN: Handler for AdminAdjustBalanceCommand.
/// VI: Handler cho AdminAdjustBalanceCommand.
/// </summary>
public class AdminAdjustBalanceCommandHandler : IRequestHandler<AdminAdjustBalanceCommand, AdminAdjustBalanceResult>
{
    private readonly IWalletRepository _walletRepository;
    private readonly ILogger<AdminAdjustBalanceCommandHandler> _logger;

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

    public async Task<AdminAdjustBalanceResult> Handle(
        AdminAdjustBalanceCommand request, 
        CancellationToken cancellationToken)
    {
        var wallet = await _walletRepository.GetByIdAsync(request.WalletId)
            ?? throw new WalletDomainException($"Wallet {request.WalletId} not found");

        var currencyType = Domain.SeedWork.Enumeration.FromValue<CurrencyType>(request.CurrencyTypeId);
        var previousBalance = wallet.GetBalance(currencyType);

        // EN: Positive amount = credit, negative = debit
        // VI: Số dương = cộng, số âm = trừ
        if (request.Amount > 0)
        {
            wallet.Deposit(request.Amount, currencyType, $"Admin Adjustment: {request.Reason}");
        }
        else if (request.Amount < 0)
        {
            wallet.Withdraw(Math.Abs(request.Amount), currencyType, $"Admin Adjustment: {request.Reason}");
        }

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

        var newBalance = wallet.GetBalance(currencyType);

        _logger.LogInformation(
            "Wallet {WalletId} balance adjusted by Admin {AdminId}. Amount: {Amount} {Currency}. Reason: {Reason}",
            request.WalletId, request.AdminId, request.Amount, currencyType.Name, request.Reason);

        return new AdminAdjustBalanceResult(
            wallet.Id,
            previousBalance,
            request.Amount,
            newBalance,
            currencyType.Name,
            request.Reason,
            DateTime.UtcNow);
    }
}
