using Baya.Domain.Entities.Bnpl;
using Baya.Domain.Entities.Payments;

namespace Baya.Test.Foundation.Bnpl;

public class BnplLedgerAndStateTests
{
    [Fact]
    public void BnplSettle_group_is_balanced_and_reflects_net_escrow()
    {
        var legs = LedgerPosting.BnplSettle(
            bookingId: 1, nurseId: 2, grossIrr: 10_000_000, commissionIrr: 1_500_000,
            payoutIrr: 8_500_000, bnplCommissionIrr: 1_000_000, bnplTransactionId: 3, createdAt: DateTime.UtcNow);

        Assert.Equal(5, legs.Count);
        Assert.Equal(
            legs.Where(l => l.Direction == LedgerDirection.Debit).Sum(l => l.AmountIrr),
            legs.Where(l => l.Direction == LedgerDirection.Credit).Sum(l => l.AmountIrr));

        // Net escrow = 10M debit − 1M credit = 9M (the settled amount), not the gross.
        var escrowNet = legs.Where(l => l.AccountType == LedgerAccountType.EscrowHeld && l.Direction == LedgerDirection.Debit).Sum(l => l.AmountIrr)
                        - legs.Where(l => l.AccountType == LedgerAccountType.EscrowHeld && l.Direction == LedgerDirection.Credit).Sum(l => l.AmountIrr);
        Assert.Equal(9_000_000, escrowNet);
        Assert.All(legs, l => Assert.Equal(LedgerSourceRefType.BnplTransaction, l.SourceRefType));
    }

    [Fact]
    public void BnplSettle_throws_when_the_capture_legs_do_not_reconcile()
        => Assert.Throws<InvalidOperationException>(() => LedgerPosting.BnplSettle(1, 2, 10_000_000, 1_500_000, 8_000_000, 1_000_000, 3, DateTime.UtcNow));

    [Fact]
    public void State_machine_allows_forward_edges_and_rejects_illegal_ones()
    {
        Assert.True(BnplTransitions.CanTransition(BnplStatus.Eligible, BnplStatus.TokenIssued));
        Assert.True(BnplTransitions.CanTransition(BnplStatus.TokenIssued, BnplStatus.Verified));
        Assert.True(BnplTransitions.CanTransition(BnplStatus.Verified, BnplStatus.Settled));
        Assert.True(BnplTransitions.CanTransition(BnplStatus.Settled, BnplStatus.Reverted));

        Assert.False(BnplTransitions.CanTransition(BnplStatus.TokenIssued, BnplStatus.Settled)); // must verify first
        Assert.False(BnplTransitions.CanTransition(BnplStatus.Settled, BnplStatus.Settled));     // no self-loop
        Assert.False(BnplTransitions.CanTransition(BnplStatus.Reverted, BnplStatus.Settled));    // terminal
    }

    [Fact]
    public void MarkSettled_enforces_the_net_equals_order_minus_commission_invariant()
    {
        var bnpl = new BnplTransaction { OrderAmountIrr = 10_000_000 };
        bnpl.MarkTokenIssued("t", null);
        bnpl.MarkVerified(null, null);

        Assert.Throws<InvalidOperationException>(() => bnpl.MarkSettled(settledAmountIrr: 9_500_000, commissionIrr: 1_000_000, settledAt: null, null));

        bnpl.MarkSettled(settledAmountIrr: 9_000_000, commissionIrr: 1_000_000, settledAt: null, null);
        Assert.Equal(BnplStatus.Settled, bnpl.Status);
    }
}
