using NetinaCMS.Repository.Abstracts;
using NetinaCMS.Repository.Models;
using NetinaCMS.Repository.Repositories.Base.Contracts;

namespace NetinaCMS.Repository.Repositories.Base
{
    public class BaseRepository<T> : Repository<T>, IBaseRepository<T> where T : class, IApiEntity
    {
        private readonly ICurrentUserService _currentUserService;

        public BaseRepository(ApplicationContext dbContext, ICurrentUserService currentUserService) : base(dbContext)
        {
            _currentUserService = currentUserService;
        }

        public virtual async ValueTask<T> GetByIdAsync(CancellationToken cancellationToken, params object[] ids)
        {
            var founded = await Entities.FindAsync(ids, cancellationToken);
            if (founded == null)
                throw new AppException($"{nameof(T)} Not Found", ApiResultStatusCode.NotFound);
            return founded;
        }

        #region Sync Methods

        public virtual T GetById(params object[] ids)
        {
            var ent = Entities.Find(ids);
            if (ent == null)
                throw new AppException($"{nameof(T)} Not Found", ApiResultStatusCode.NotFound);
            Detach(ent);
            return ent;
        }

        public virtual void Add(T entity)
        {
            AssertExtensions.NotNull(entity, nameof(entity));
            Entities.Add(entity);
        }

        public virtual void AddRange(IEnumerable<T> entities)
        {
            var apiEntities = entities as T[] ?? entities.ToArray();
            AssertExtensions.NotNull(apiEntities, nameof(entities));
            Entities.AddRange(apiEntities);
        }

        public virtual void Update(T entity)
        {
            AssertExtensions.NotNull(entity, nameof(entity));
            Detach(entity);
            Entities.Update(entity);
        }

        public virtual void UpdateRange(IEnumerable<T> entities)
        {
            var apiEntities = entities as T[] ?? entities.ToArray();
            AssertExtensions.NotNull(apiEntities, nameof(entities));
            Entities.UpdateRange(apiEntities);
        }

        public void HardDelete(T entity)
        {
            AssertExtensions.NotNull(entity, nameof(entity));
            Entities.Remove(entity);
        }

        public virtual void Delete(T entity)
        {
            AssertExtensions.NotNull(entity, nameof(entity));

            Entities.Entry(entity).Property(e => e.RemovedAt)
                .CurrentValue = DateTime.Now;
            Entities.Entry(entity).Property(e => e.IsRemoved)
                .CurrentValue = true;
            if (_currentUserService.UserName != null)
                Entities.Entry(entity).Property(e => e.RemovedBy)
                    .CurrentValue = _currentUserService.UserName;
            Entities.Update(entity);
        }

        public virtual void DeleteRange(IEnumerable<T> entities)
        {
            var apiEntities = entities.ToList();
            AssertExtensions.NotNull(apiEntities, nameof(entities));
            foreach (var entity in apiEntities)
            {
                Entities.Entry(entity).Property(e => e.RemovedAt)
                    .CurrentValue = DateTime.Now;
                Entities.Entry(entity).Property(e => e.IsRemoved)
                    .CurrentValue = true;
                if (_currentUserService.UserName != null)
                    Entities.Entry(entity).Property(e => e.RemovedBy)
                        .CurrentValue = _currentUserService.UserName;
                Entities.Update(entity);
            }
        }

        #endregion

        #region Attach & Detach

        public virtual void Detach(T entity)
        {
            AssertExtensions.NotNull(entity, nameof(entity));
            var entry = DbContext.Entry(entity);
            entry.State = EntityState.Detached;
        }

        public virtual void Attach(T entity)
        {
            AssertExtensions.NotNull(entity, nameof(entity));
            if (DbContext.Entry(entity).State == EntityState.Detached)
                Entities.Attach(entity);
        }

        #endregion
    }
}