#nullable enable
using Baya.Application.Contracts.Persistence;
using Baya.Domain.Entities.Payments;
using Baya.Infrastructure.Persistence.Repositories.Common;
using Microsoft.EntityFrameworkCore;

namespace Baya.Infrastructure.Persistence.Repositories;

internal sealed class PaymentRepository : BaseAsyncRepository<PaymentTransaction>, IPaymentRepository
{
    public PaymentRepository(ApplicationDbContext dbContext) : base(dbContext)
    {
    }

    public Task<long?> GetActiveGatewayIdAsync(string type, CancellationToken cancellationToken)
        => DbContext.Set<PaymentGateway>().AsNoTracking()
            .Where(g => g.Type == type && g.IsActive)
            .OrderBy(g => g.Priority)
            .Select(g => (long?)g.Id)
            .FirstOrDefaultAsync(cancellationToken);

    public Task AddGatewayAsync(PaymentGateway gateway, CancellationToken cancellationToken)
        => DbContext.Set<PaymentGateway>().AddAsync(gateway, cancellationToken).AsTask();

    public Task AddTransactionAsync(PaymentTransaction transaction, CancellationToken cancellationToken)
        => base.AddAsync(transaction);

    public Task<bool> HasSucceededTransactionForRequestAsync(long bookingRequestId, CancellationToken cancellationToken)
        => TableNoTracking.AnyAsync(
            t => t.BookingRequestId == bookingRequestId && t.Status == PaymentTransactionStatus.Succeeded,
            cancellationToken);

    public Task<PaymentTransaction?> GetTrackedTransactionByReferenceAsync(string gatewayReferenceCode, CancellationToken cancellationToken)
        => Table.FirstOrDefaultAsync(t => t.GatewayReferenceCode == gatewayReferenceCode, cancellationToken);

    public Task<PaymentTransaction?> GetTrackedTransactionByIdAsync(long id, CancellationToken cancellationToken)
        => Table.FirstOrDefaultAsync(t => t.Id == id, cancellationToken);

    public Task<PaymentWebhookEvent?> GetWebhookEventByKeyAsync(string providerCode, string externalEventId, CancellationToken cancellationToken)
        => DbContext.Set<PaymentWebhookEvent>().AsNoTracking()
            .FirstOrDefaultAsync(e => e.ProviderCode == providerCode && e.ExternalEventId == externalEventId, cancellationToken);

    public Task AddWebhookEventAsync(PaymentWebhookEvent webhookEvent, CancellationToken cancellationToken)
        => DbContext.Set<PaymentWebhookEvent>().AddAsync(webhookEvent, cancellationToken).AsTask();

    public Task<bool> LedgerGroupExistsForTransactionAsync(long paymentTransactionId, CancellationToken cancellationToken)
        => DbContext.Set<LedgerEntry>().AsNoTracking().AnyAsync(
            e => e.SourceRefType == LedgerSourceRefType.PaymentTransaction && e.SourceRefId == paymentTransactionId,
            cancellationToken);

    public Task AddLedgerEntriesAsync(IEnumerable<LedgerEntry> entries, CancellationToken cancellationToken)
        => DbContext.Set<LedgerEntry>().AddRangeAsync(entries, cancellationToken);

    public async Task<long> GetNursePayableBalanceAsync(long nurseId, CancellationToken cancellationToken)
    {
        // Signed sum over the append-only ledger — credit adds, debit subtracts. No cached wallet column.
        var query = DbContext.Set<LedgerEntry>().AsNoTracking()
            .Where(e => e.AccountType == LedgerAccountType.NursePayable && e.NurseId == nurseId);

        var credits = await query.Where(e => e.Direction == LedgerDirection.Credit)
            .SumAsync(e => (long?)e.AmountIrr, cancellationToken) ?? 0;
        var debits = await query.Where(e => e.Direction == LedgerDirection.Debit)
            .SumAsync(e => (long?)e.AmountIrr, cancellationToken) ?? 0;

        return credits - debits;
    }
}
