using System;
using System.Data;
using System.Data.Common;
using System.Threading.Tasks;
using System.Linq.Expressions;
using System.Collections.Generic;
using DapperORM.Models;
using DapperORM.Enums;
using DapperORM.Common.Models;
using DapperORM.Common.Enums;
using DapperORM.ORMExtention;
using System.Configuration;
using Dapper;

namespace DapperORM
{
    /// <summary>
    /// 仓储基类
    /// </summary>
    public class BaseRepository<TRep> where TRep : class, new()
    {
        #region 成员变量

        /// <summary>
        /// 写入数据的数据库连接
        /// </summary>
        protected readonly string WriteConnStr;

        /// <summary>
        /// 读取数据的数据库连接
        /// </summary>
        protected readonly string ReadConnStr;

        /// <summary>
        /// 数据库驱动工厂对象
        /// </summary>
        protected static DbProviderFactory DbProviderFactory;
        #endregion

        #region 构造函数

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="writeConnStr">写入数据的数据库连接，为NULL读取webConfig中的</param>
        /// <param name="readConnStr">读取数据库的数据库连接</param>
        /// <param name="providerType">数据库驱动类型</param>
        public BaseRepository(string writeConnStr = null, string readConnStr = null, DbProviderType providerType = DbProviderType.MySql)
        {
            WriteConnStr = writeConnStr ?? ConfigurationManager.ConnectionStrings["WriteConnStr"].ConnectionString;
            ReadConnStr = readConnStr ?? ConfigurationManager.ConnectionStrings["ReadConnStr"].ConnectionString;
            DbProviderFactory = ProviderFactory.GetDbProviderFactory(providerType);
            if (DbProviderFactory == null)
                throw new ArgumentException("无法加载对应的数据库驱动");
        }

        #endregion

        #region 底层基础读写分离封装

        /// <summary>
        /// 执行写操作
        /// </summary>
        /// <typeparam name="RType">返回结果类型</typeparam>
        /// <param name="func">执行方法</param>
        /// <returns></returns>
        protected internal async Task<RType> ExecuteWriteAsync<RType>(Func<IDbConnection, Task<RType>> func) where RType : ResultModel, new()
            => await ExecuteAsync(func, WriteConnStr);

        /// <summary>
        /// 执行读操作，返回具体类型
        /// </summary>
        /// <typeparam name="RType"></typeparam>
        /// <param name="func"></param>
        /// <returns></returns>
        protected internal async Task<ResultModel<RType>> ExecuteReadAsync<RType>(Func<IDbConnection, Task<RType>> func)
            => await ExecuteAsync(async conn =>
            {
                var res = await func(conn);
                return res != null ? new ResultModel<RType>(res) : new ResultModel<RType>(ResultType.ObjectNull, "未发现相关数据！");
            }, ReadConnStr);

        /// <summary>
        /// 执行读操作，返回继承自ResultModel的实体
        /// </summary>
        /// <typeparam name="RType"></typeparam>
        /// <param name="func"></param>
        /// <returns></returns>
        protected internal async Task<RType> ExecuteReadResAsync<RType>(Func<IDbConnection, Task<RType>> func) where RType : ResultModel, new()
            => await ExecuteAsync(func, ReadConnStr);

        /// <summary>
        /// 执行方法
        /// </summary>
        /// <typeparam name="RType">返回结果类型</typeparam>
        /// <param name="func">执行方法</param>
        /// <param name="connecStr">连接字符串</param>
        /// <returns></returns>
        private static async Task<RType> ExecuteAsync<RType>(Func<IDbConnection, Task<RType>> func, string connStr) where RType : ResultModel, new()
        {
            var t = default(RType);
            try
            {
                using (var conn = DbProviderFactory.CreateConnection())
                {
                    conn.ConnectionString = connStr;
                    if (conn.State == ConnectionState.Closed)
                        conn.Open();

                    t = await func(conn);
                }
            }
            catch (Exception e)
            {
#if DEBUG
                throw e;
#else
                 //记录错误的日志
#endif
            }
            return t ?? new RType() { Result = (int)ResultType.ObjectNull, Msg = "未发现对应结果响应" };
        }

        /// <summary>
        /// 执行dataset方法
        /// </summary>
        /// <param name="func">方法</param>
        /// <param name="connStr">连接字符串</param>
        /// <returns></returns>
        private static async Task<ResultModelDataSet> ExecuteDataSetAsync(Func<IDbConnection, Task<ResultModelDataSet>> func, string connStr)
        {
            var t = default(ResultModelDataSet);
            try
            {
                using (var conn = DbProviderFactory.CreateConnection())
                {
                    conn.ConnectionString = connStr;
                    if (conn.State == ConnectionState.Closed)
                        conn.Open();

                    t = await func(conn);
                }
            }
            catch (Exception e)
            {
#if DEBUG
                throw e;
#else
                 //记录错误的日志
#endif
            }
            return t ?? new ResultModelDataSet((int)ResultType.ObjectNull, "未发现相关数据");
        }

        #endregion

        #region 基础CRUD操作方法

        #region 插入数据

