using System.Data;
using System.Linq.Expressions;
using System.Text.RegularExpressions;

namespace ThingsGateway.SqlSugar
{
    public partial class UpdateableProvider<T> : IUpdateable<T> where T : class, new()
    {
        private bool IsUpdateNullByList()
        {
            return this.UpdateObjs.Length > 1 && (this.UpdateBuilder.IsNoUpdateNull || this.UpdateBuilder.IsNoUpdateDefaultValue);
        }

        private int DatasTrackingExecommand()
        {
            var trakRows = 0;
            var isNoTran = this.Context.Ado.IsNoTran();
            try
            {
                if (isNoTran)
                {
                    this.Context.Ado.BeginTran();
                }
                int i = 0;
                foreach (var item in this.UpdateObjs)
                {
                    var newUpdateable = this.Clone();
                    (newUpdateable as UpdateableProvider<T>).UpdateObjs = new[] { item };
                    newUpdateable.UpdateBuilder.IsListUpdate = null;
                    newUpdateable.UpdateBuilder.DbColumnInfoList =
                        newUpdateable.UpdateBuilder.DbColumnInfoList.Where(it => it.TableId == i).ToList();
                    AppendTracking(item, newUpdateable);
                    if (newUpdateable.UpdateBuilder.DbColumnInfoList?.Count > 0)
                    {
                        trakRows += newUpdateable.ExecuteCommand();
                    }
                    ++i;
                }
                if (isNoTran)
                {
                    this.Context.Ado.CommitTran();
                }
            }
            catch (Exception)
            {
                if (isNoTran)
                {
                    this.Context.Ado.RollbackTran();
                }
                throw;
            }
            return trakRows;
        }
        private async Task<int> DatasTrackingExecommandAsync()
        {
            var isNoTran = this.Context.Ado.IsNoTran();
            var trakRows = 0;
            try
            {
                if (isNoTran)
                {
                    await Context.Ado.BeginTranAsync().ConfigureAwait(false);
                }
                int i = 0;
                foreach (var item in this.UpdateObjs)
                {
                    var newUpdateable = this.Clone();
                    (newUpdateable as UpdateableProvider<T>).UpdateObjs = new[] { item };
                    newUpdateable.UpdateBuilder.IsListUpdate = null;
                    newUpdateable.UpdateBuilder.DbColumnInfoList =
                        newUpdateable.UpdateBuilder.DbColumnInfoList.Where(it => it.TableId == i).ToList();
                    AppendTracking(item, newUpdateable);
                    if (newUpdateable.UpdateBuilder.DbColumnInfoList?.Count > 0)
                    {
                        trakRows += await newUpdateable.ExecuteCommandAsync().ConfigureAwait(false);
                    }
                    ++i;
                }
                if (isNoTran)
                {
                    await Context.Ado.CommitTranAsync().ConfigureAwait(false);
                }
            }
            catch (Exception)
            {
                if (isNoTran)
                {
                    await Context.Ado.RollbackTranAsync().ConfigureAwait(false);
                }
                throw;
            }
            return trakRows;
        }
        private bool UpdateObjectNotWhere()
        {
            if (this.Context?.CurrentConnectionConfig?.MoreSettings?.DatabaseModel == DbType.SqlServer)
            {
                return false;
            }
            return this.Context.CurrentConnectionConfig.DbType != DbType.MySql
                && this.Context.CurrentConnectionConfig.DbType != DbType.MySqlConnector
                && this.Context.CurrentConnectionConfig.DbType != DbType.SqlServer;
        }
        private void AppendTracking(T item, IUpdateable<T> newUpdateable)
        {
            if (IsTrakingData() || IsTrakingDatas())
            {
                var trackingData = this.Context.TempItems.FirstOrDefault(it => it.Key.StartsWith("Tracking_" + item.GetHashCode()));
                var diffColumns = FastCopy.GetDiff(item, (T)trackingData.Value);
                if (diffColumns.Count > 0)
                {
                    var pks = EntityInfo.Columns
                        .Where(it => it.IsPrimarykey).Select(it => it.PropertyName).ToHashSet();
                    diffColumns = diffColumns.Where(it => !pks.Contains(it)).ToList();
                    if (diffColumns.Count > 0)
                    {
                        newUpdateable.UpdateColumns(diffColumns.ToArray());
                    }
                }
                else
                {
                    (newUpdateable as UpdateableProvider<T>).UpdateObjs = new T[] { null };
                    newUpdateable.UpdateBuilder.DbColumnInfoList = new List<DbColumnInfo>();
                }
            }
        }
        private void AppendSets()
        {
            if (SetColumnsIndex > 0)
            {
                var keys = UpdateBuilder.SetValues.Select(it => SqlBuilder.GetNoTranslationColumnName(it.Key)).ToList();
                var addKeys = keys.Where(k => !this.UpdateBuilder.DbColumnInfoList.Any(it => it.PropertyName.Equals(k, StringComparison.CurrentCultureIgnoreCase) || it.DbColumnName.Equals(k, StringComparison.CurrentCultureIgnoreCase))).ToList();
                var addItems = this.EntityInfo.Columns.Where(it => !GetPrimaryKeys().Any(p => p.Equals(it.PropertyName, StringComparison.CurrentCultureIgnoreCase) || p.Equals(it.DbColumnName, StringComparison.CurrentCultureIgnoreCase)) && addKeys.Any(k => it.PropertyName?.Equals(k, StringComparison.OrdinalIgnoreCase) == true || it.DbColumnName?.Equals(k, StringComparison.OrdinalIgnoreCase) == true)).ToList();
                this.UpdateBuilder.DbColumnInfoList.AddRange(addItems.Select(it => new DbColumnInfo() { PropertyName = it.PropertyName, DbColumnName = it.DbColumnName }));
            }
            SetColumnsIndex++;
        }
        private string _ExecuteCommand()
        {
            CheckWhere();
            PreToSql();
            AutoRemoveDataCache();
            Check.ExceptionEasy(this.UpdateParameterIsNull && this.UpdateBuilder.DbColumnInfoList.Count == this.UpdateObjs.Length && this.UpdateObjs.Length == this.UpdateBuilder.DbColumnInfoList.Count(it => it.IsPrimarykey), "The primary key cannot be updated", "主键不能更新，更新主键会对代码逻辑存在未知隐患,如果非要更新：建议你删除在插入或者新建一个没主键的类。");
            Check.Exception(UpdateBuilder.WhereValues.IsNullOrEmpty() && GetPrimaryKeys().IsNullOrEmpty(), "You cannot have no primary key and no conditions");
            string sql = UpdateBuilder.ToSqlString();
            ValidateVersion();
            RestoreMapping();
            Before(sql);
            return sql;
        }

