using System;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
using DapperExtensions.Mapper;
using DapperExtensions.Sql;
using Mall.DataAccess;
using System.Collections.Concurrent;
using System.Configuration;
using DapperExtensions.Lambda;

namespace DapperExtensions
{
    /// <summary>
    /// Dapper扩展类
    /// </summary>
    public static class DapperExtension
    {
        /// <summary>
        /// 应对多类型数据库  需要配置 EnabledMultipleDB =true
        /// </summary>
        [ThreadStatic]
        private static IDapperImplementor _DapperImplementor_MultipleDBType;
        /// <summary>
        /// 单一类型数据库
        /// </summary>
        private static IDapperImplementor _DapperImplementor;

        /// <summary>
        /// //是否启用多类型数据库
        /// </summary>
        private static readonly bool IsEnabledMultipleDBType = Convert.ToBoolean((ConfigurationManager.AppSettings["IsEnabledMultipleDBType"] ?? "false"));
        private const DataBaseType DefaultDBType = DataBaseType.MySql;
        private static Func<IDapperExtensionConfiguration, DataBaseType, IDapperImplementor> _instanceFactory;
        private static ConcurrentDictionary<DataBaseType, IDapperImplementor> _instanceList = new ConcurrentDictionary<DataBaseType, IDapperImplementor>();

        /// <summary>
        /// ORM映射的程序集
        /// </summary>
        private static readonly string ormMapperAssemblyStr = (ConfigurationManager.AppSettings["OrmMapperAssemblyStr"] ?? "");

        /// <summary>
        /// 单一类型数据库对象
        /// </summary>
        public static IDapperImplementor DapperImplementor
        {
            get
            {
                if (!IsEnabledMultipleDBType)
                {
                    if (null == _DapperImplementor)
                    {
                        Instance();
                    }
                    return DapperExtension._DapperImplementor;
                }
                else
                {
                    if (null == _DapperImplementor_MultipleDBType)
                    {
                        Instance();
                    }
                    return DapperExtension._DapperImplementor_MultipleDBType;
                }
            }
        }

        /// <summary>
        /// Get or sets the Dapper Extensions Implementation Factory.
        /// 获取或设置Dapper扩展实现工厂
        /// </summary>
        public static Func<IDapperExtensionConfiguration, DataBaseType, IDapperImplementor> InstanceFactory
        {
            get
            {
                if (_instanceFactory == null)
                {
                    _instanceFactory = (config, dbType) => new DapperImplementor(new SqlGeneratorImpl(config, dbType), dbType);
                }
                return _instanceFactory;
            }
        }

        /// <summary>
        /// 活动Dapper扩展对象的实例
        /// </summary>
        /// <param name="dbType">DB类型，默认SqlServer</param>
        /// <param name="ormMapperAssemblyList">ORM映射的程序集</param>
        /// <returns></returns>
        public static IDapperImplementor Instance(DataBaseType dbType = DefaultDBType, List<string> ormMapperAssemblyList = null)
        {
            dbType = DataBaseType.MySql;
            if (!_instanceList.TryGetValue(dbType, out IDapperImplementor instance))
            {
                ISqlDialect sqlDialect;
                switch (dbType)
                {
                    case DataBaseType.SqlServer: sqlDialect = new SqlServerDialect(); break;
                    case DataBaseType.Oracle: sqlDialect = new OracleDialect(); break;
                    case DataBaseType.MySql: sqlDialect = new MySqlDialect(); break;
                    default: sqlDialect = new SqlServerDialect(); break;
                }
                List<Assembly> assemblyList = new List<Assembly>();
                if (ormMapperAssemblyList != null)
                {
                    foreach (var item in ormMapperAssemblyList)
                    {
                        assemblyList.Add(Assembly.Load(item));
                    }
                }
                else
                {
                    if (!string.IsNullOrEmpty(ormMapperAssemblyStr))
                    {
                        foreach (var item in ormMapperAssemblyStr.Split(';'))
                        {
                            if (!string.IsNullOrEmpty(item))
                            {
                                assemblyList.Add(Assembly.Load(item));
                            }
                        }
                    }
                }
                IDapperExtensionConfiguration iDapperExtensionsConfiguration = new DapperExtensionConfiguration(
                    typeof(AutoClassMapper<>),
                   assemblyList,
                    sqlDialect
                    );
                instance = InstanceFactory(iDapperExtensionsConfiguration, dbType);
                _instanceList[dbType] = instance;
            }
            if (!IsEnabledMultipleDBType)
            {
                if (null == _DapperImplementor)
                {
                    _DapperImplementor = instance;
                }
            }
            else
            {
                if (null == _DapperImplementor_MultipleDBType || _DapperImplementor_MultipleDBType.DbType != dbType)
                {
                    _DapperImplementor_MultipleDBType = instance;
                }
            }
            return instance;
        }

