using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using VCommon.VEntity;

namespace VCommon.VEntityFrameworkCore
{
    public abstract class VEfRepositoryBase<TDbContext, TEntity> : IDisposable where TEntity : VEntityBase where TDbContext : VEfDbContextBase
    {
        private TDbContext _dbContext;

        public TDbContext DbContext
        {
            get => _dbContext ??= ResolveDbContextInstance();
            set => _dbContext = value;
        }

        private readonly Dictionary<string, bool> _filterState = new()
        {
            { VEfDataFilters.SoftDelete, true },
            { VEfDataFilters.Passive, true },
        };

        protected virtual IQueryable<TEntity> ProcessFilters(IQueryable<TEntity> input)
        {
            if (_filterState[VEfDataFilters.SoftDelete] && typeof(TEntity).IsAssignableTo(typeof(ISoftDelete)))
            {
                input = input.Where(p => ((ISoftDelete)p).IsAbolish == false);
            }

            if (_filterState[VEfDataFilters.Passive] && typeof(TEntity).IsAssignableTo(typeof(IPassive)))
            {
                input = input.Where(p => ((IPassive)p).IsEnable == false);
            }

            return input;
        }

        public abstract VEfRepositoryBase<TDbContext, TForkEntity> ForkRepository<TForkEntity>() where TForkEntity : VEntityBase;

        protected abstract TDbContext ResolveDbContextInstance();

        protected DbSet<TEntity> DbSet => DbContext.Set<TEntity>();

        public virtual void DisableFilter(string filterName) => _filterState[filterName] = false;

        public virtual void EnableFilter(string filterName) => _filterState[filterName] = true;

        public virtual bool IsFilterEnabled(string filterName) => _filterState[filterName];

        public virtual IQueryable<TEntity> QueryNoTracking(Expression<Func<TEntity, bool>> queryExpression = null)
        {
            var source = ProcessFilters(DbSet.AsNoTracking());

            return queryExpression != null
                ? source.Where(queryExpression)
                : source;
        }

        public virtual IQueryable<TEntity> Query(Expression<Func<TEntity, bool>> queryExpression = null)
        {
            var source = ProcessFilters(DbSet);

            return null == queryExpression ? source : source.Where(queryExpression);
        }


        public virtual void Delete(TEntity entity) => DbSet.Remove(entity);

        public virtual TEntity Add(TEntity entity) => DbSet.Add(entity).Entity;

        public bool CheckExist(Expression<Func<TEntity, bool>> expression = null)
        {
            var source = ProcessFilters(DbSet);

            return null == expression ? source.Any() : source.Any(expression);
        }

        public TEntity GetEntityOrDefault(Guid id) => ProcessFilters(DbSet).FirstOrDefault(p => p.Id == id);

        public TEntity GetEntityOrDefault(Expression<Func<TEntity, bool>> expression = null)
        {
            var source = ProcessFilters(DbSet);
            return null == expression ? source.FirstOrDefault() : source.FirstOrDefault(expression);
        }

        public int SaveChanges() => DbContext.SaveChanges();

        public void Dispose() => DbContext?.Dispose();
    }
}