        private void CheckWhere()
        {
            if (UpdateParameterIsNull && UpdateBuilder.WhereValues.IsNullOrEmpty())
            {
                Check.ExceptionEasy("Update requires conditions", "更新需要条件 Where");
            }
        }

        private void _WhereColumn(string columnName)
        {
            var columnInfos = columns.Where(it => it.DbColumnName.Equals(columnName, StringComparison.OrdinalIgnoreCase) || it.PropertyName.Equals(columnName, StringComparison.OrdinalIgnoreCase)).ToList();
            if (!this.UpdateBuilder.DbColumnInfoList.Any(y => y.DbColumnName == columnInfos.First().DbColumnName))
            {
                this.UpdateBuilder.DbColumnInfoList.AddRange(columnInfos);
            }
        }

        private void AutoRemoveDataCache()
        {
            var moreSetts = this.Context.CurrentConnectionConfig.MoreSettings;
            var extService = this.Context.CurrentConnectionConfig.ConfigureExternalServices;
            if (moreSetts?.IsAutoRemoveDataCache == true && extService?.DataInfoCacheService != null)
            {
                this.RemoveDataCache();
            }
        }
        internal void Init()
        {
            this.UpdateBuilder.TableName = EntityInfo.EntityName;
            if (IsMappingTable)
            {
                var mappingInfo = this.Context.MappingTables.SingleOrDefault(it => it.EntityName == EntityInfo.EntityName);
                if (mappingInfo != null)
                {
                    this.UpdateBuilder.TableName = mappingInfo.DbTableName;
                }
            }
            //Check.Exception(UpdateObjs == null || UpdateObjs.Any(), "UpdateObjs is null");
            int i = 0;
            if (this.EntityInfo.Columns.Any(it => it.IsPrimarykey))
            {
                this.UpdateBuilder.OldPrimaryKeys = this.EntityInfo.Columns.Where(it => it.IsPrimarykey).Select(it => it.DbColumnName).ToList();
            }
            foreach (var item in UpdateObjs)
            {
                List<DbColumnInfo> updateItem = new List<DbColumnInfo>();
                var isDic = item is Dictionary<string, object>;
                if (item is Dictionary<string, string>)
                {
                    Check.ExceptionEasy("To use Updateable dictionary, use string or object", "Updateable字典请使用string,object类型");
                }
                if (isDic)
                {
                    SetUpdateItemByDic(i, item, updateItem);
                }
                else
                {
                    DataAop(item);
                    SetUpdateItemByEntity(i, item, updateItem);
                    Tracking(item);
                }
                ++i;
            }
            this.columns = this.UpdateBuilder.DbColumnInfoList;

            var ignoreColumns = EntityInfo.Columns.Where(it => it.IsOnlyIgnoreUpdate).ToList();
            if (ignoreColumns != null && ignoreColumns.Count != 0)
            {
                this.IgnoreColumns(ignoreColumns.Select(it => it.PropertyName).ToArray());
            }
        }

        private void Tracking(T item)
        {
            if (IsTrakingData())
            {
                var trackingData = this.Context.TempItems.FirstOrDefault(it => it.Key.StartsWith("Tracking_" + item.GetHashCode()));
                if (trackingData.Key == null && trackingData.Value == null)
                {
                    return;
                }
                var diffColumns = FastCopy.GetDiff(item, (T)trackingData.Value);
                if (diffColumns.Count > 0)
                {
                    var pks = EntityInfo.Columns
                        .Where(it => it.IsPrimarykey).Select(it => it.PropertyName).ToHashSet();
                    diffColumns = diffColumns.Where(it => !pks.Contains(it)).ToList();
                    if (diffColumns.Count > 0)
                    {
                        this.UpdateColumns(diffColumns.ToArray());
                    }
                }
                else
                {
                    this.UpdateObjs = new T[] { null };
                    this.UpdateBuilder.DbColumnInfoList = new List<DbColumnInfo>();
                }
            }
        }