        /// <summary>
        /// 查询
        /// </summary>
        /// <typeparam name="T">泛型约束</typeparam>
        /// <param name="connection">连接对象</param>
        /// <param name="id">动态编号</param>
        /// <param name="transaction">事务对象</param>
        /// <param name="commandTimeout">超时时间</param>
        /// <param name="dbType">数据库类型</param>
        /// <returns></returns>
        public static T Get<T>(this IDbConnection connection, dynamic id, IDbTransaction transaction = null, int? commandTimeout = null, DataBaseType dbType = DefaultDBType) where T : class
        {
            var result = Instance(dbType).Get<T, T>(connection, id, transaction, commandTimeout);
            return (T)result;
        }

        /// <summary>
        /// Executes a query for the specified id, returning the data typed as per T
        /// 对指定的id执行查询，返回输入的数据。
        /// </summary>
        /// <typeparam name="T">泛型约束</typeparam>
        /// <typeparam name="TReturn">泛型约束</typeparam>
        /// <param name="connection">数据库连接对象</param>
        /// <param name="id"></param>
        /// <param name="transaction"></param>
        /// <param name="commandTimeout"></param>
        /// <param name="dbType"></param>
        /// <returns></returns>
        public static TReturn Get<T, TReturn>(this IDbConnection connection, dynamic id, IDbTransaction transaction = null, int? commandTimeout = null, DataBaseType dbType = DefaultDBType)
            where T : class
            where TReturn : class
        {
            var result = Instance(dbType).Get<T, TReturn>(connection, id, transaction, commandTimeout);
            return (TReturn)result;
        }

        /// <summary>
        /// Executes an insert query for the specified entity, returning the primary key.  
        /// If the entity has a single key, just the value is returned.  
        /// If the entity has a composite key, an IDictionary&lt;string, object&gt; is returned with the key values.
        /// The key value for the entity will also be updated if the KeyType is a Guid or Identity.
        /// </summary>
        public static dynamic Insert<T>(this IDbConnection connection, T entity, IDbTransaction transaction = null, int? commandTimeout = null, DataBaseType dbType = DefaultDBType) where T : class
        {
            return Instance(dbType).Insert<T>(connection, entity, transaction, commandTimeout);
        }

        /// <summary>
        /// Executes an update query for the specified entity.
        /// </summary>
        public static bool Update<T>(this IDbConnection connection, T entity, IDbTransaction transaction = null, int? commandTimeout = null, DataBaseType dbType = DefaultDBType) where T : class
        {
            return Instance(dbType).Update<T>(connection, entity, transaction, commandTimeout);
        }

        /// <summary>
        /// Executes a delete query for the specified entity.
        /// </summary>
        public static int Delete<T>(this IDbConnection connection, T entity, IDbTransaction transaction = null, int? commandTimeout = null, DataBaseType dbType = DefaultDBType) where T : class
        {
            return Instance(dbType).Delete<T>(connection, entity, transaction, commandTimeout);
        }

        /// <summary>
        /// Executes a delete query using the specified predicate.
        /// </summary>
        public static int Delete<T>(this IDbConnection connection, object predicate, IDbTransaction transaction = null, int? commandTimeout = null, DataBaseType dbType = DefaultDBType) where T : class
        {
            return Instance(dbType).Delete<T>(connection, predicate, transaction, commandTimeout);
        }

        /// <summary>
        /// Executes a select query using the specified predicate, returning an IEnumerable data typed as per T.
        /// </summary>
        public static IEnumerable<T> GetList<T>(this IDbConnection connection, object predicate = null, IList<ISort> sort = null, IDbTransaction transaction = null, int? commandTimeout = null, DataBaseType dbType = DefaultDBType) where T : class
        {
            return Instance(dbType).GetList<T, T>(connection, predicate, sort, transaction, commandTimeout);
        }

        /// <summary>
        /// Executes a select query using the specified predicate, returning an IEnumerable data typed as per T.
        /// </summary>
        public static IEnumerable<TReturn> GetList<T, TReturn>(this IDbConnection connection, object predicate = null, IList<ISort> sort = null, IDbTransaction transaction = null, int? commandTimeout = null, DataBaseType dbType = DefaultDBType)
            where T : class
            where TReturn : class
        {
            return Instance(dbType).GetList<T, TReturn>(connection, predicate, sort, transaction, commandTimeout);
        }