        /// <summary>
        /// 单条插入
        /// </summary>
        /// <param name="obj">数据库表对象实体</param>
        /// <returns>操作结果</returns>
        public virtual async Task<ResultModel> InsertAsync<T>(T obj) where T : BaseModel, new()
            => await ExecuteWriteAsync(conn => conn.InsertAsync(obj));

        /// <summary>
        /// 批量插入（单表）
        /// </summary>
        /// <param name="objs">数据库表对象实体集合</param>
        /// <returns>操作结果</returns>
        public virtual async Task<ResultModel> InsertManyAsync<T>(IList<T> objs) where T : BaseModel, new()
            => await ExecuteWriteAsync(conn => conn.InsertManyAsync(objs));

        #endregion

        #region 读取数据

        /// <summary>
        /// 执行查询语句
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="sql">SQL语句</param>
        /// <param name="param">参数</param>
        /// <returns>查询结果</returns>
        public virtual async Task<ResultModel<IEnumerable<T>>> QueryAsync<T>(string sql, Dictionary<string, object> param = null)
        {
            var paramters = new DynamicParameters(param);
            return await ExecuteReadAsync(conn => conn.QueryAsync<T>(sql, paramters));
        }

        /// <summary>
        /// 执行查询语句
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="sql">SQL语句</param>
        /// <param name="param">参数</param>
        /// <returns>查询结果</returns>
        public virtual async Task<ResultModel<T>> QueryFirstOrDefaultAsync<T>(string sql, Dictionary<string, object> param = null)
        {
            var paramters = new DynamicParameters(param);
            return await ExecuteReadAsync(conn => conn.QueryFirstOrDefaultAsync<T>(sql, paramters));
        }

        /// <summary>
        /// 读取单条数据
        /// </summary>
        /// <param name="where">查询条件字段</param>
        /// <param name="select">要查询的字段</param>
        /// <param name="sort">排序条件,true=升序,false=降序</param>
        /// <returns>查询结果</returns>
        public virtual async Task<ResultModel<T>> GetAsync<T>(Expression<Func<T, bool>> where = null, Expression<Func<T, object>> select = null, IDictionary<string, bool> sort = null)
            => await ExecuteReadAsync(conn => conn.GetAsync(where, select, sort));

        /// <summary>
        /// 读取集合数据
        /// </summary>
        /// <param name="where">条件</param>
        /// <param name="select">要查询的字段</param>
        /// <param name="sort">排序条件,true=升序,false=降序</param>
        /// <returns>查询结果</returns>
        public virtual async Task<ResultModel<IEnumerable<T>>> FetchAsync<T>(Expression<Func<T, bool>> where = null, Expression<Func<T, object>> select = null, IDictionary<string, bool> sort = null)
            => await ExecuteReadAsync(conn => conn.FetchAsync(where, select, sort));

        /// <summary>
        /// 获取记录条数
        /// </summary>
        /// <param name="where">where条件</param>
        /// <returns>记录条数</returns>
        public virtual async Task<ResultModel<int>> GetCountAsync<T>(Expression<Func<T, bool>> where = null)
            => await ExecuteReadAsync(conn => conn.GetCountAsync(where));

        /// <summary>
        /// 获取记录条数
        /// </summary>
        /// <param name="sql">sql</param>
        /// <param name="param">参数</param>
        /// <returns>记录条数</returns>
        public virtual async Task<ResultModel<int>> GetCountAsync(string sql, Dictionary<string, object> param = null)
            => await ExecuteReadAsync(conn => conn.ExecuteScalarAsync<int>(sql, param));

        /// <summary>
        /// 分页查询
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="selectSql">查询数据语句</param>
        /// <param name="totalSql">查询记录总条数的语句</param>
        /// <param name="paramters">参数</param>
        /// <param name="pageIndex">页码，默认1</param>
        /// <param name="pageSize">页大小，默认10</param>
        /// <returns>分页结果</returns>
        public virtual async Task<PageListModel<T>> PagedFetchAsync<T>(string selectSql, string totalSql, Dictionary<string, object> paramters = null, int pageIndex = 1, int pageSize = 10) where T : class, new()
        {
            return await ExecuteReadResAsync(async conn =>
            {
                var param = new DynamicParameters(paramters);
                int totalReadCount = await conn.ExecuteScalarAsync<int>(totalSql, param);
                var list = await conn.QueryAsync<T>(selectSql, param);
                return new PageListModel<T>(ResultType.Success, pageIndex, pageSize, totalReadCount, list.AsList());
            });
        }

        /// <summary>
        /// 执行查询语句返回dataset
        /// </summary>
        /// <param name="sql">查询语句</param>
        /// <param name="param">参数</param>
        /// <param name="commandTimeout">超时时间</param>
        /// <param name="commandType">类型</param>
        /// <returns>dataset</returns>
        public virtual async Task<ResultModelDataSet> QueryDataSetAysnc(string sql, Dictionary<string, object> param = null, int? commandTimeout = null, CommandType? commandType = null)
        {
            return await ExecuteDataSetAsync(async conn =>
            {
                var ds = await conn.QueryDataSetAsync(sql, param, commandTimeout, commandType);
                return ds != null ? new ResultModelDataSet(ds) : new ResultModelDataSet((int)ResultType.ObjectNull, "未发现对应结果响应");
            }, ReadConnStr);
        }

