using QYZH.InteractiveMagazine.Models.Entity;
using Microsoft.AspNetCore.Http;
using QYZH.InteractiveMagazine.Infrastructure.Context;
using SqlSugar;
using System.Linq.Expressions;
using System.Reflection;
using System.Security.Claims;
using Yitter.IdGenerator;


namespace QYZH.InteractiveMagazine.Repository.Core
{
    /// <summary>
    /// sql sugar 管理器
    /// </summary>
    public static class SqlSugarExtension
    {
        /// <summary>
        /// 生成类文件
        /// </summary>
        /// <param name="db"></param>
        /// <param name="directoryPath">生成路径</param>
        /// <param name="nameSpace">名称空间</param>
        public static ISqlSugarClient CreateClassFile(this ISqlSugarClient db, string directoryPath, string nameSpace = "Models")
        {
            foreach (var item in db.DbMaintenance.GetTableInfoList())
            {
                string[] entityNameArray = item.Name.Split('_', StringSplitOptions.RemoveEmptyEntries).ToArray();
                string entityName = string.Join("", entityNameArray.Select(c => c.ToCamelCase()));
                db.MappingTables.Add(entityName, item.Name);
                foreach (var col in db.DbMaintenance.GetColumnInfosByTableName(item.Name))
                {
                    var columnNames = col.DbColumnName.ToLower().Split('_', StringSplitOptions.RemoveEmptyEntries).ToArray();
                    string columnName = string.Join("", columnNames.Select(c => c.ToCamelCase()));
                    db.MappingColumns.Add(columnName, col.DbColumnName, entityName);
                }
            }
            db.DbFirst.IsCreateAttribute().CreateClassFile(directoryPath, nameSpace);
            return db;
        }


        /// <summary>
        /// 添加全局过滤器
        /// IsDeleted
        /// </summary>
        /// <param name="db">数据库Client</param>
        /// <param name="assemblyName">程序集名称</param>
        /// <returns></returns>
        public static ISqlSugarClient SetQueryFilter(this ISqlSugarClient db, string assemblyName)
        {
            var assembly = Assembly.Load(assemblyName);

            var modelBaseType = typeof(SqlSugarBaseEntity);
            var repoBaseType = typeof(SqlSugarBaseEntity);

            var types = assembly.ExportedTypes
                .Where(t => (modelBaseType.IsAssignableFrom(t) || repoBaseType.IsAssignableFrom(t))
                         && t != modelBaseType && t != repoBaseType
                         && !t.IsGenericTypeDefinition)
                .ToList();

            foreach (var entityType in types)
            {
                var property = entityType.GetProperty("IsDeleted", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.DeclaredOnly);
                if (property == null)
                {
                    var baseType = entityType.BaseType;
                    while (baseType != null && baseType != typeof(object))
                    {
                        property = baseType.GetProperty("IsDeleted", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.DeclaredOnly);
                        if (property != null) break;
                        baseType = baseType.BaseType;
                    }
                }

                if (property == null) continue;

                var propertyType = property.PropertyType;

                Expression filterExpression;
                var param = Expression.Parameter(entityType, "it");
                var propertyAccess = Expression.Property(param, property);

                if (propertyType == typeof(bool))
                {
                    filterExpression = Expression.Equal(propertyAccess, Expression.Constant(false, typeof(bool)));
                }
                else if (propertyType == typeof(bool?))
                {
                    filterExpression = Expression.Equal(propertyAccess, Expression.Constant(false, typeof(bool?)));
                }
                else if (propertyType == typeof(byte))
                {
                    filterExpression = Expression.Equal(propertyAccess, Expression.Constant((byte)0, typeof(byte)));
                }
                else if (propertyType == typeof(byte?))
                {
                    filterExpression = Expression.Equal(propertyAccess, Expression.Constant((byte)0, typeof(byte?)));
                }
                else
                {
                    continue;
                }

                var lambda = Expression.Lambda(filterExpression, param);
                db.QueryFilter.AddTableFilter(entityType, lambda);
            }

            return db;
        }

        /// <summary>
        /// 添加创建人 创建时间 更新人 更新时间 默认值
        /// </summary>
        /// <param name="db"></param>
        public static ISqlSugarClient SetDefaultValue(this ISqlSugarClient db)
        {
            #region Id 创建人 创建时间 更新人 更新时间 默认值
            db.Aop.DataExecuting = (oldValue, entityInfo) =>
            {
                var entityValue = entityInfo.EntityColumnInfo.PropertyInfo.GetValue(entityInfo.EntityValue)?.ToString();
                var currentUserName = GetCurrentUserName();
                /*** inset生效 ***/
                if (entityInfo.OperationType == DataFilterType.InsertByObject)
                {
                    if (entityInfo.PropertyName == "CreatedAt" && (string.IsNullOrWhiteSpace(entityValue) || entityValue == DateTime.MinValue.ToString()))
                        entityInfo.SetValue(DateTime.Now);//修改CreateTime字段
                    else if (entityInfo.PropertyName == "CreatedBy" && ShouldSetOperatorName(entityValue, currentUserName))
                        entityInfo.SetValue(currentUserName);//修改创建人字段
                    else if (entityInfo.PropertyName == "UpdatedAt" && (string.IsNullOrWhiteSpace(entityValue) || entityValue == DateTime.MinValue.ToString()))
                        entityInfo.SetValue(DateTime.Now);//修改UpdatedTime字段
                    else if (entityInfo.PropertyName == "UpdatedBy" && ShouldSetOperatorName(entityValue, currentUserName))
                        entityInfo.SetValue(currentUserName);//修改更新人字段
                    else if (entityInfo.PropertyName == "IsDeleted" && string.IsNullOrWhiteSpace(entityValue))
                        entityInfo.SetValue("0");//修改CreateTime字段
                }

                /*** update生效 ***/
                if (entityInfo.OperationType == DataFilterType.UpdateByObject)
                {
                    if (entityInfo.PropertyName == "UpdatedAt" && (string.IsNullOrWhiteSpace(entityValue) || entityValue == DateTime.MinValue.ToString()))
                        entityInfo.SetValue(DateTime.Now);//修改UpdatedTime字段
                    else if (entityInfo.PropertyName == "UpdatedBy" && ShouldSetOperatorName(entityValue, currentUserName))
                        entityInfo.SetValue(currentUserName);//修改更新人字段
                }
            };
            return db;
            #endregion
        }


        private static string GetCurrentUserName()
        {
            try
            {
                var httpContextAccessor = ServiceContext.ServiceProvider?.GetService(typeof(IHttpContextAccessor)) as IHttpContextAccessor;
                var user = httpContextAccessor?.HttpContext?.User;
                var userName = user?.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value
                    ?? user?.Identity?.Name;

                return string.IsNullOrWhiteSpace(userName) ? "System" : userName;
            }
            catch
            {
                return "System";
            }
        }

        private static bool ShouldSetOperatorName(string? entityValue, string currentUserName)
        {
            if (string.IsNullOrWhiteSpace(entityValue) || entityValue == "0")
            {
                return true;
            }

            if (currentUserName != "System" && long.TryParse(entityValue, out _))
            {
                return true;
            }

            return false;
        }


        /// <summary>
        /// 把一个字符串转成驼峰规则的字符串
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string ToCamelCase(this string str)
        {
            if (!string.IsNullOrEmpty(str) && str.Length > 1)
            {
                return char.ToUpperInvariant(str[0]) + str.Substring(1);
            }
            return str;
        }
    }
}
