using Lada.Framework.Data.Mappings.DbContexts;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;

namespace Lada.Framework.Data.Repositories
{
    public class BaseRepository<T> : IDisposable, IBaseRepository<T> where T : class
    {
        public BaseRepository(LadaDbContext context)
        {
            Context = context;
            DbSet = Context.Set<T>();
            _context = context;
        }

        protected LadaDbContext Context { get; }
        private readonly LadaDbContext _context;
        protected DbSet<T> DbSet { get; }

        public IDbContextTransaction BeginTransaction()
        {
            return Context.Database.BeginTransaction();
        }

        public IQueryable<T> Query()
        {
            return DbSet;
        }


        public virtual async Task<int> InsertAsync(T entity)
        {
            if (entity == null)
                throw new ArgumentNullException(nameof(entity));
            int rs = 0;
            try
            {
                DbSet.Add(entity);
                rs = await _context.SaveChangesAsync();
                return rs;
            }
            catch (DbUpdateException exception)
            {
                throw new Exception(GetFullErrorTextAndRollbackEntityChanges(exception), exception);
            }

        }
        public virtual async Task<int> InsertAsync(IEnumerable<T> entities)
        {
            if (entities == null)
                throw new ArgumentNullException(nameof(entities));
            int rs = 0;
            try
            {
                DbSet.AddRange(entities);
                rs = await _context.SaveChangesAsync();
                return rs;
            }
            catch (DbUpdateException)
            {
            }
            return rs;
        }

        public virtual async Task<int> UpdateAsync(T entity)
        {
            if (entity == null)
                throw new ArgumentNullException(nameof(entity));
            int rs = 0;
            try
            {
                DbSet.Update(entity);
                rs = await _context.SaveChangesAsync();
                return rs;
            }
            catch (DbUpdateException)
            {
            }
            return rs;
        }
        public virtual async Task<int> UpdateAsync(IEnumerable<T> entities)
        {
            if (entities == null)
                throw new ArgumentNullException(nameof(entities));
            int rs = 0;
            try
            {
                DbSet.UpdateRange(entities);
                rs = await _context.SaveChangesAsync();
                return rs;
            }
            catch (DbUpdateException)
            {
            }
            return rs;
        }

        

        private string GetFullErrorTextAndRollbackEntityChanges(DbUpdateException exception)
        {
            //rollback entity changes
            if (Context is DbContext dbContext)
            {
                var entries = dbContext.ChangeTracker.Entries()
                    .Where(e => e.State == EntityState.Added || e.State == EntityState.Modified).ToList();

                entries.ForEach(entry => entry.State = EntityState.Unchanged);
            }

            Context.SaveChanges();
            return exception.ToString();
        }
        public void Dispose()
        {

        }
    }
}