        private bool IsTrakingData()
        {
            return this.UpdateParameterIsNull == false
                                    && this.Context.TempItems?.Any(it => it.Key.StartsWith("Tracking_")) == true
                                    && this.UpdateObjs.Length == 1;
        }

        private bool IsTrakingDatas()
        {
            return this.UpdateParameterIsNull == false
                                    && this.Context.TempItems?.Any(it => it.Key.StartsWith("Tracking_")) == true
                                    && this.UpdateObjs.Length > 1;
        }
        private void DataAop(T item)
        {
            var dataEvent = this.Context.CurrentConnectionConfig.AopEvents?.DataExecuting;
            if (dataEvent != null && item != null)
            {
                foreach (var columnInfo in this.EntityInfo.Columns)
                {
                    if (columnInfo.ForOwnsOnePropertyInfo != null)
                    {
                        var data = columnInfo.ForOwnsOnePropertyInfo.GetValue(item, null);
                        if (data != null)
                        {
                            dataEvent(columnInfo.PropertyInfo.GetValue(data, null), new DataFilterModel() { OperationType = DataFilterType.UpdateByObject, EntityValue = item, EntityColumnInfo = columnInfo });
                        }
                    }
                    else if (columnInfo.PropertyInfo.Name == "Item" && columnInfo.IsIgnore)
                    {
                        //class index
                    }
                    else
                    {
                        dataEvent(columnInfo.PropertyInfo.GetValue(item, null), new DataFilterModel() { OperationType = DataFilterType.UpdateByObject, EntityValue = item, EntityColumnInfo = columnInfo });
                    }
                }
            }
        }

        private void DataChangesAop(T[] items)
        {
            if (typeof(T).FullName.StartsWith("System.Collections.Generic.Dictionary`"))
            {
                return;
            }
            var dataEvent = this.Context.CurrentConnectionConfig.AopEvents?.DataChangesExecuted;
            if (dataEvent != null)
            {
                foreach (var item in items)
                {
                    if (item != null)
                    {
                        foreach (var columnInfo in this.EntityInfo.Columns)
                        {
                            if (columnInfo.ForOwnsOnePropertyInfo != null)
                            {
                                var data = columnInfo.ForOwnsOnePropertyInfo.GetValue(item, null);
                                if (data != null)
                                {
                                    dataEvent(columnInfo.PropertyInfo.GetValue(data, null), new DataFilterModel() { OperationType = DataFilterType.UpdateByObject, EntityValue = item, EntityColumnInfo = columnInfo });
                                }
                            }
                            else if (columnInfo.PropertyInfo.Name == "Item" && columnInfo.IsIgnore)
                            {
                                //class index
                            }
                            else
                            {
                                dataEvent(columnInfo.PropertyInfo.GetValue(item, null), new DataFilterModel() { OperationType = DataFilterType.UpdateByObject, EntityValue = item, EntityColumnInfo = columnInfo });
                            }
                        }
                    }
                }
            }
        }