        /// <summary>
        /// 获取分页
        /// </summary>
        /// <typeparam name="T">约束</typeparam>
        /// <param name="connection">连接对象</param>
        /// <param name="page">页码</param>
        /// <param name="resultsPerPage">页大小</param>
        /// <param name="allRowsCount">总条数</param>
        /// <param name="sql">sql命令</param>
        /// <param name="param">动态参数</param>
        /// <param name="allRowsCountSql">总条数sql</param>
        /// <param name="transaction">事务</param>
        /// <param name="commandTimeout">超时时间</param>
        /// <param name="dbType">数据库类型</param>
        /// <returns></returns>
        public static IEnumerable<T> GetPage<T>(this IDbConnection connection, int page, int resultsPerPage, out long allRowsCount, string sql, dynamic param = null, string allRowsCountSql = null, IDbTransaction transaction = null, int? commandTimeout = null, DataBaseType dbType = DefaultDBType) where T : class
        {
            return Instance(dbType).GetPage<T>(connection, page, resultsPerPage, out allRowsCount, sql, param, allRowsCountSql, transaction, commandTimeout);
        }

        /// <summary>
        /// 获取条数
        /// </summary>
        /// <typeparam name="T">约束</typeparam>
        /// <param name="connection">连接对象</param>
        /// <param name="predicate">动态参数</param>
        /// <param name="transaction">事务</param>
        /// <param name="commandTimeout">超时时间</param>
        /// <param name="dbType">数据库类型</param>
        /// <returns></returns>
        public static int Count<T>(this IDbConnection connection, object predicate, IDbTransaction transaction = null, int? commandTimeout = null, DataBaseType dbType = DefaultDBType) where T : class
        {
            return Instance(dbType).Count<T>(connection, predicate, transaction, commandTimeout);
        }

        /// <summary>
        /// LambdaUpdate
        /// </summary>
        /// <param name="connection">连接对象</param>
        /// <param name="transaction">事务</param>
        /// <param name="commandTimeout">超时时间</param>
        /// <param name="dbType">数据库类型</param>
        /// <returns></returns>
        public static LambdaUpdateHelper<T> LambdaUpdate<T>(this IDbConnection connection, IDbTransaction transaction = null, int? commandTimeout = null, DataBaseType dbType = DefaultDBType) where T : class
        {
            return Instance(dbType).LambdaUpdate<T>(connection, transaction, commandTimeout);
        }

        /// <summary>
        /// LambdaUpdate
        /// </summary>
        /// <typeparam name="T">约束</typeparam>
        /// <param name="connKey">连接字符串</param>
        /// <param name="commandTimeout">超时时间</param>
        /// <param name="dbType">数据库类型</param>
        /// <returns></returns>
        public static LambdaUpdateHelper<T> LambdaUpdate<T>(string connKey, int? commandTimeout = null, DataBaseType dbType = DefaultDBType) where T : class
        {
            return Instance(dbType).LambdaUpdate<T>(connKey, commandTimeout);
        }

        /// <summary>
        /// LambdaQuery
        /// </summary>
        /// <typeparam name="T">约束</typeparam>
        /// <param name="connection">连接对象</param>
        /// <param name="transaction">事务</param>
        /// <param name="commandTimeout">超时时间</param>
        /// <param name="dbType">数据库类型</param>
        /// <returns></returns>
        public static LambdaQueryHelper<T> LambdaQuery<T>(this IDbConnection connection, IDbTransaction transaction = null, int? commandTimeout = null, DataBaseType dbType = DefaultDBType) where T : class
        {
            return Instance(dbType).LambdaQuery<T>(connection, transaction, commandTimeout);
        }

        /// <summary>
        /// LambdaQuery
        /// </summary>
        /// <typeparam name="T">约束</typeparam>
        /// <param name="connKey">连接字符串</param>
        /// <param name="commandTimeout">超时时间</param>
        /// <param name="dbType">数据库类型</param>
        /// <returns></returns>
        public static LambdaQueryHelper<T> LambdaQuery<T>(string connKey, int? commandTimeout = null, DataBaseType dbType = DefaultDBType) where T : class
        {
            return Instance(dbType).LambdaQuery<T>(connKey, commandTimeout);
        }
    }
}