using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Siger.Middlelayer.Repository.Extensions;
using Siger.Middlelayer.Repository.Paged;

namespace Siger.Middlelayer.Repository
{
    public abstract class RepositoryBase<TEntity> : IRepositoryBase<TEntity> where TEntity : EntityBase
    {
        private readonly DbSet<TEntity> _dbSet;
        private readonly DbContext _context;
        protected RepositoryBase(DbContext context)
        {
            _dbSet = context.Set<TEntity>();
            _context = context;
            //context.Database.ExecuteSqlCommand
        }

        public void Insert(TEntity entity)
        {
           _dbSet.Add(entity);
        }

        public async Task InsertAsync(TEntity entity)
        {
            await _dbSet.AddAsync(entity);
        }

        public void Insert(IEnumerable<TEntity> entities)
        {
            
            _dbSet.AddRange(entities);
        }

        public async Task InsertAsync(IEnumerable<TEntity> entities)
        {
            await _dbSet.AddRangeAsync(entities);
        }

        public void Delete(object id)
        {
            var entity = _dbSet.Find(id);
            if (entity != null)
            {
                Delete(entity);
            }
        }

        public async Task DeleteAsync(object id)
        {
            await Task.Run(() =>
            {
                var entity = _dbSet.Find(id);
                if (entity != null)
                {
                    Delete(entity);
                }
            });
        }

        public void Delete(TEntity entity)
        {
            _dbSet.Remove(entity);
        }

        public async Task DeleteAsync(TEntity entity)
        {
            await Task.Run(() =>
            {
                Delete(entity);
            });
        }

        public void Update(TEntity entity)
        {
            _dbSet.Update(entity);
        }

        public async Task UpdateAsync(TEntity entity)
        {
            await Task.Run(() => _dbSet.Update(entity));
        }

        public TEntity Get(object key)
        {
            return _dbSet.Find(key);
        }

        public async Task<TEntity> GetAsync(object key)
        {
            return await _dbSet.FindAsync(key);
        }

        public TEntity Get(Expression<Func<TEntity, bool>> predicate)
        {
            return _dbSet.Where(predicate).FirstOrDefault();
        }

        public async Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> predicate)
        {
            return await Task.Run(() => _dbSet.Where(predicate).FirstOrDefault());
        }

        public bool IsExist(Expression<Func<TEntity, bool>> predicate)
        {
            var entity = _dbSet.Where(predicate).FirstOrDefault();
            return entity != null;
        }

        public async Task<bool> IsExistAsync(Expression<Func<TEntity, bool>> predicate)
        {
            var entity = await GetAsync(predicate);
            return entity != null;
        }

        public IQueryable<TEntity> GetList(Expression<Func<TEntity, bool>> predicate = null, string orderByPropertyName = null, bool desc = true)
        {
            predicate = predicate ?? (x => true);
            if (!string.IsNullOrEmpty(orderByPropertyName))
            {
                return _dbSet.Where(predicate).AsNoTracking().OrderByPropertyName(orderByPropertyName, desc);
            }
            return _dbSet.Where(predicate).AsNoTracking();
        }

        public async Task<IQueryable<TEntity>> GetListAsync(Expression<Func<TEntity, bool>> predicate = null, string orderByPropertyName = null, bool desc = true)
        {
            return await Task.Run(() =>
            {
                predicate = predicate ?? (x => true);
                if (!string.IsNullOrEmpty(orderByPropertyName))
                {
                    return _dbSet.Where(predicate).AsNoTracking().OrderByPropertyName(orderByPropertyName, desc);
                }

                return _dbSet.Where(predicate).AsNoTracking();
            });
        }

        public IPagedCollectionResult<TEntity> GetPagedList(int pageIndex, int pageSize, Expression<Func<TEntity, bool>> predicate = null
        , string orderByPropertyName = null, bool desc = true)
        {
            long totalCount = 0;
            IList<TEntity> entities = null;
            predicate = predicate ?? (x => true);

            if (!string.IsNullOrEmpty(orderByPropertyName))
            {
                totalCount = _dbSet.Count(predicate);
                entities = _dbSet.Where(predicate).Skip((pageIndex - 1) * pageSize).Take(pageSize)
                    .OrderByPropertyName(orderByPropertyName, desc).AsNoTracking().ToList();
            }
            else
            {
                totalCount = _dbSet.Count(predicate);
                entities = _dbSet.Where(predicate).Skip((pageIndex - 1) * pageSize).Take(pageSize).AsNoTracking().ToList();
            }

            return new PagedCollectionResult<TEntity>(entities, totalCount);
        }

        public async Task<IPagedCollectionResult<TEntity>> GetPagedListAsync(int pageIndex, int pageSize, Expression<Func<TEntity, bool>> predicate = null
        , string orderByPropertyName = null, bool desc = true)
        {
            return await Task.Run(() =>
            {
                long totalCount = 0;
                IList<TEntity> entities = null;
                predicate = predicate ?? (x => true);

                if (!string.IsNullOrEmpty(orderByPropertyName))
                {
                    totalCount = _dbSet.Count(predicate);
                    entities = _dbSet.Where(predicate).Skip((pageIndex - 1) * pageSize).Take(pageSize)
                        .OrderByPropertyName(orderByPropertyName, desc).AsNoTracking().ToList();
                }
                else
                {
                    totalCount = _dbSet.Count(predicate);
                    entities = _dbSet.Where(predicate).Skip((pageIndex - 1) * pageSize).Take(pageSize).AsNoTracking().ToList();
                }

                return new PagedCollectionResult<TEntity>(entities, totalCount);
            });
        }

        public int ExecSqlCommond(string sql)
        {
            return _context.Database.ExecuteSqlCommand(sql);
        }
    }
}
