using AyCode.Core.Helpers;
using AyCode.Database.DataLayers;
using AyCode.Database.DbContexts;
using AyCode.Interfaces.Entities;
using AyCode.Utils.Extensions;
using Microsoft.EntityFrameworkCore;

namespace AyCode.Database.Extensions;

public static class AcDalExtension
{
    public static Task<TResultType> SessionAsync<TDbContext, TResultType>(this IAcDalBase<TDbContext> acDal, Func<TDbContext, TResultType> callback) where TDbContext : AcDbContextBase
        => TaskHelper.ToThreadPoolTask(() => acDal.Session(callback));

    public static Task<IEnumerable<TEntity>> SessionAsync<TDbContext, TEntity>(this IAcDalBase<TDbContext> acDal, Func<TDbContext, IQueryable<TEntity>> callback) where TEntity : IEntity where TDbContext : AcDbContextBase
        => TaskHelper.ToThreadPoolTask(() => acDal.Session(callback));

    public static TResultType Session<TDbContext, TResultType>(this IAcDalBase<TDbContext> acDal, Func<TDbContext, TResultType> callback) where TDbContext : AcDbContextBase
    {
        //using var ctx = acDal.CreateDbContext();

        using (acDal.MutextLock.UseWaitOne())
        {
            return acDal.Context.Session(callback);
        }
    }

    public static IEnumerable<TEntity> Session<TDbContext, TEntity>(this IAcDalBase<TDbContext> acDal, Func<TDbContext, IQueryable<TEntity>> callback) where TEntity : IEntity where TDbContext : AcDbContextBase
    {
        //using var ctx = acDal.CreateDbContext();

        using (acDal.MutextLock.UseWaitOne())
        {
            return acDal.Context.Session(callback);
        }
    }

    public static Task<bool> TransactionAsync<TDbContext>(this IAcDalBase<TDbContext> acDal, Func<TDbContext, bool> callbackTransactionBody, bool throwException = false) where TDbContext : AcDbContextBase
        => TaskHelper.ToThreadPoolTask(() => acDal.Transaction(callbackTransactionBody, throwException));

    public static bool Transaction<TDbContext>(this IAcDalBase<TDbContext> acDal, Func<TDbContext, bool> callbackTransactionBody, bool throwException = false) where TDbContext : AcDbContextBase
    {
        //using var ctx = acDal.CreateDbContext();

        using (acDal.MutextLock.UseWaitOne())
        {
            return acDal.Context.Transaction(callbackTransactionBody, throwException);
        }
    }

    public static Task<TEntity?> UpdateSafeAsync<TDbContext, TEntity>(this IAcDalBase<TDbContext> acDal, TEntity entity, Func<TDbContext, TEntity, bool>? callbackTransactionBody = null, bool throwException = false) where TDbContext : AcDbContextBase where TEntity : class, IEntityGuid
        => TaskHelper.ToThreadPoolTask(() => acDal.UpdateSafe(entity, callbackTransactionBody, throwException));

    public static TEntity? UpdateSafe<TDbContext, TEntity>(this IAcDalBase<TDbContext> acDal, TEntity entity, Func<TDbContext, TEntity, bool>? callbackTransactionBody = null, bool throwException = false) where TDbContext : AcDbContextBase where TEntity : class, IEntityGuid
    {
        TEntity? updateEntity = null;

        var isSuccess = acDal.Context.Transaction(ctx =>
        {
            updateEntity = ctx.Set<TEntity>().FirstOrDefault(x => x.Id == entity.Id);
            if (updateEntity == null) return false;

            //ctx.Entry(updateEntity).State = EntityState.Detached;
            ctx.Entry(updateEntity).CurrentValues.SetValues(entity);

            if (callbackTransactionBody == null)
                return ctx.Update(updateEntity).State == EntityState.Modified;

            return callbackTransactionBody.Invoke(ctx, updateEntity);
        }, throwException);

        return isSuccess ? updateEntity : null;
    }
}