using Dapper;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using System.Data.SqlClient;
using XYY.Common.Standard;

namespace XYY.Core.Standard.Data.Infrastructure
{
    public interface IBaseRepository<T> where T : class
    {

        Task<int> ExecuteSqlNotTransferAsync(string sql);
        Task<int> ExecuteSqlAsync(string sql);
        Task<int> ExecuteSqlAsync(string sql, object param);
        Task<long> InsertAsync(T t);
        Task<bool> InsertAsync(List<T> t);
        Task<bool> InsertNotIdentityAsync(T t);

        Task<TScalar> ExecuteScalarAsync<TScalar>(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null);

        /// <summary>
        /// 向没有主键、非自增表插入数据
        /// </summary>
        /// <param name="t"></param>
        Task<bool> InsertOperateLogAsync(T t);
        Task<PageResult<T>> QueryPager2Async(QueryModel queryModel);
        Task<bool> UpdateAsync(T t);
        Task<bool> UpdateByPrimaryKeyAsync(int pk, string field, object value);
        Task<bool> UpdateByPrimaryKeyAsync<TProperty>(Expression<Func<T, TProperty>> expression, int id, object value);
        Task<bool> UpdateByConditionAsync<TProperty>(Expression<Func<T, TProperty>> expression, string[] propertys, object[] values);
        Task<bool> UpdateByConditionAsync<TProperty>(Expression<Func<T, TProperty>> expression, object t);
        Task<bool> UpdateBySqlAsync<TProperty>(Expression<Func<T, TProperty>> expression, string sql);

        Task<bool> DeleteAsync(T t);
        Task<bool> DeleteAsync(Expression<Func<T, bool>> expression);
        [NonTrans]
        Task<T> GetAsync(int id);
        [NonTrans]
        Task<bool> IsExistsAsync(Expression<Func<T, bool>> expression);
        [NonTrans]
        Task<IEnumerable<T>> QueryAsync(string sql, object param = null);

        [NonTrans]
        Task<IEnumerable<T2>> QueryAsync<T2>(string sql, object param = null);

        [NonTrans]
        Task<IEnumerable<T>> QueryBySqlAsync(string sql);

        [NonTrans]
        Task<IEnumerable<T>> QueryBySqlAsync(string sql, object param);
        [NonTrans]
        Task<IEnumerable<T>> QueryProc(string procName, object param = null);
        [NonTrans]
        Task<IEnumerable<T>> QueryAsync(Expression<Func<T, bool>> expression);
        [NonTrans]
        Task<IEnumerable<T>> QueryAsync(Expression<Func<T, bool>> expression, string customSql);
        [NonTrans]
        Task<IEnumerable<T>> QueryAsync();
        [NonTrans]
        Task<PageResult<T>> QueryPagerAsync(QueryModel queryModel);
        [NonTrans]
        Task<PageResult<T>> QueryPagerAsync(QueryModel queryModel, string customSql);
        [NonTrans]
        Task<int> CountAsync(Expression<Func<T, bool>> expression);
        [NonTrans]
        Task<int> CountAsync(Expression<Func<T, bool>> expression, QueryModel model);
        [NonTrans]
        Task<IDictionary<int, int>> GroupCountAsync<TProperty>(Expression<Func<T, TProperty>> propName, Expression<Func<T, bool>> expression = null, QueryModel model = null);

        Task<object> ExecuteStoredProcedureAsync(string procName, object param = null, int outtime = 60);

        Task<bool> BulkToDBAsync(List<T> t);

        Task<bool> DeleteByIdAsync(int id);
        [NonTrans]
        string GetWhere(List<QueryParamer> paramer);

        Task<DataTable> GetDataTableAsync(string sql); 
    }

    public abstract class BaseRepository<T> : IBaseRepository<T> where T : class
    {

        protected readonly IUnitOfWork _unitOfWork;

        public BaseRepository(IUnitOfWork unitOfWork)
        {
            _unitOfWork = unitOfWork;
        }
        public async Task<int> ExecuteSqlNotTransferAsync(string sql)
        {
            using (var sqlConnection = new SqlConnection(_unitOfWork.ConnectionAddress))
            {
                sqlConnection.Open();
                var sqlCommand = new SqlCommand(sql, sqlConnection);
                return await sqlCommand.ExecuteNonQueryAsync();
            }
        }

        public async Task<DataTable> GetDataTableAsync(string sql)
        {
            using (var sqlConnection = new SqlConnection(_unitOfWork.ConnectionAddress))
            {
                DataTable table = new DataTable("MyTable");
                sqlConnection.Open();
                var dr = await sqlConnection.ExecuteReaderAsync(sql);
                table.Load(dr);
                return table;
            }

        }

