using ERPCore.Infra.Persistence;
using Microsoft.EntityFrameworkCore;

namespace ERPCore.Infra.UoW;

/// <summary>
/// EF Core-backed unit of work. Uses the provider execution strategy so the
/// transactional path is safe under connection resiliency / retries.
/// </summary>
public sealed class UnitOfWork : IUnitOfWork
{
    private readonly ErpDbContext _db;

    public UnitOfWork(ErpDbContext db) => _db = db;

    public Task<int> SaveChangesAsync(CancellationToken ct = default) => _db.SaveChangesAsync(ct);

    public async Task ExecuteInTransactionAsync(Func<CancellationToken, Task> action, CancellationToken ct = default)
    {
        await ExecuteInTransactionAsync<object?>(async token =>
        {
            await action(token);
            return null;
        }, ct);
    }

    public async Task<T> ExecuteInTransactionAsync<T>(Func<CancellationToken, Task<T>> action, CancellationToken ct = default)
    {
        var strategy = _db.Database.CreateExecutionStrategy();
        return await strategy.ExecuteAsync(async () =>
        {
            await using var tx = await _db.Database.BeginTransactionAsync(ct);
            try
            {
                var result = await action(ct);
                await _db.SaveChangesAsync(ct);
                await tx.CommitAsync(ct);
                return result;
            }
            catch
            {
                await tx.RollbackAsync(ct);
                throw;
            }
        });
    }
}