        private void CheckTranscodeing(bool checkIsJson = true)
        {
            if (this.EntityInfo.Columns.Any(it => it.IsTranscoding))
            {
                Check.Exception(true, ErrorMessage.GetThrowMessage("UpdateColumns no support IsTranscoding", "SetColumns方式更新不支持IsTranscoding，你可以使用db.Updateable(实体)的方式更新"));
            }
            //if (checkIsJson && this.EntityInfo.Columns.Any(it => it.IsJson))
            //{
            //    Check.Exception(true, ErrorMessage.GetThrowMessage("UpdateColumns no support IsJson", "SetColumns方式更新不支持IsJson，你可以使用db.Updateable(实体)的方式更新"));
            //}
            //if (this.EntityInfo.Columns.Any(it => it.IsArray))
            //{
            //    Check.Exception(true, ErrorMessage.GetThrowMessage("UpdateColumns no support IsArray", "SetColumns方式更新不支持IsArray，你可以使用db.Updateable(实体)的方式更新"));
            //}
        }
        private void SetUpdateItemByDic(int i, T item, List<DbColumnInfo> updateItem)
        {
            foreach (var column in (item as Dictionary<string, object>).OrderBy(it => it.Key))
            {
                var columnInfo = new DbColumnInfo()
                {
                    Value = column.Value,
                    DbColumnName = column.Key,
                    PropertyName = column.Key,
                    PropertyType = column.Value == null ? DBNull.Value.GetType() : UtilMethods.GetUnderType(column.Value.GetType()),
                    TableId = i
                };
                if (columnInfo.PropertyType.IsEnum())
                {
                    if (this.Context.CurrentConnectionConfig.MoreSettings?.TableEnumIsString == true)
                    {
                        columnInfo.PropertyType = UtilConstants.StringType;
                        columnInfo.Value = columnInfo.Value.ToString();
                    }
                    else
                    {
                        columnInfo.Value = Convert.ToInt64(columnInfo.Value);
                    }
                }
                updateItem.Add(columnInfo);
            }
            this.UpdateBuilder.DbColumnInfoList.AddRange(updateItem);
        }
        private void SetUpdateItemByEntity(int i, T item, List<DbColumnInfo> updateItem)
        {
            foreach (var column in EntityInfo.Columns)
            {
                if (column.IsIgnore) continue;
                Check.ExceptionEasy(item == null, "db.Updateable(data) data is required ", "db.Updateable(data) data不能是null");
                var columnInfo = new DbColumnInfo()
                {
                    Value = GetValue(item, column),
                    DbColumnName = GetDbColumnName(column.PropertyName),
                    PropertyName = column.PropertyName,
                    PropertyType = UtilMethods.GetUnderType(column.PropertyInfo),
                    SqlParameterDbType = column.SqlParameterDbType,
                    TableId = i,
                    UpdateSql = column.UpdateSql,
                    UpdateServerTime = column.UpdateServerTime,
                    IsPrimarykey = column.IsPrimarykey,
                    DataType = column.DataType
                };
                if (column.ForOwnsOnePropertyInfo != null)
                {
                    columnInfo.DbColumnName = column.DbColumnName;
                }
                if (columnInfo.PropertyType.IsEnum() && columnInfo.Value != null)
                {
                    if (this.Context.CurrentConnectionConfig.MoreSettings?.TableEnumIsString == true)
                    {
                        columnInfo.PropertyType = UtilConstants.StringType;
                        columnInfo.Value = columnInfo.Value.ToString();
                    }
                    else
                    {
                        columnInfo.Value = Convert.ToInt64(columnInfo.Value);
                    }
                }
                if (column.IsJson)
                {
                    columnInfo.IsJson = true;
                    var insertBuilder = InstanceFactory.GetInsertBuilder(this.Context?.CurrentConnectionConfig);
                    if (insertBuilder?.SerializeObjectFunc != null && columnInfo.Value != null)
                    {
                        columnInfo.Value = insertBuilder?.SerializeObjectFunc(columnInfo.Value);
                    }
                    else if (columnInfo.Value != null)
                        columnInfo.Value = this.Context.Utilities.SerializeObject(columnInfo.Value);
                }
                if (column.IsArray)
                {
                    columnInfo.IsArray = true;
                }
                var tranColumn = EntityInfo.Columns.FirstOrDefault(it => it.IsTranscoding && it.DbColumnName.Equals(column.DbColumnName, StringComparison.CurrentCultureIgnoreCase));
                if (tranColumn != null && columnInfo.Value.HasValue())
                {
                    columnInfo.Value = UtilMethods.EncodeBase64(columnInfo.Value.ToString());
                }
                updateItem.Add(columnInfo);
            }
            this.UpdateBuilder.DbColumnInfoList.AddRange(updateItem);
        }

        private static object GetValue(T item, EntityColumnInfo column)
        {
            if (column.ForOwnsOnePropertyInfo != null)
            {
                var owsPropertyValue = column.ForOwnsOnePropertyInfo.GetValue(item, null);
                return column.PropertyInfo.GetValue(owsPropertyValue, null);
            }
            else
            {
                return column.PropertyInfo.GetValue(item, null);
            }
        }
        private string GetSetSql(string value, Expression<Func<T, T>> columns)
        {
            if (value.Contains("= \"SYSDATE\""))
            {
                value = value.Replace("= \"SYSDATE\"", "= SYSDATE");
            }
            var shortName = (columns as LambdaExpression).Parameters.First().Name;
            var replaceKey = "," + this.SqlBuilder.GetTranslationColumnName(shortName) + ".";
            var newKey = "," + this.SqlBuilder.GetTranslationColumnName(this.EntityInfo.DbTableName) + ".";
            if (replaceKey != newKey)
            {
                value = value.Replace(replaceKey, ",");
            }
            return value;
        }