        #endregion

        #region 修改数据

        /// <summary>
        /// 更新全部字段
        /// </summary>
        /// <param name="model">更新值实体对象</param>
        /// <param name="whereExp">条件表达式</param>
        /// <returns>操作结果</returns>
        public virtual async Task<ResultModel> UpdateAllAsync<T>(T model, Expression<Func<T, bool>> whereExp = null) where T : BaseModel, new()
            => await ExecuteWriteAsync(conn => conn.UpdateAllAsync(model, whereExp));

        /// <summary>
        /// 更新部分字段
        /// </summary>
        /// <param name="model">更新值实体对象</param>
        /// <param name="updateExp">更新字段表达式</param>
        /// <param name="whereExp">条件表达式</param>
        /// <returns>操作结果</returns>
        public virtual async Task<ResultModel> UpdatePartailAsync<T>(T model, Expression<Func<T, object>> updateExp, Expression<Func<T, bool>> whereExp = null) where T : BaseModel, new()
            => await ExecuteWriteAsync(conn => conn.UpdatePartailAsync(model, updateExp, whereExp));

        #endregion

        #region 删除数据

        /// <summary>
        /// 物理删除
        /// </summary>
        /// <param name="whereExp">删除条件</param>
        /// <returns>操作结果</returns>
        public virtual async Task<ResultModel> PhysicalDeleteAsync<T>(Expression<Func<T, bool>> whereExp = null) where T : BaseModel, new()
            => await ExecuteWriteAsync(conn => conn.DeleteAsync(whereExp));

        /// <summary>
        /// 逻辑删除
        /// </summary>
        /// <param name="whereExp">删除条件</param>
        /// <param name="model">赋值实体</param>
        /// <returns>操作结果</returns>
        public virtual async Task<ResultModel> LogicalDeleteAsync<T>(Expression<Func<T, bool>> whereExp = null, T model = null) where T : BaseModel, new()
        {
            if (model == null)
                model = new T();

            model.Is_Deleted = (int)TrueFalseStatus.True;
            model.Update_Time = DateTime.Now;
            return await ExecuteWriteAsync(conn => conn.UpdatePartailAsync(model, x => new { x.Is_Deleted }, whereExp));
        }

        #endregion

        #endregion

        #region 事务
        /// <summary>
        /// 执行事务
        /// </summary>
        /// <param name="trans">事务内的语句，元组 string是语句，obj是参数</param>
        /// <param name="commandTimeout">超时时间</param>
        /// <returns>操作结果</returns>
        public virtual async Task<ResultModel> ExecuteTransactionAsync(IList<Tuple<string, object>> trans, int? commandTimeout = null)
            => await ExecuteWriteAsync(conn => conn.ExecuteTransactionAsync(trans, commandTimeout));

        #endregion

        #region 执行非查询语句
        /// <summary>
        /// 执行非查询语句
        /// </summary>
        /// <param name="sql">执行语句</param>
        /// <param name="param">参数</param>
        /// <param name="trans">事务</param>
        /// <param name="commandTimeout">超时时间</param>
        /// <param name="commandType">类型</param>
        /// <returns>操作结果</returns>
        public virtual async Task<ResultModel> ExecuteNonQueryAsync(string sql, Dictionary<string, object> param, IDbTransaction trans = null, int? commandTimeout = null, CommandType? commandType = null)
         => await ExecuteWriteAsync(conn => conn.ExecuteNonQueryAsync(sql, param, trans, commandTimeout, commandType));
        #endregion

        #region 辅助方法

        #region 获取插入语句
        /// <summary>
        /// 获取插入语句
        /// </summary>
        /// <typeparam name="T">数据库表实体类型</typeparam>
        /// <returns>插入语句</returns>
        public static string GetInsertSql<T>()
        {
            return ConnectionExtention.GetInsertSql<T>();
        }
        #endregion

        #region 获取删除语句
        /// <summary>
        /// 获取删除语句
        /// </summary>
        /// <typeparam name="T">数据库表实体类型</typeparam>
        /// <param name="whereExp">where条件</param>
        /// <returns>删除语句</returns>
        public static string GetDeleteSql<T>(Expression<Func<T, bool>> whereExp)
        {
            return ConnectionExtention.GetDeleteSql<T>(whereExp);
        }
        #endregion

        #region 获取更新语句
        /// <summary>
        /// 获取更新语句
        /// </summary>
        /// <typeparam name="T">要更新的实体类型</typeparam>
        /// <param name="update">要更新的字段，空更新全部字段</param>
        /// <param name="where">where条件</param>
        /// <returns>更新语句</returns>
        public static string GetUpdateSql<T>(Expression<Func<T, object>> update, Expression<Func<T, bool>> where = null)
        {
            return ConnectionExtention.GetUpdateSql<T>(update, where);
        }
        #endregion

        #endregion

    }
}
