using DataAccessLayer.Entities;
using DataAccessLayer.Filtering;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace DataAccessLayer
{
    /// <summary>
    /// Base repository class for DB operations
    /// </summary>
    /// <typeparam name="T">Type of entity to handle</typeparam>
    public class BaseRepository<T> where T : EntityBase
    {

        public BaseRepository(ISDbContext context)
        {
            _context = context;
        }

        /// <summary>
        /// Create a new entity
        /// </summary>
        /// <param name="entity"></param>
        public void Create(T entity)
        {
            _context.Set<T>().Add(entity);
        }

        /// <summary>
        /// Delete etity
        /// </summary>
        /// <param name="entity"></param>
        public async Task DeleteAsync(int id)
        {
            var entityToDelete = await _context.Set<T>().FindAsync(id);

            if (_context.Entry(entityToDelete).State == EntityState.Detached)
            {
                _context.Set<T>().Attach(entityToDelete);
            }
            _context.Set<T>().Remove(entityToDelete);
        }

        /// <summary>
        /// Get entity by its id
        /// </summary>
        /// <param name="id"></param>
        /// <param name="includeProperties">List of connected entities to load</param>
        /// <returns>Entity with matching id</returns>
        public async Task<T> GetByIdAsync(int id, string includeProperties = "")
        {
            IQueryable<T> query = _context.Set<T>().Where(x => x.Id == id);
            foreach (var includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
            {
                query = query.Include(includeProperty);
            }
            return await query.FirstOrDefaultAsync();
        }

        /// <summary>
        /// Get all entities
        /// </summary>
        /// <param name="filter">Expression to filter entities</param>
        /// <param name="orderBy">Expression to order entities</param>
        /// <param name="includeProperties">List of connected entities to load</param>
        /// <returns>Ordered list of entities that match the filter</returns>
        public async Task<IEnumerable<T>> GetAsync(Filter filter = null, string includeProperties = "")
        {
            IQueryable<T> query = _context.Set<T>();

            foreach (var includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
            {
                query = query.Include(includeProperty);
            }

            query = query.ApplyFilterAll(filter);

            return await query.ToListAsync();
        }

        /// <summary>
        /// Update entity
        /// </summary>
        /// <param name="entity"></param>
        public void Update(T entity)
        {
            _context.Entry(entity).State = System.Data.Entity.EntityState.Modified;
        }


        private ISDbContext _context;
    }
}
