using Microsoft.EntityFrameworkCore;

using System.Linq.Expressions;

namespace HuanMeng.DotNetCore.Base;

/// <summary>
/// 基本数据库操作，需要安装 Microsoft.EntityFrameworkCore 和 Microsoft.EntityFrameworkCore.Relational
/// </summary>
/// <typeparam name="TDbContext"></typeparam>
public class EfCoreDaoBase<TDbContext> where TDbContext : DbContext
{
    private TDbContext _context;

    public TDbContext Context => _context;

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="context"></param>
    public EfCoreDaoBase(TDbContext context)
    {
        _context = context ?? throw new ArgumentNullException(nameof(context));
    }

    /// <summary>
    /// 是否手动提交
    /// </summary>
    public bool IsManualSubmit { get; set; }

    /// <summary>
    /// SqlQueryRaw
    /// </summary>
    public async Task<T?> SqlQueryAsync<T>(string sql, params object[] parameters) where T : class
    {
        return await Context.Database.SqlQueryRaw<T>(sql, parameters).FirstOrDefaultAsync();
    }

    /// <summary>
    /// SqlQueryList
    /// </summary>
    public async Task<List<T>> SqlQueryListAsync<T>(string sql, params object[] parameters)
    {
        return await Context.Database.SqlQueryRaw<T>(sql, parameters).ToListAsync();
    }

    /// <summary>
    /// ExecuteSql
    /// </summary>
    public async Task<int> ExecuteSqlAsync(string sql, params object[] parameters)
    {
        var result = await Context.Database.ExecuteSqlRawAsync(sql, parameters);
        await AutoSaveChangesAsync();
        return result;
    }

    /// <summary>
    /// 添加实体
    /// </summary>
    public async Task AddAsync<T>(T entity) where T : class
    {
        Context.Set<T>().Add(entity);
        await AutoSaveChangesAsync();
    }

    /// <summary>
    /// 批量添加实体
    /// </summary>
    public async Task AddRangeAsync<T>(IEnumerable<T> entities) where T : class
    {
        Context.Set<T>().AddRange(entities);
        await AutoSaveChangesAsync();
    }

    /// <summary>
    /// 删除某个实体
    /// </summary>
    public async Task DeleteAsync<T>(T entity) where T : class
    {
        Context.Entry(entity).State = EntityState.Deleted;
        await AutoSaveChangesAsync();
    }

    /// <summary>
    /// 更新实体
    /// </summary>
    public async Task UpdateAsync<T>(T entity) where T : class
    {
        if (Context.Entry(entity).State == EntityState.Detached)
        {
            Context.Set<T>().Attach(entity);
            Context.Entry(entity).State = EntityState.Modified;
        }
        await AutoSaveChangesAsync();
    }

    /// <summary>
    /// 清除上下文跟踪
    /// </summary>
    public void RemoveTracking<T>(T entity) where T : class
    {
        if (Context.Entry(entity).State != EntityState.Detached)
        {
            Context.Entry(entity).State = EntityState.Detached;
        }
    }

    /// <summary>
    /// 获取实体，根据主键
    /// </summary>
    public async Task<T?> GetModelAsync<T>(params object[] keyValues) where T : class
    {
        return await Context.Set<T>().FindAsync(keyValues);
    }

    /// <summary>
    /// 获取实体，根据条件
    /// </summary>
    public async Task<T?> GetModelAsync<T>(Expression<Func<T, bool>> where, bool isNoTracking = false) where T : class
    {
        var query = Context.Set<T>().AsQueryable();
        if (isNoTracking)
            query = query.AsNoTracking();
        return await query.FirstOrDefaultAsync(where);
    }

    /// <summary>
    /// 获取列表数据
    /// </summary>
    public IQueryable<T> GetList<T>(Expression<Func<T, bool>> where, bool isNoTracking = false) where T : class
    {
        var query = Context.Set<T>().Where(where);
        return isNoTracking ? query.AsNoTracking() : query;
    }

    /// <summary>
    /// 获取记录数量
    /// </summary>
    public async Task<int> GetCountAsync<T>(Expression<Func<T, bool>> where) where T : class
    {
        return await Context.Set<T>().AsNoTracking().CountAsync(where);
    }

    /// <summary>
    /// 判断是否存在记录
    /// </summary>
    public async Task<bool> ExistsAsync<T>(Expression<Func<T, bool>> where) where T : class
    {
        return await Context.Set<T>().AsNoTracking().AnyAsync(where);
    }

    /// <summary>
    /// 根据提交状态决定是否自动保存更改
    /// </summary>
    private async Task AutoSaveChangesAsync()
    {
        if (!IsManualSubmit)
        {
            await Context.SaveChangesAsync();
        }
    }
}