        public async Task<object> ExecuteSqlNotTransferAsync(string sql, object obj)
        {
            using (var sqlConnection = new SqlConnection(_unitOfWork.ConnectionAddress))
            {
                sqlConnection.Open();
                return await sqlConnection.ExecuteScalarAsync(sql, obj, null, null, CommandType.StoredProcedure);
            }
        }

        public async Task<int> ExecuteSqlAsync(string sql)
        {
            return await _unitOfWork.Connection.ExecuteAsync(sql, null, _unitOfWork.Transaction);
        }
        public async Task<int> ExecuteSqlAsync(string sql, object param)
        {
            return await _unitOfWork.Connection.ExecuteAsync(sql, param, _unitOfWork.Transaction);

        }


        public async Task<TScalar> ExecuteScalarAsync<TScalar>(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
        {
            if (transaction == null)
            {
                transaction = _unitOfWork.Transaction;
            }
            return await _unitOfWork.Connection.ExecuteScalarAsync<TScalar>(sql, param, transaction, commandTimeout, commandType);
        }

        public virtual async Task<T> GetAsync(int id)
        {
            return await _unitOfWork.GetAsync<T>(id);
        }

        public virtual async Task<IEnumerable<T>> QueryAsync(string sql, object param = null)
        {
            Ensure.NotNullOrEmpty(sql, "sql");
            return await _unitOfWork.Connection.QueryAsync<T>(sql, param);
        }

        [NonTrans]
        public virtual async Task<IEnumerable<T2>> QueryAsync<T2>(string sql, object param = null)
        {
            Ensure.NotNullOrEmpty(sql, "sql");
            return await _unitOfWork.Connection.QueryAsync<T2>(sql, param);
        }

        public virtual async Task<IEnumerable<T>> QueryBySqlAsync(string sql)
        {
            Ensure.NotNullOrEmpty(sql, "sql");
            return await _unitOfWork.QueryBySqlAsync<T>(sql);
        }

        public virtual async Task<IEnumerable<T>> QueryBySqlAsync(string sql, object param)
        {
            Ensure.NotNullOrEmpty(sql, "sql");
            return await _unitOfWork.QueryBySqlAsync<T>(sql, param: param);
        }


        public virtual async Task<IEnumerable<T>> QueryProc(string procName, object param = null)
        {
            throw new Exception("暂不实现");
            //Ensure.NotNullOrEmpty(procName, "procName");
            //return await _unitOfWork.Connection.QueryAsync<T>(sql: procName, 
            //    param: param, 
            //    transaction: _unitOfWork.Transaction,
            //    buffered: true,
            //    commandTimeout: null,
            //    commandType: CommandType.StoredProcedure);
        }

        public virtual async Task<SqlMapper.GridReader> QueryMultipleAsync(string sql, object param)
        {
            Ensure.NotNullOrEmpty(sql, "sql");
            return await _unitOfWork.Connection.QueryMultipleAsync(sql, param);
        }

        public virtual async Task<IEnumerable<T>> QueryAsync(Expression<Func<T, bool>> expression)
        {
            Ensure.NotNull(expression, "expression");
            return await _unitOfWork.QueryAsync<T>(expression);
        }
        public virtual async Task<IEnumerable<T>> QueryAsync(Expression<Func<T, bool>> expression, string customSql)
        {
            Ensure.NotNull(expression, "expression");
            if (string.IsNullOrEmpty(customSql))
                return await this.QueryAsync(expression);
            return await _unitOfWork.QueryAsync<T>(expression, customSql);
        }

        public virtual async Task<PageResult<T>> QueryPagerAsync(QueryModel queryModel)
        {
            Ensure.NotNull(queryModel, "queryModel");
            Ensure.GreaterThan(0, queryModel.PageIndex, "queryModel.PageIndex");
            Ensure.GreaterThan(0, queryModel.PageSize, "queryModel.PageSize");

            return await _unitOfWork.GetPagingListAsync<T>(queryModel);
        }

        public virtual async Task<PageResult<T>> QueryPager2Async(QueryModel queryModel)
        {
            Ensure.NotNull(queryModel, "queryModel");
            Ensure.GreaterThan(0, queryModel.PageIndex, "queryModel.PageIndex");
            Ensure.GreaterThan(0, queryModel.PageSize, "queryModel.PageSize");

            return await _unitOfWork.GetPagingList2Async<T>(queryModel);

        }

        public virtual async Task<PageResult<T>> QueryPagerAsync(QueryModel queryModel, string customSql)
        {
            if (string.IsNullOrEmpty(customSql.Trim()))
                return await QueryPagerAsync(queryModel);
            Ensure.NotNull(queryModel, "queryModel");
            Ensure.GreaterThan(0, queryModel.PageIndex, "queryModel.PageIndex");
            Ensure.GreaterThan(0, queryModel.PageSize, "queryModel.PageSize");

            return await _unitOfWork.GetPagingListAsync<T>(queryModel, customSql);
        }


        public virtual async Task<bool> IsExistsAsync(Expression<Func<T, bool>> expression)
        {
            Ensure.NotNull(expression, "expression");
            return await _unitOfWork.IsExistsAsync(expression);

        }

        public virtual async Task<long> InsertAsync(T t)
        {
            Ensure.NotNull(t, "t");
            return await _unitOfWork.InsertAsync(t);
        }

        public virtual async Task<bool> UpdateAsync(T t)
        {
            Ensure.NotNull(t, "t");
            return await _unitOfWork.UpdateAsync(t);

        }

        public virtual async Task<bool> DeleteAsync(T t)
        {
            Ensure.NotNull(t, "t");
            return await _unitOfWork.DeleteAsync(t);
        }
        public virtual async Task<bool> DeleteAsync(Expression<Func<T, bool>> expression)
        {
            var t = await QueryAsync(expression);
            return await _unitOfWork.DeleteAsync(t.ToList());
        }

        public virtual async Task<bool> DeleteByIdAsync(int id)
        {
            Ensure.GreaterThan(0, id, "id传递有误");
            return await _unitOfWork.DeleteByIdAsync<T>(id);
        }


        public virtual async Task<bool> InsertAsync(List<T> t)
        {
            foreach (var item in t)
            {
                await InsertAsync(item);
            }
            return true;
        }


        public virtual async Task<IEnumerable<T>> QueryAsync()
        {
            return await _unitOfWork.QueryAsync<T>();
        }


        public virtual async Task<bool> UpdateByPrimaryKeyAsync(int pk, string field, object value)
        {
            Ensure.NotNullOrEmpty(field, "field");
            return await _unitOfWork.UpdateByPrimaryKeyAsync<T>(field, value, pk);
        }


        public async Task<bool> UpdateByPrimaryKeyAsync<TProperty>(Expression<Func<T, TProperty>> expression, int id, object value)
        {
            return await _unitOfWork.UpdateByPrimaryKeyAsync<T, TProperty>(expression, id, value);
        }
        public async Task<bool> UpdateByConditionAsync<TProperty>(Expression<Func<T, TProperty>> expression, string[] propertys, object[] values)
        {
            return await _unitOfWork.UpdateByConditionAsync<T, TProperty>(expression, propertys, values);
        }

        public async Task<bool> UpdateByConditionAsync<TProperty>(Expression<Func<T, TProperty>> expression, object t)
        {
            return await _unitOfWork.UpdateByConditionAsync<T, TProperty>(expression, t);
        }

        public async Task<bool> UpdateByConditionNoTransferAsync<TProperty>(Expression<Func<T, TProperty>> expression, object t)
        {
            return await _unitOfWork.UpdateByConditionNoTransferAsync(expression, t);
        }

        public async Task<int> CountAsync(Expression<Func<T, bool>> expression)
        {
            return await _unitOfWork.CountAsync(expression);
        }

        /// <summary>
        /// 执行存储过程
        /// </summary>
        /// <param name="procName">存储过程名称</param>
        /// <param name="param">参数列表</param>
        /// <returns></returns>
        public async Task<object> ExecuteStoredProcedureAsync(string procName, object param = null, int timeout = 60)
        {
            return await _unitOfWork.ExecuteStoredProcedureAsync(procName, param, timeout);
        }


        /// <summary>
        /// Id非自增表的数据插入
        /// </summary>
        /// <param name="t"></param>
        public async Task<bool> InsertNotIdentityAsync(T t)
        {
            await _unitOfWork.InsertNotIdentityAsync(t);
            return true;
        }

        /// <summary>
        /// 向没有主键、非自增表插入数据
        /// </summary>
        /// <param name="t"></param>
        public async Task<bool> InsertOperateLogAsync(T t)
        {
            await _unitOfWork.InsertOperateLogAsync(t);
            return true;
        }

        public virtual async Task<bool> BulkToDBAsync(List<T> t)
        {
            return await _unitOfWork.BulkToDBAsync(t);
        }

        public string GetWhere(List<QueryParamer> paramer)
        {
            return _unitOfWork.GetWhere(paramer);
        }
        public async Task<int> CountAsync(Expression<Func<T, bool>> expression, QueryModel model)
        {
            return await _unitOfWork.CountAsync(expression, model);
        }

        public async Task<IDictionary<int, int>> GroupCountAsync<TProperty>(Expression<Func<T, TProperty>> propName, Expression<Func<T, bool>> expression = null, QueryModel model = null)
        {
            return await _unitOfWork.GroupCountAsync(propName, expression, model);
        }


        public async Task<bool> UpdateBySqlAsync<TProperty>(Expression<Func<T, TProperty>> expression, string sql)
        {
            return await _unitOfWork.UpdateByConditionAsync<T, TProperty>(expression, sql);
        }
    }
}
