using System.Linq.Expressions;
using SqlSugar;
using XF.Domain.Entities;
using XF.Domain.Repository;

namespace DataSharing.Repository
{
    public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class, IEntity<string>, IHasCreationTime, IDataPermission, new()
    {
        protected ISugarUnitOfWork<DataSharingDbContext> Uow { get; }
        protected ISqlSugarClient Db { get; }

        public BaseRepository(ISugarUnitOfWork<DataSharingDbContext> uow)
        {
            Uow = uow;
            Db = uow.Db;
        }

        public async Task<string> AddAsync(TEntity entity, CancellationToken cancellationToken = default)
        {
            var excEntity = await Db.Insertable(entity).ExecuteReturnEntityAsync();
            return excEntity.Id;
        }

        /// <summary>
        /// 批量插入（应用场景：小数据量，超出1万条建议另行实现）
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task AddRangeAsync(List<TEntity> entities, CancellationToken cancellationToken = default)
        {
            await Db.Insertable(entities).ExecuteCommandAsync(cancellationToken);
        }

        public async Task RemoveAsync(TEntity entity, bool? soft = false, CancellationToken cancellationToken = default)
        {
            if (soft.HasValue && soft.Value)
            {
                await Db.Deleteable(entity).IsLogic().ExecuteCommandAsync("IsDeleted", true, "DeletionTime");
            }
            else
            {
                await Db.Deleteable(entity).ExecuteCommandAsync(cancellationToken);
            }
        }

        public async Task RemoveAsync(string id, bool? soft = false, CancellationToken cancellationToken = default)
        {
            if (soft.HasValue && soft.Value)
            {
                await Db.Deleteable<TEntity>().In(id).IsLogic().ExecuteCommandAsync("IsDeleted", true, "DeletionTime");
            }
            else
            {
                await Db.Deleteable<TEntity>().In(id).ExecuteCommandAsync(cancellationToken);
            }
        }

        public async Task RemoveAsync(Expression<Func<TEntity, bool>> predicate, bool? soft, CancellationToken cancellationToken = default)
        {
            if (soft.HasValue && soft.Value)
            {
                await Db.Deleteable<TEntity>().Where(predicate).IsLogic().ExecuteCommandAsync("IsDeleted", true, "DeletionTime");
            }
            else
            {
                await Db.Deleteable<TEntity>().Where(predicate).ExecuteCommandAsync(cancellationToken);
            }
        }

        public async Task RemoveRangeAsync(IEnumerable<TEntity> entities, CancellationToken cancellationToken = default)
        {
            await Db.Deleteable<TEntity>(entities).ExecuteCommandAsync(cancellationToken);
        }

        public async Task RemoveRangeAsync(IEnumerable<TEntity> entities, bool? soft, CancellationToken cancellationToken = default)
        {
	        if (soft.HasValue && soft.Value)
	        {
		        await Db.Deleteable<TEntity>(entities).IsLogic().ExecuteCommandAsync("IsDeleted", true, "DeletionTime");
	        }
	        else
	        {
		        await Db.Deleteable<TEntity>(entities).ExecuteCommandAsync(cancellationToken);
	        }
        }

		public async Task UpdateAsync(TEntity entity, CancellationToken cancellationToken = default)
        {
            await Db.Updateable(entity)
                .IgnoreColumns(ignoreAllNullColumns: true)
                .IgnoreColumns(d => d.CreationTime)
                .ExecuteCommandAsync(cancellationToken);
        }

        public async Task UpdateRangeAsync(List<TEntity> entities, CancellationToken cancellationToken = default)
        {
            await Db.Updateable(entities)
                .IgnoreColumns(d => d.CreationTime)
                .ExecuteCommandAsync(cancellationToken);
        }

        public async Task<TEntity?> GetAsync(string id, CancellationToken cancellationToken = default)
        {
            return await Db.Queryable<TEntity>().FirstAsync(d => d.Id == id, cancellationToken);
        }

        public TEntity Get(string id)
        {
            return Db.Queryable<TEntity>().First(d => d.Id == id);
        }

        public TEntity Get(Expression<Func<TEntity, bool>> predicate)
        {
            return Db.Queryable<TEntity>().First(predicate);
        }


        public async Task<TEntity?> GetAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default)
        {
            return await Db.Queryable<TEntity>().FirstAsync(predicate, cancellationToken);
        }