        private void PreToSql()
        {
            if (this.UpdateBuilder.UpdateColumns.HasValue())
            {
                var columns = this.UpdateBuilder.UpdateColumns;
                this.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.Where(it => GetPrimaryKeys().Contains(it.DbColumnName, StringComparer.OrdinalIgnoreCase)
                || columns.Contains(it.PropertyName, StringComparer.OrdinalIgnoreCase)
                || columns.Contains(it.DbColumnName, StringComparer.OrdinalIgnoreCase)).ToList();
            }
            UpdateBuilder.EntityInfo = this.EntityInfo;
            UpdateBuilder.PrimaryKeys = GetPrimaryKeys();
            if (this.IsWhereColumns)
            {
                foreach (var pkName in UpdateBuilder.PrimaryKeys)
                {
                    if (WhereColumnList?.Count > 0)
                    {
                        continue;
                    }
                    var isContains = this.UpdateBuilder.DbColumnInfoList.Select(it => it.DbColumnName).Contains(pkName, StringComparer.OrdinalIgnoreCase);
                    Check.Exception(isContains == false, "Use UpdateColumns().WhereColumn() ,UpdateColumns need {0}", pkName);
                }
            }
            #region IgnoreColumns
            if (this.Context.IgnoreColumns?.Any() == true)
            {
                var currentIgnoreColumns = this.Context.IgnoreColumns.Where(it => it.EntityName == this.EntityInfo.EntityName).ToList();
                this.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.Where(it =>
                {
                    return !currentIgnoreColumns.Any(i => it.PropertyName.Equals(i.PropertyName, StringComparison.CurrentCulture));
                }).ToList();
            }
            #endregion
            if (this.IsSingle)
            {
                var isDic = this.EntityInfo.DbTableName.StartsWith("Dictionary`");
                foreach (var item in this.UpdateBuilder.DbColumnInfoList)
                {
                    if (this.UpdateBuilder.Parameters == null) this.UpdateBuilder.Parameters = new List<SugarParameter>();
                    if (this.UpdateBuilder.SetValues.Any(it => this.SqlBuilder.GetNoTranslationColumnName(it.Key) == item.PropertyName))
                    {
                        continue;
                    }
                    if (item.SqlParameterDbType is Type)
                    {
                        continue;
                    }
                    var parameter = new SugarParameter(this.SqlBuilder.SqlParameterKeyWord + item.DbColumnName, item.Value, item.PropertyType);
                    if (item.IsJson)
                    {
                        parameter.IsJson = true;
                        SqlBuilder.ChangeJsonType(parameter);
                    }
                    if (item.IsArray)
                    {
                        parameter.IsArray = true;
                        if (parameter.Value == null || parameter.Value == DBNull.Value)
                        {
                            ArrayNull(item, parameter);
                        }
                    }
                    if (item.Value == null && isDic)
                    {
                        var type = this.SqlBuilder.GetNullType(this.UpdateBuilder.GetTableNameString, item.DbColumnName);
                        if (type != null)
                        {
                            parameter = new SugarParameter(this.SqlBuilder.SqlParameterKeyWord + item.DbColumnName, item.Value, type);
                        }
                    }
                    this.UpdateBuilder.Parameters.Add(parameter);
                }
            }

            #region Identities
            List<string> identities = GetIdentityKeys();
            if (identities != null && identities.Count != 0)
            {
                this.UpdateBuilder.DbColumnInfoList.ForEach(it =>
                {
                    var mappingInfo = identities.SingleOrDefault(i => it.DbColumnName.Equals(i, StringComparison.CurrentCultureIgnoreCase));
                    if (mappingInfo != null && mappingInfo.Length != 0)
                    {
                        it.IsIdentity = true;
                    }
                });
            }
            #endregion
            List<string> primaryKey = GetPrimaryKeys();
            if (primaryKey?.Count > 0)
            {
                this.UpdateBuilder.DbColumnInfoList.ForEach(it =>
                {
                    var mappingInfo = primaryKey.SingleOrDefault(i => it.DbColumnName.Equals(i, StringComparison.CurrentCultureIgnoreCase));
                    if (mappingInfo != null && mappingInfo.Length != 0)
                    {
                        it.IsPrimarykey = true;
                    }
                });
            }
            if (this.UpdateBuilder.Parameters.HasValue() && this.UpdateBuilder.SetValues.IsValuable())
            {
                this.UpdateBuilder.Parameters.RemoveAll(it => this.UpdateBuilder.SetValues.Any(v => (SqlBuilder.SqlParameterKeyWord + SqlBuilder.GetNoTranslationColumnName(v.Key)) == it.ParameterName));
            }
        }

        private static void ArrayNull(DbColumnInfo item, SugarParameter parameter)
        {
            if (item.PropertyType.IsIn(typeof(Guid[]), typeof(Guid?[])))
            {
                parameter.DbType = System.Data.DbType.Guid;
            }
            else if (item.PropertyType.IsIn(typeof(int[]), typeof(int?[])))
            {
                parameter.DbType = System.Data.DbType.Int32;
            }
            else if (item.PropertyType.IsIn(typeof(long[]), typeof(long?[])))
            {
                parameter.DbType = System.Data.DbType.Int64;
            }
            else if (item.PropertyType.IsIn(typeof(short[]), typeof(short?[])))
            {
                parameter.DbType = System.Data.DbType.Int16;
            }
        }
        private void OptRollBack(int updateRows, T updateData, object oldValue, string name)
        {
            if (updateRows == 0)
            {
                var verInfo = this.EntityInfo.Columns.FirstOrDefault(it => it.PropertyName == name);
                if (verInfo != null)
                {
                    verInfo.PropertyInfo.SetValue(updateData, oldValue);
                }
            }
        }
        private string GetDbColumnName(string propertyName)
        {
            if (!IsMappingColumns)
            {
                return propertyName;
            }
            if (this.Context.MappingColumns.Any(it => it.EntityName.Equals(EntityInfo.EntityName, StringComparison.CurrentCultureIgnoreCase)))
            {
                this.MappingColumnList = this.Context.MappingColumns.Where(it => it.EntityName.Equals(EntityInfo.EntityName, StringComparison.CurrentCultureIgnoreCase)).ToList();
            }
            if (MappingColumnList == null || MappingColumnList.Count == 0)
            {
                return propertyName;
            }
            else
            {
                var mappInfo = this.MappingColumnList.FirstOrDefault(it => it.PropertyName.Equals(propertyName, StringComparison.CurrentCultureIgnoreCase));
                return mappInfo == null ? propertyName : mappInfo.DbColumnName;
            }
        }
        protected List<string> GetPrimaryKeys()
        {
            if (this.WhereColumnList.HasValue())
            {
                return this.WhereColumnList;
            }
            else
            {
                return this.EntityInfo.Columns.Where(it => it.IsPrimarykey).Select(it => it.DbColumnName).ToList();
            }
        }
        protected virtual List<string> GetIdentityKeys()
        {

            return this.EntityInfo.Columns.Where(it => it.IsIdentity).Select(it => it.DbColumnName).ToList();
        }
        private void RestoreMapping()
        {
            if (IsAs)
            {
                this.Context.MappingTables = OldMappingTableList;
            }
        }


