#nullable enable
using Baya.Application.Contracts.Payments;
using Baya.Application.Contracts.Persistence;
using Baya.Application.Models.Common;
using Baya.Domain.Entities.Bnpl;
using Mediator;

namespace Baya.Application.Features.Bnpl.Commands.VerifyBnplOrder;

internal sealed class VerifyBnplOrderCommandHandler(
    IUnitOfWork unitOfWork,
    IBnplProviderResolver providerResolver)
    : IRequestHandler<VerifyBnplOrderCommand, OperationResult<bool>>
{
    public async ValueTask<OperationResult<bool>> Handle(VerifyBnplOrderCommand request, CancellationToken cancellationToken)
    {
        var bnpl = await unitOfWork.BnplRepository.GetTrackedByIdAsync(request.BnplTransactionId, cancellationToken);
        if (bnpl is null)
            return OperationResult<bool>.NotFoundResult("BNPL order not found.");

        // Already verified/settled — idempotent no-op (a replayed callback must not re-drive the transition).
        if (bnpl.Status is BnplStatus.Verified or BnplStatus.Settled)
            return OperationResult<bool>.SuccessResult(true);
        if (bnpl.Status != BnplStatus.TokenIssued || string.IsNullOrEmpty(bnpl.ExternalPaymentToken))
            return OperationResult<bool>.ConflictResult("This BNPL order is not awaiting verification.");

        var provider = providerResolver.Resolve(bnpl.ProviderCode);
        if (provider is null)
            return OperationResult<bool>.FailureResult("The BNPL provider is not available.");

        var verify = await provider.VerifyAsync(bnpl.ExternalPaymentToken!, bnpl.OrderAmountIrr, cancellationToken);

        // Never trust the callback alone — the provider must confirm and the amount must match the stored order.
        if (verify.Status != PaymentProviderStatus.Succeeded || verify.OrderAmountIrr != bnpl.OrderAmountIrr)
        {
            bnpl.MarkFailed();
            await unitOfWork.CommitAsync();
            return OperationResult<bool>.FailureResult("The BNPL order could not be verified with the provider.");
        }

        bnpl.MarkVerified(verify.ExternalTransactionId, request.CallbackPayloadJson);
        await unitOfWork.CommitAsync();
        return OperationResult<bool>.SuccessResult(true);
    }
}