        public async Task<TEntity?> GetAsync(Expression<Func<TEntity, bool>> predicate, bool isDesc, Expression<Func<TEntity, object>> orderBy, CancellationToken cancellationToken = default)
        {
            if (isDesc)
                return await Db.Queryable<TEntity>().OrderBy(orderBy, OrderByType.Desc).FirstAsync(predicate, cancellationToken);
            else
                return await Db.Queryable<TEntity>().OrderBy(orderBy, OrderByType.Asc).FirstAsync(predicate, cancellationToken);
        }


        public async Task<List<TEntity>> QueryAsync(Expression<Func<TEntity, bool>>? predicate = null, params (bool isWhere, Expression<Func<TEntity, bool>> expression)[] whereIfs)
        {
            var query = Db.Queryable<TEntity>().Where(predicate ??= d => true);
            if (whereIfs.Any())
            {
                foreach (var whereIf in whereIfs)
                {
                    query = query.WhereIF(whereIf.isWhere, whereIf.expression);
                }
            }

            return await query.ToListAsync();
        }

        public Task<bool> AnyAsync(CancellationToken cancellationToken = default) => Db.Queryable<TEntity>().AnyAsync();

        public Task<bool> AnyAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default) =>
             Db.Queryable<TEntity>().AnyAsync(predicate, cancellationToken);

        public Task<int> CountAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default) =>
             Db.Queryable<TEntity>().CountAsync(predicate, cancellationToken);

        public ISugarQueryable<TEntity> Queryable(bool permissionVerify = false, bool includeDeleted = false)
        {
            if (includeDeleted)
                Db.QueryFilter.Clear();

            var query = Db.Queryable<TEntity>();
            return query;
        }

        public IUpdateable<TEntity> Updateable() => Db.Updateable<TEntity>();
        public IUpdateable<TEntity> Updateable(TEntity entity) => Db.Updateable(entity);
        public IUpdateable<TEntity> Updateable(List<TEntity> entities) => Db.Updateable(entities);
        
        public IDeleteable<TEntity> Removeable() => Db.Deleteable<TEntity>();

        public UpdateNavTaskInit<TEntity, TEntity> UpdateNav(TEntity entity) => Db.UpdateNav(entity);

        public UpdateNavTaskInit<TEntity, TEntity> UpdateNav(TEntity entity, UpdateNavRootOptions options) => Db.UpdateNav(entity, options);

        public UpdateNavTaskInit<TEntity, TEntity> UpdateNav(List<TEntity> entities) => Db.UpdateNav(entities);

        public UpdateNavTaskInit<TEntity, TEntity> UpdateNav(List<TEntity> entities, UpdateNavRootOptions options) => Db.UpdateNav(entities, options);

        public InsertNavTaskInit<TEntity, TEntity> AddNav(TEntity entity)
        {
            return Db.InsertNav(entity);
        }

        public InsertNavTaskInit<TEntity, TEntity> AddNav(TEntity entity, InsertNavRootOptions options)
        {
            return Db.InsertNav(entity, options);
        }

        public InsertNavTaskInit<TEntity, TEntity> AddNav(List<TEntity> entities)
        {
            return Db.InsertNav(entities);
        }

        public InsertNavTaskInit<TEntity, TEntity> AddNav(List<TEntity> entities, InsertNavRootOptions options)
        {
            return Db.InsertNav(entities, options);
        }

        public DeleteNavTaskInit<TEntity, TEntity> RemoveNav(TEntity entity) => Db.DeleteNav(entity);
        public DeleteNavTaskInit<TEntity, TEntity> RemoveNav(List<TEntity> entities) => Db.DeleteNav(entities);

        public DeleteNavTaskInit<TEntity, TEntity> RemoveNav(Expression<Func<TEntity, bool>> predicate) => Db.DeleteNav(predicate);

        /// <summary>
        /// 基础分页
        /// </summary>
        /// <param name="predicate"></param>
        /// <param name="orderByCreator"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <param name="permissionVerify"></param>
        /// <returns></returns>
        public async Task<(int Total, List<TEntity> Items)> QueryPagedAsync(
            Expression<Func<TEntity, bool>> predicate,
            Func<ISugarQueryable<TEntity>, ISugarQueryable<TEntity>> orderByCreator,
            int pageIndex,
            int pageSize,
            bool permissionVerify = false,
            params (bool isWhere, Expression<Func<TEntity, bool>> expression)[] whereIfs)
        {
            RefAsync<int> total = 0;
            var query = Db.Queryable<TEntity>().Where(predicate);

            if (whereIfs.Any())
            {
                foreach (var whereIf in whereIfs)
                {
                    query = query.WhereIF(whereIf.isWhere, whereIf.expression);
                }
            }
            var items = await orderByCreator(query).ToPageListAsync(pageIndex, pageSize, total);
            return (total.Value, items);
        }


        public async Task<(int Total, List<TEntity> Items)> QueryPagedAsync(
            Expression<Func<TEntity, bool>> predicate,
            int pageIndex,
            int pageSize,
            Func<ISugarQueryable<TEntity>, ISugarQueryable<TEntity>>? includes = null,
            Func<ISugarQueryable<TEntity>, ISugarQueryable<TEntity>>? orderByCreator = null,
            bool permissionVerify = false,
            params (bool isWhere, Expression<Func<TEntity, bool>> expression)[] whereIfs)
        {
            RefAsync<int> total = 0;
            var query = Db.Queryable<TEntity>().Where(predicate);

            if (includes is not null)
                query = includes(query);

            if (whereIfs.Any())
            {
                foreach (var whereIf in whereIfs)
                {
                    query = query.WhereIF(whereIf.isWhere, whereIf.expression);
                }
            }
            if (orderByCreator is not null)
                query = orderByCreator(query);

            var items = await query.ToPageListAsync(pageIndex, pageSize, total);
            return (total.Value, items);
        }

        public async Task<List<TEntity>> QueryExtAsync(
            Expression<Func<TEntity, bool>> predicate,
            Func<ISugarQueryable<TEntity>, ISugarQueryable<TEntity>>? includes = null,
            Func<ISugarQueryable<TEntity>, ISugarQueryable<TEntity>>? orderByCreator = null,
            bool permissionVerify = false,
            params (bool isWhere, Expression<Func<TEntity, bool>> expression)[] whereIfs)
        {
            var query = Db.Queryable<TEntity>().Where(predicate);

            if (includes is not null)
                query = includes(query);

            if (whereIfs.Any())
            {
                foreach (var whereIf in whereIfs)
                {
                    query = query.WhereIF(whereIf.isWhere, whereIf.expression);
                }
            }

            if (orderByCreator is not null)
                query = orderByCreator(query);

            return await query.ToListAsync();
        }

        public async Task<List<TEntity>> QueryExtAsync(
            Expression<Func<TEntity, bool>> predicate,
            Func<ISugarQueryable<TEntity>, ISugarQueryable<TEntity>> includes,
            bool permissionVerify = false)
        {
            var query = Db.Queryable<TEntity>().Where(predicate);
            query = includes(query);
            return await query.ToListAsync();
        }

        public async Task<TEntity> GetExtAsync(
            Expression<Func<TEntity, bool>> predicate,
            Func<ISugarQueryable<TEntity>, ISugarQueryable<TEntity>> includes,
            bool permissionVerify = false)
        {
            var query = Db.Queryable<TEntity>();
            query = includes(query);
            return await query.FirstAsync(predicate);
        }

        public async Task<TEntity> GetExtAsync(string id, Func<ISugarQueryable<TEntity>, ISugarQueryable<TEntity>> includes, bool permissionVerify = false)
        {
            var query = Db.Queryable<TEntity>();
            query = includes(query);
            return await query.FirstAsync(d => d.Id == id);
        }

        public async Task UpdateAsync(TEntity entity, bool ignoreNullColumns = true, CancellationToken cancellationToken = default)
        {
            await Db.Updateable(entity)
                .IgnoreColumns(ignoreAllNullColumns: ignoreNullColumns)
                .IgnoreColumns(d => d.CreationTime)
                .ExecuteCommandAsync(cancellationToken);
        }
    }
}
