﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Monitor.Framework.Models.Base;
using Monitor.Framework.Utility.Extensions;

namespace Monitor.Framework.Context
{
    public class DbContextBase : DbContext
    {
        protected DbContextBase(string nameOrConnectionString)
            : base(nameOrConnectionString)
        {
        }

        public void Add<T>(T entity) where T : EntityBase<Guid>
        {
            entity.CheckNotNull("entity");

            entity.CreateTime = DateTime.Now;

            Set<T>().Add(entity);
        }
        public void Remove<T>(T entity) where T : EntityBase<Guid>
        {
            Entry(entity).State = EntityState.Deleted;
            SaveChanges();
        }
        public Task<int> RemoveAsync<T>(T entity) where T : EntityBase<Guid>
        {
            Entry(entity).State = EntityState.Deleted;
            return SaveChangesAsync();
        }
        public void Save<T>(T entity) where T : EntityBase<Guid>
        {
            entity.CheckNotNull("entity");
            DbEntityEntry<T> entry = Entry(entity);

            if (entry.State == EntityState.Detached)
                Set<T>().Add(entity);
        }
        public Task<int> SaveAsync<T>(T entity) where T : EntityBase<Guid>
        {
            entity.CheckNotNull("entity");
            DbEntityEntry<T> entry = Entry(entity);

            if (entry.State == EntityState.Detached)
                Set<T>().Add(entity);

            return SaveChangesAsync();
        }
        public int SaveOrUpdate<T>(T entity, Expression<Func<T, bool>> identifierExpression) where T : EntityBase<Guid>
        {
            var entry = Entry(entity);

            if (entry.State == EntityState.Detached)
                Set<T>().Add(entity);

            if (Set<T>().Any(identifierExpression))
            {
                entry.State = EntityState.Modified;
            }

            return SaveChanges();
        }
        public Task<int> SaveOrUpdateAsync<T>(T entity, Expression<Func<T, bool>> identifierExpression) where T : EntityBase<Guid>
        {
            var entry = Entry(entity);

            if (entry.State == EntityState.Detached)
                Set<T>().Add(entity);

            if (Set<T>().Any(identifierExpression))
            {
                entry.State = EntityState.Modified;
            }

            return SaveChangesAsync();
        }
        public IQueryable<T> Query<T>() where T : EntityBase<Guid>
        {
            return Set<T>();
        }
        public IQueryable<T> ReadonlyQuery<T>() where T : EntityBase<Guid>
        {
            return Set<T>().AsNoTracking();
        }
        public T TryFind<T>(Expression<Func<T, bool>> predicate) where T : EntityBase<Guid>
        {
            return Set<T>().AsNoTracking().FirstOrDefault(predicate);
        }
        public Task<T> TryFindAsync<T>(Expression<Func<T, bool>> predicate) where T : EntityBase<Guid>
        {
            return Set<T>().AsNoTracking().FirstOrDefaultAsync(predicate);
        }
    }
}