        private void ValidateVersion()
        {
            var versionColumn = this.EntityInfo.Columns.FirstOrDefault(it => it.IsEnableUpdateVersionValidation);
            var pks = this.UpdateBuilder.DbColumnInfoList.Where(it => it.IsPrimarykey).ToList();
            if (versionColumn != null && this.IsVersionValidation)
            {
                Check.Exception(pks.IsNullOrEmpty(), "UpdateVersionValidation the primary key is required.");
                List<IConditionalModel> conModels = new List<IConditionalModel>();
                foreach (var item in pks)
                {
                    conModels.Add(new ConditionalModel() { CSharpTypeName = item.PropertyType.Name, FieldName = item.DbColumnName, ConditionalType = ConditionalType.Equal, FieldValue = item.Value.ObjToString() });
                }
                var dbInfo = this.Context.Queryable<T>().Where(conModels).First();
                if (dbInfo != null)
                {
                    var currentVersion = this.EntityInfo.Type.GetProperty(versionColumn.PropertyName).GetValue(UpdateObjs.Last(), null);
                    var dbVersion = this.EntityInfo.Type.GetProperty(versionColumn.PropertyName).GetValue(dbInfo, null);
                    Check.Exception(currentVersion == null, "UpdateVersionValidation entity property {0} is not null", versionColumn.PropertyName);
                    Check.Exception(dbVersion == null, "UpdateVersionValidation database column {0} is not null", versionColumn.DbColumnName);
                    if (versionColumn.PropertyInfo.PropertyType.IsIn(UtilConstants.IntType, UtilConstants.LongType))
                    {
                        if (Convert.ToInt64(dbVersion) != Convert.ToInt64(currentVersion))
                        {
                            throw new VersionExceptions(string.Format("UpdateVersionValidation {0} Not the latest version ", versionColumn.PropertyName));
                        }
                    }
                    else if (versionColumn.PropertyInfo.PropertyType.IsIn(UtilConstants.DateType))
                    {
                        if (dbVersion.ObjToDate() != currentVersion.ObjToDate())
                        {
                            throw new VersionExceptions(string.Format("UpdateVersionValidation {0} Not the latest version ", versionColumn.PropertyName));
                        }
                    }
                    else if (versionColumn.PropertyInfo.PropertyType.IsIn(UtilConstants.ByteArrayType))
                    {
                        if (UtilMethods.GetLong((byte[])dbVersion) != UtilMethods.GetLong((byte[])currentVersion))
                        {
                            throw new VersionExceptions(string.Format("UpdateVersionValidation {0} Not the latest version ", versionColumn.PropertyName));
                        }
                    }
                    else
                    {
                        Check.ThrowNotSupportedException(string.Format("UpdateVersionValidation Not Supported Type [ {0} ] , {1}", versionColumn.PropertyInfo.PropertyType, versionColumn.PropertyName));
                    }
                }
            }
        }
        private void After(string sql)
        {
            if (this.IsEnableDiffLogEvent && !string.IsNullOrEmpty(sql))
            {
                var isDisableMasterSlaveSeparation = this.Ado.IsDisableMasterSlaveSeparation;
                this.Ado.IsDisableMasterSlaveSeparation = true;
                var parameters = UpdateBuilder.Parameters;
                if (parameters == null)
                    parameters = new List<SugarParameter>();
                diffModel.AfterData = GetDiffTable(sql, parameters);
                diffModel.Time = this.Context.Ado.SqlExecutionTime;
                if (this.Context.CurrentConnectionConfig.AopEvents.OnDiffLogEvent != null)
                    this.Context.CurrentConnectionConfig.AopEvents.OnDiffLogEvent(diffModel);
                this.Ado.IsDisableMasterSlaveSeparation = isDisableMasterSlaveSeparation;
            }
            if (this.RemoveCacheFunc != null)
            {
                this.RemoveCacheFunc();
            }

            DataChangesAop(this.UpdateObjs);
        }
        private string _ExecuteCommandWithOptLock(T updateData, ref object oldVerValue)
        {
            Check.ExceptionEasy(UpdateParameterIsNull == true, "Optimistic lock can only be an entity update method", "乐观锁只能是实体更新方式");
            var verColumn = this.EntityInfo.Columns.FirstOrDefault(it => it.IsEnableUpdateVersionValidation);
            Check.ExceptionEasy(verColumn == null, $" {this.EntityInfo.EntityName} need  IsEnableUpdateVersionValidation=true ", $"实体{this.EntityInfo.EntityName}没有找到版本标识特性 IsEnableUpdateVersionValidation");
            Check.ExceptionEasy(UpdateObjs.Length > 1, $"Optimistic lock can only handle a single update ", $"乐观锁只能处理单条更新");
            Check.ExceptionEasy(!verColumn.UnderType.IsIn(UtilConstants.StringType, UtilConstants.LongType, UtilConstants.GuidType, UtilConstants.DateType), $"Optimistic locks can only be guid, long, and string types", $"乐观锁只能是Guid、Long和字符串类型");
            var oldValue = verColumn.PropertyInfo.GetValue(updateData);
            oldVerValue = oldValue;
            var newValue = UtilMethods.GetRandomByType(verColumn.UnderType);
            verColumn.PropertyInfo.SetValue(updateData, newValue);
            var data = this.UpdateBuilder.DbColumnInfoList.FirstOrDefault(it =>
            it.PropertyName.EqualCase(verColumn.PropertyName));
            if (data == null)
            {
                data = new DbColumnInfo() { DbColumnName = verColumn.DbColumnName, PropertyName = verColumn.PropertyName, Value = newValue };
                this.UpdateBuilder.DbColumnInfoList.Add(data);
            }
            data.Value = newValue;
            var pks = GetPrimaryKeys();
            Check.ExceptionEasy(pks.Count == 0, "need primary key or WhereColumn", "需要主键或者WhereColumn");
            this.Where(verColumn.DbColumnName, "=", oldValue);
            foreach (var p in pks)
            {
                var pkColumn = this.EntityInfo.Columns.FirstOrDefault(
                    it => it.DbColumnName.EqualCase(p) || it.PropertyName.EqualCase(p));
                this.Where(pkColumn.DbColumnName, "=", pkColumn.PropertyInfo.GetValue(updateData));
            }
            return verColumn.PropertyName;
        }
        private void Before(string sql)
        {
            if (this.IsEnableDiffLogEvent && !string.IsNullOrEmpty(sql))
            {
                var isDisableMasterSlaveSeparation = this.Ado.IsDisableMasterSlaveSeparation;
                this.Ado.IsDisableMasterSlaveSeparation = true;
                var parameters = UpdateBuilder.Parameters;
                if (parameters == null)
                    parameters = new List<SugarParameter>();
                diffModel.BeforeData = GetDiffTable(sql, parameters);
                diffModel.Sql = sql;
                diffModel.Parameters = parameters.ToArray();
                this.Ado.IsDisableMasterSlaveSeparation = isDisableMasterSlaveSeparation;
            }
        }
        private bool IsPrimaryKey(DbColumnInfo it)
        {
            var result = GetPrimaryKeys().Any(p => p.Equals(it.DbColumnName, StringComparison.CurrentCultureIgnoreCase) || p.Equals(it.PropertyName, StringComparison.CurrentCultureIgnoreCase));
            return result;
        }

