using System.Data;
using System.Data.Common;
using System.Linq.Expressions;

namespace HealthMonitor.Core.Dal
{
    /// <summary>
    /// 数据接入层接口
    /// </summary>
    public interface IDataAccessor : IWithDataSchema, IDisposable
    {
        /// <summary>
        /// 是否已关闭
        /// </summary>
        /// <returns></returns>
        bool IsClose();

        /// <summary>
        /// 关闭连接
        /// </summary>
        void Close();

        /// <summary>
        /// 获取第一个符合条件的实体,或返回null
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        T? GetFirstOrDefault<T>(Expression<Func<T, bool>> expression) where T : class;

        Task<T> GetFirstOrDefaultAsync<T>(Expression<Func<T, bool>> expression) where T : class;

        /// <summary>
        /// 根据主键值来查询某条记录,单主键的表可直接输入主键,多主键的表注意主键次序
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="values">主键值</param>
        /// <returns>返回主键值为传入值的实体</returns>
        T GetByID<T>(params object[] values) where T : class;

        /// <summary>
        /// 根据主键值来查询某条记录,单主键的表可直接输入主键,多主键的表注意主键次序
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="values">主键值</param>
        /// <returns>返回主键值为传入值的实体</returns>
        Task<T> GetByIDAsync<T>(params object[] values) where T : class;

        /// <summary>
        /// 获取某个表的所有数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        IQueryable<T> GetAll<T>() where T : class;

        /// <summary>
        /// 根据表达式进行查询返回结果
        /// </summary>
        /// <typeparam name="T">查询的类</typeparam>
        /// <param name="expression">查询表达式</param>
        /// <returns></returns>
        IQueryable<T> GetMany<T>(Expression<Func<T, bool>> expression) where T : class;

        /// <summary>
        /// 根据某个字段的值进行排序
        /// </summary>
        /// <typeparam name="T">排序后获得的集合的类型</typeparam>
        /// <typeparam name="TKey">排序字段的类型</typeparam>
        /// <param name="orderExpression">字段表达式 如:basicInfo下根据caseID排序(s=>s.caseID)</param>
        /// <param name="isASC">是否升序</param>
        /// <returns></returns>
        IEnumerable<T> Order<T, TKey>(Func<T, TKey> orderExpression, bool isASC = false) where T : class;

        /// <summary>
        /// 在数据库中进行分页查询
        /// </summary>
        /// <typeparam name="T">查询的类</typeparam>
        /// <typeparam name="Tkey">查询类的主键类型</typeparam>
        /// <param name="pageSize">每页条数</param>
        /// <param name="pageIdx">当前页</param>
        /// <param name="expression">查询表达式</param>
        /// <param name="orderExpression"></param>
        /// <returns></returns>
        List<T> GetPageList<T, Tkey>(int pageSize, int pageIdx, Expression<Func<T, bool>> expression, Func<T, Tkey> orderExpression) where T : class;

        /// <summary>
        /// 在数据库中进行分页查询
        /// </summary>
        /// <typeparam name="T">查询的类</typeparam>
        /// <typeparam name="Tkey">查询类的主键类型</typeparam>
        /// <param name="pageSize">页大小</param>
        /// <param name="pageIdx">页下标</param>
        /// <param name="expression">查询表达式</param>
        /// <param name="orderExpression">排序表达式</param>
        /// <returns>分页查询结果</returns>
        Task<List<T>> GetPageListAsync<T, Tkey>(int pageSize, int pageIdx, Expression<Func<T, bool>> expression, Func<T, Tkey> orderExpression) where T : class;

        /// <summary>
        /// 获取条目数
        /// </summary>
        /// <typeparam name="T">查询的类</typeparam>
        /// <param name="expression">查询表达式</param>
        /// <returns>条目数</returns>
        int GetCount<T>(Expression<Func<T, bool>> expression) where T : class;

