using Baya.Application.Features.Payments.Commands.ConfirmPaymentAndPostLedger;
using Baya.Application.Features.Payments.Commands.InitiatePayment;
using Baya.Domain.Entities.Payments;
using Microsoft.EntityFrameworkCore;

namespace Baya.Test.Foundation.Payments;

public class InitiatePaymentTests
{
    private static readonly DateTimeOffset Now = new(2026, 7, 6, 10, 0, 0, TimeSpan.Zero);

    private static InitiatePaymentCommandHandler Initiate(PaymentsTestHost host)
        => new(host.AsCustomer(), host.UnitOfWork, host.PaymentProvider, host.Clock(Now));

    [Fact]
    public async Task Initiate_creates_a_pending_transaction_with_the_frozen_gross_and_a_reference()
    {
        using var host = new PaymentsTestHost();
        var variantId = host.AddVariant(sessionCount: 2, price: 5_000_000); // gross = 10_000_000
        var requestId = host.AddAcceptedRequest(variantId);

        var result = await Initiate(host).Handle(new InitiatePaymentCommand(requestId, null), CancellationToken.None);

        Assert.True(result.IsSuccess);
        Assert.False(string.IsNullOrEmpty(result.Result.RedirectUrl));

        var txn = host.Db.Set<PaymentTransaction>().AsNoTracking().Single(t => t.Id == result.Result.TransactionId);
        Assert.Equal(PaymentTransactionStatus.Pending, txn.Status);
        Assert.Equal(10_000_000, txn.Amount);
        Assert.Null(txn.BookingId);
        Assert.Equal(result.Result.GatewayReferenceCode, txn.GatewayReferenceCode);

        // No ledger rows and no booking yet.
        Assert.Empty(host.Db.Set<LedgerEntry>().AsNoTracking());
    }

    [Fact]
    public async Task Initiate_after_capture_is_a_conflict()
    {
        using var host = new PaymentsTestHost();
        var variantId = host.AddVariant(sessionCount: 1, price: 5_000_000);
        var requestId = host.AddAcceptedRequest(variantId);

        var first = await Initiate(host).Handle(new InitiatePaymentCommand(requestId, null), CancellationToken.None);
        var confirm = new ConfirmPaymentAndPostLedgerCommandHandler(
            host.UnitOfWork, host.Config(), host.Clock(Now), host.PaymentProvider, host.Settlement, host.Serializer, host.Notifications(), TestSenders.WithTicketHooks());
        await confirm.Handle(new ConfirmPaymentAndPostLedgerCommand(first.Result.TransactionId), CancellationToken.None);

        // A repeat initiate for an already-paid booking is a 409, not a second attempt.
        var again = await Initiate(host).Handle(new InitiatePaymentCommand(requestId, "fresh-key"), CancellationToken.None);
        Assert.False(again.IsSuccess);
        Assert.True(again.IsConflict);
    }
}