        internal List<DiffLogTableInfo> GetTableDiff(DataTable dt)
        {
            List<DiffLogTableInfo> result = new List<DiffLogTableInfo>();
            if (dt.Rows?.Count > 0)
            {
                foreach (DataRow row in dt.Rows)
                {
                    DiffLogTableInfo item = new DiffLogTableInfo();
                    item.TableDescription = this.EntityInfo.TableDescription;
                    item.TableName = this.EntityInfo.DbTableName;
                    item.Columns = new List<DiffLogColumnInfo>();
                    foreach (DataColumn col in dt.Columns)
                    {
                        try
                        {
                            var sugarColumn = this.EntityInfo.Columns.Where(it => it.DbColumnName != null).First(it =>
                                              it.DbColumnName.Equals(col.ColumnName, StringComparison.CurrentCultureIgnoreCase));
                            DiffLogColumnInfo addItem = new DiffLogColumnInfo();
                            addItem.Value = row[col.ColumnName];
                            addItem.ColumnName = col.ColumnName;
                            addItem.IsPrimaryKey = sugarColumn.IsPrimarykey;
                            addItem.ColumnDescription = sugarColumn.ColumnDescription;
                            item.Columns.Add(addItem);
                        }
                        catch (Exception ex)
                        {
                            Check.ExceptionEasy(col.ColumnName + " No corresponding entity attribute found in difference log ." + ex.Message, col.ColumnName + "在差异日志中可能没有找到相应的实体属性,详细:" + ex.Message);
                        }
                    }
                    result.Add(item);
                }
            }
            return result;
        }
        private List<DiffLogTableInfo> GetDiffTable(string sql, List<SugarParameter> parameters)
        {
            List<DiffLogTableInfo> result = new List<DiffLogTableInfo>();
            DataTable dt = null;
            if (this.UpdateParameterIsNull)
            {
                var whereSql = Regex.Replace(sql, ".* WHERE ", "", RegexOptions.Singleline);
                if (IsExists(sql))
                {
                    if (this.UpdateBuilder.SetValues != null)
                    {
                        foreach (var item in this.UpdateBuilder.SetValues)
                        {
                            if (item.Value?.Contains("SELECT") == true)
                            {
                                sql = sql.Replace(item.Value, null);
                            }
                        }
                    }
                    whereSql = UtilMethods.RemoveBeforeFirstWhere(sql);
                }
                dt = this.Context.Queryable<T>().AS(this.UpdateBuilder.TableName).Filter(null, true).Where(whereSql).AddParameters(parameters).ToDataTable();
            }
            else
            {
                if (this.UpdateObjs.ToList().Count == 0)
                {
                    dt = new DataTable();
                }
                else if (this.WhereColumnList?.Count > 0)
                {
                    dt = this.Context.Queryable<T>().Filter(null, true).WhereClassByWhereColumns(this.UpdateObjs.ToList(), this.WhereColumnList.ToArray()).ToDataTable();
                }
                else if (this.UpdateBuilder.TableName.HasValue())
                {
                    dt = this.Context.Queryable<T>().AS(this.UpdateBuilder.TableName).Filter(null, true).WhereClassByPrimaryKey(this.UpdateObjs.ToList()).ToDataTable();
                }
                else
                {
                    dt = this.Context.Queryable<T>().Filter(null, true).WhereClassByPrimaryKey(this.UpdateObjs.ToList()).ToDataTable();
                }
            }
            if (dt.Rows?.Count > 0)
            {
                foreach (DataRow row in dt.Rows)
                {
                    DiffLogTableInfo item = new DiffLogTableInfo();
                    item.TableDescription = this.EntityInfo.TableDescription;
                    item.TableName = this.EntityInfo.DbTableName;
                    item.Columns = new List<DiffLogColumnInfo>();
                    foreach (DataColumn col in dt.Columns)
                    {
                        try
                        {
                            var sugarColumn = this.EntityInfo.Columns.Where(it => it.DbColumnName != null).First(it =>
                                              it.DbColumnName.Equals(col.ColumnName, StringComparison.CurrentCultureIgnoreCase));
                            DiffLogColumnInfo addItem = new DiffLogColumnInfo();
                            addItem.Value = row[col.ColumnName];
                            addItem.ColumnName = col.ColumnName;
                            addItem.IsPrimaryKey = sugarColumn.IsPrimarykey;
                            addItem.ColumnDescription = sugarColumn.ColumnDescription;
                            item.Columns.Add(addItem);
                        }
                        catch (Exception ex)
                        {
                            Check.ExceptionEasy(col.ColumnName + " No corresponding entity attribute found in difference log ." + ex.Message, col.ColumnName + "在差异日志中可能没有找到相应的实体属性,详细:" + ex.Message);
                        }
                    }
                    result.Add(item);
                }
            }
            return result;
        }