        /// <summary>
        /// 获取条目数
        /// </summary>
        /// <typeparam name="T">查询的类</typeparam>
        /// <param name="expression">查询表达式</param>
        /// <returns>条目数</returns>
        Task<int> GetCountAsync<T>(Expression<Func<T, bool>> expression) where T : class;

        /// <summary>
        /// 插入数据,保存并更新其自增键
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="model"></param>
        /// <returns></returns>
        T AddWithIdentity<T>(T model) where T : class;

        /// <summary>
        /// 插入数据,保存并更新其自增键
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="model"></param>
        /// <returns></returns>
        Task<T> AddWithIdentityAsync<T>(T model) where T : class;

        /// <summary>
        /// 向上下文增加记录,但不保存,需要手动调用Save
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="model"></param>
        /// <returns></returns>
        void Add<T>(T model) where T : class;

        /// <summary>
        /// 向上下文增加记录,但不保存,需要手动调用Save
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        void Add(object model);

        /// <summary>
        /// 批量插入实体
        /// </summary>
        /// <param name="models"></param>
        void AddRange(IEnumerable<object> models);

        /// <summary>
        /// 按主键标记实体删除
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="model"></param>
        /// <returns></returns>
        void Delete<T>(T model) where T : class;

        /// <summary>
        /// 按主键标记实体删除
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        void Delete(object model);

        /// <summary>
        ///批量删除实体
        /// </summary>
        /// <param name="models"></param>
        void DeleteRanage(IEnumerable<object> models);

        /// <summary>
        /// 更新操作
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="model"></param>
        /// <returns></returns>
        void Update<T>(T model) where T : class;

        /// <summary>
        /// 根据主键更新指定字段,使用时需要注意EFCore的仓储机制可能会使数据库和缓存仓的数据不一致。
        /// </summary>
        /// <typeparam name="T">映射类</typeparam>
        /// <param name="model">更新的实体</param>
        /// <param name="property">要更新的属性, VisualStudio在这里有Bug, 不能智能显示类型属性, 但不影响使用</param>
        /// <returns></returns>
        void Update<T>(T model, Expression<Func<T, object>> property) where T : class;

        /// <summary>
        /// 更新操作
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        void Update(object model);

        /// <summary>
        /// 批量更新实体
        /// </summary>
        /// <param name="models"></param>
        void UpdateRanage(IEnumerable<object> models);

        /// <summary>
        /// 执行存储过程,返回存储过程中返回的数据表
        /// </summary>
        /// <param name="procName">存储过程名</param>
        /// <param name="parameters">参数</param>
        /// <returns>返回的数据表集合</returns>
        List<DataTable> CallProcedure(string procName, params DbParameter[] parameters);

        /// <summary>
        /// 执行存储过程,返回存储过程中返回的数据表
        /// </summary>
        /// <param name="procName">存储过程名</param>
        /// <param name="parameters">参数</param>
        /// <returns>返回的数据表集合</returns>
        Task<List<DataTable>?> CallProcedureAsync(string procName, params DbParameter[] parameters);

        /// <summary>
        /// 提交对数据进行的处理,如无处理返回-1
        /// </summary>
        /// <returns></returns>
        int Save();

        /// <summary>
        /// 提交对数据进行的处理,如无处理返回-1
        /// </summary>
        /// <returns></returns>
        Task<int> SaveAsync();

        ///// <summary>
        ///// 异步获取一个事务对象
        ///// </summary>
        ///// <returns></returns>
        //Task<IDbContextTransaction> BeginTransactionAsync();

        ///// <summary>
        ///// 获取一个事务对象
        ///// </summary>
        ///// <returns></returns>
        //IDbContextTransaction BeginTransaction();

        /// <summary>
        /// 不跟踪实体状态(针对DbContext所用)
        /// </summary>
        /// <param name="models"></param>
        void UntrackEntities(IEnumerable<object> models);

        /// <summary>
        /// 不跟踪实体状态(针对DbContext所用)
        /// </summary>
        /// <param name="models"></param>
        void UntrackEntities(object model);
    }
}