        private static bool IsExists(string sql)
        {
            return UtilMethods.CountSubstringOccurrences(sql, "WHERE") > 1;
        }
        protected bool IsCorrectErrorSqlParameterName()
        {
            return this.Context?.CurrentConnectionConfig?.MoreSettings?.IsCorrectErrorSqlParameterName == true;
        }

        protected void ThrowUpdateByExpression()
        {
            Check.Exception(UpdateParameterIsNull == true, ErrorMessage.GetThrowMessage(" no support UpdateColumns and WhereColumns", "根据表达式更新 db.Updateable<T>() 禁止使用 UpdateColumns和WhereColumns,你可以使用 SetColumns Where 等。更新分为2种方式 1.根据表达式更新 2.根据实体或者集合更新， 具体用法请查看文档 "));
        }
        protected void ThrowUpdateByObject()
        {
            Check.Exception(UpdateParameterIsNull == false, ErrorMessage.GetThrowMessage(" no support SetColumns and Where", "根据对像更新 db.Updateabe(对象) 禁止使用 SetColumns和Where ,你可以使用WhereColumns 和  UpdateColumns。 更新分为2种方式 1.根据表达式更新 2.根据实体或者集合更新 ， 具体用法请查看文档 "));
        }
        protected void ThrowUpdateByExpressionByMesage(string message)
        {
            Check.Exception(UpdateParameterIsNull == true, ErrorMessage.GetThrowMessage(" no support " + message, "根据表达式更新 db.Updateable<T>()禁止使用 " + message + "。 更新分为2种方式 1.根据表达式更新 2.根据实体或者集合更新 ， 具体用法请查看文档 "));
        }
        private void ThrowUpdateByObjectByMesage(string message)
        {
            Check.Exception(UpdateParameterIsNull == false, ErrorMessage.GetThrowMessage(" no support " + message, "根据对象更新 db.Updateable(对象)禁止使用 " + message + "。 更新分为2种方式 1.根据表达式更新 2.根据实体或者集合更新 ， 具体用法请查看文档 "));
        }
    }
}
