using System;
using System.Configuration;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace DapperORM.ORMExtention
{
    /// <summary>
    /// SQL表达式访问器
    /// </summary>
    public class SqlExpressionVisitor
    {
        /// <summary>
        /// 表达式中的常量参数列表
        /// </summary>
        public Dictionary<string, object> Parameters => parameters;

        /// <summary>
        /// 表达式中的属性参数列表
        /// </summary>
        public Dictionary<string, PropertyInfo> Properties => properties;

        /// <summary>
        /// 参数
        /// </summary>
        private readonly Dictionary<string, object> parameters;

        /// <summary>
        /// 对象属性
        /// </summary>
        private readonly Dictionary<string, PropertyInfo> properties;

        /// <summary>
        /// 构造函数
        /// </summary>
        public SqlExpressionVisitor()
        {
            parameters = new Dictionary<string, object>();
            properties = new Dictionary<string, PropertyInfo>();
        }

        /// <summary>
        /// 递归解析方法入口
        /// </summary>
        /// <param name="exp"></param>
        /// <param name="flag"></param>
        public virtual void Visit(Expression exp, SqlVistorFlag flag)
        {
            switch (exp.NodeType)
            {
                case ExpressionType.Lambda:
                    VisitLambda(exp as LambdaExpression, flag);
                    break;
                case ExpressionType.MemberAccess:
                    VisitMember(exp as MemberExpression, flag);
                    break;
                case ExpressionType.Add:
                case ExpressionType.AddChecked:
                case ExpressionType.Subtract:
                case ExpressionType.SubtractChecked:
                case ExpressionType.Multiply:
                case ExpressionType.MultiplyChecked:
                case ExpressionType.Divide:
                case ExpressionType.Modulo:
                case ExpressionType.And:
                case ExpressionType.AndAlso:
                case ExpressionType.Or:
                case ExpressionType.OrElse:
                case ExpressionType.LessThan:
                case ExpressionType.LessThanOrEqual:
                case ExpressionType.GreaterThan:
                case ExpressionType.GreaterThanOrEqual:
                case ExpressionType.Equal:
                case ExpressionType.NotEqual:
                case ExpressionType.Coalesce:
                case ExpressionType.ArrayIndex:
                case ExpressionType.RightShift:
                case ExpressionType.LeftShift:
                case ExpressionType.ExclusiveOr:
                    VisitBinary(exp as BinaryExpression, flag);
                    break;
                case ExpressionType.Negate:
                case ExpressionType.NegateChecked:
                case ExpressionType.Not:
                case ExpressionType.Convert:
                case ExpressionType.ConvertChecked:
                case ExpressionType.ArrayLength:
                case ExpressionType.Quote:
                case ExpressionType.TypeAs:
                    VisitUnary(exp as UnaryExpression, flag);
                    break;
                case ExpressionType.Constant:
                    VisitConstant(exp as ConstantExpression, flag);
                    break;
                case ExpressionType.Parameter:
                    VisitParameter(exp as ParameterExpression, flag);
                    break;
                case ExpressionType.Call:
                    VisitMethodCall(exp as MethodCallExpression, flag);
                    break;
                case ExpressionType.New:
                    VisitNew(exp as NewExpression, flag);
                    break;
                case ExpressionType.NewArrayInit:
                case ExpressionType.NewArrayBounds:
                    VisitNewArray(exp as NewArrayExpression, flag);
                    break;
                case ExpressionType.MemberInit:
                    VisitMemberInit(exp as MemberInitExpression, flag);
                    break;
                case ExpressionType.Conditional:
                    VisitConditional(exp as ConditionalExpression);
                    break;
            }
        }

        /// <summary>
        /// 递归解析时，保留上层的IsRight状态
        /// </summary>
        /// <param name="exp"></param>
        /// <param name="flag"></param>
        protected virtual void VisitRight(Expression exp, SqlVistorFlag flag)
        {
            var isRight = flag.IsRight;
            flag.IsRight = true;
            Visit(exp, flag);
            flag.IsRight = isRight; //  回归
        }

        #region 不同表达式解析方法

        #region 解析方法表达式
        /// <summary>
        /// 解析方法表达式
        /// </summary>
        /// <param name="exp">方法表达式</param>
        /// <param name="flag">标识</param>
        protected virtual void VisitMethodCall(MethodCallExpression exp, SqlVistorFlag flag)
        {
            var methodName = exp.Method.Name;
            switch (methodName) //这里其实还可以改成反射调用，不用写switch
            {
                case "Contains":
                    MethodCallLike(exp, flag);
                    break;


                    //  todo 补充其他方法
            }
        }

        /// <summary>
        /// LIKE方法表达式
        /// </summary>
        /// <param name="exp">方法表达式</param>
        /// <param name="flag">标识</param>
        private void MethodCallLike(MethodCallExpression exp, SqlVistorFlag flag)
        {
            Visit(exp.Object, flag);
            flag.Append(GetUnaryOperater(flag.UnaryType));
            flag.Append(" LIKE CONCAT('%',");
            VisitRight(exp.Arguments[0], flag);
            flag.Append(",'%')");
        }

        #endregion
        /// <summary>
        /// 解析二进制表达式
        /// </summary>
        /// <param name="exp">二进制表达式</param>
        /// <param name="flag">标识</param>
        protected virtual void VisitBinary(BinaryExpression exp, SqlVistorFlag flag)
        {
            var operand = GetBinaryOperater(exp.NodeType);
            if (exp.NodeType == ExpressionType.AndAlso ||
                exp.NodeType == ExpressionType.OrElse)
            {
                flag.Append("(");
                Visit(exp.Left, flag);
                flag.Append(") ").Append(operand).Append(" (");
                Visit(exp.Right, flag);
                flag.Append(")");
            }
            else
            {
                Visit(exp.Left, flag);
                flag.Append(operand);
                VisitRight(exp.Right, flag);
            }
        }

        /// <summary>
        /// 解析实例化数组表达式
        /// </summary>
        /// <param name="na">实例化数组表达式</param>
        /// <param name="flag">标识</param>
        protected void VisitNewArray(NewArrayExpression na, SqlVistorFlag flag)
        {
            var original = na.Expressions;
            for (int i = 0, n = original.Count; i < n; i++)
            {
                var e = original[i];
                if (e.NodeType == ExpressionType.NewArrayInit ||
                    e.NodeType == ExpressionType.NewArrayBounds)
                {
                    var newArrayExpression = e as NewArrayExpression;
                    if (newArrayExpression != null)
                        VisitNewArray(newArrayExpression, flag);
                }
                else
                {
                    Visit(e, flag);
                }
            }
        }

        /// <summary>
        /// 解析常量表达式
        /// </summary>
        /// <param name="c">常量表达式</param>
        /// <param name="flag">标识</param>
        protected virtual void VisitConstant(ConstantExpression c, SqlVistorFlag flag)
        {
            var value = c.Value ?? "null";
            if (flag.IsRight)
            {
                var paraName = flag.GetCustomParaName();
                flag.Append(paraName);

                AddParameter(paraName, c.Type == typeof(bool) ? ((bool)value ? 1 : 0) : value);
            }
            else
            {
                flag.Append(value.ToString());
            }
        }

        /// <summary>
        /// 解析一元运算表达式
        /// </summary>
        /// <param name="u">一元运算表达式</param>
        /// <param name="flag">标识</param>
        protected virtual void VisitUnary(UnaryExpression u, SqlVistorFlag flag)
        {
            var nodeType = flag.UnaryType;
            flag.UnaryType = u.NodeType;
            Visit(u.Operand, flag);
            flag.UnaryType = nodeType;

        }

        /// <summary>
        /// 解析Lambda表达式
        /// </summary>
        /// <param name="lambda">Lambda表达式</param>
        /// <param name="flag">标识</param>
        protected virtual void VisitLambda(LambdaExpression lambda, SqlVistorFlag flag)
        {
            Visit(lambda.Body, flag);
        }

        /// <summary>
        /// 解析实例化表达式
        /// </summary>
        /// <param name="nex">实例化表达式</param>
        /// <param name="flag">标识</param>
        protected virtual void VisitNew(NewExpression nex, SqlVistorFlag flag)
        {
            for (var i = 0; i < nex.Members.Count; i++)
            {
                var arg = nex.Arguments[i];
                var member = nex.Members[i];
                if (flag.VistorType != SqlVistorType.Select)
                {
                    flag.Append(flag.GetColName(member.Name)).Append("=");
                    VisitRight(arg, flag);

                    if (i != nex.Members.Count - 1)
                        flag.Append(",");
                }
                else
                    flag.Append(flag.GetColName(member.Name), true);
            }
        }

        /// <summary>
        /// 解析对象成员表达式
        /// </summary>
        /// <param name="exp">对象成员表达式</param>
        /// <param name="flag">标识</param>
        protected virtual void VisitMember(MemberExpression exp, SqlVistorFlag flag)
        {
            if (exp.Expression != null
                && exp.Expression.NodeType == ExpressionType.Parameter)
            {
                if (flag.IsRight)
                {
                    var proParaName = flag.GetParaName(exp.Member.Name);

                    flag.Append(proParaName);

                    AddMemberProperty(proParaName, exp.Member.DeclaringType.GetProperty(exp.Member.Name));
                }
                else
                {
                    if (exp.Member.DeclaringType.GetTypeInfo().IsGenericType
                        && exp.Member.DeclaringType.GetGenericTypeDefinition() == typeof(Nullable<>))
                    {
                        if (exp.Member.Name == "Value")
                            Visit(exp.Expression, flag);

                        if (exp.Member.Name != "HasValue") return;

                        var doesNotEqualNull = Expression.MakeBinary(ExpressionType.NotEqual, exp.Expression,
                            Expression.Constant(null));
                        Visit(doesNotEqualNull, flag);
                    }
                    else
                        flag.Append(flag.GetColName(exp.Member.Name));
                }
            }
            else if (exp.Expression != null && flag.IsRight)
            {
                var value = Expression.Lambda(exp).Compile().DynamicInvoke();
                Visit(Expression.Constant(value), flag);
            }
        }

        /// <summary>
        /// 解析条件表达式
        /// </summary>
        /// <param name="conditionalExpression">条件表达式</param>
        protected virtual void VisitConditional(ConditionalExpression conditionalExpression)
        {
            // todo
        }

        /// <summary>
        /// 解析成员初始化表达式
        /// </summary>
        /// <param name="memberInitExpression">成员初始化表达式</param>
        /// <param name="flag">标识</param>
        protected virtual void VisitMemberInit(MemberInitExpression memberInitExpression, SqlVistorFlag flag)
        {
            //  todo
        }

        /// <summary>
        /// 解析参数表达式
        /// </summary>
        /// <param name="parameterExpression">参数表达式</param>
        /// <param name="flag">标识</param>
        protected virtual void VisitParameter(ParameterExpression parameterExpression, SqlVistorFlag flag)
        {
            // todo
        }

        #endregion

        #region 辅助方法

        /// <summary>
        /// 添加成员属性信息
        /// </summary>
        /// <param name="name"></param>
        /// <param name="pro"></param>
        private void AddMemberProperty(string name, PropertyInfo pro)
        {
            if (!properties.ContainsKey(name))
            {
                properties.Add(name, pro);
            }
        }

        /// <summary>
        /// 添加值对应的参数
        /// </summary>
        /// <param name="paraName"></param>
        /// <param name="value"></param>
        private void AddParameter(string paraName, object value)
        {
            parameters.Add(paraName, value);
        }


        protected virtual string GetBinaryOperater(ExpressionType e)
        {
            switch (e)
            {
                case ExpressionType.Add:
                    return "+";
                case ExpressionType.And:
                    return "&";
                case ExpressionType.AndAlso:
                    return "AND";
                case ExpressionType.Equal:
                    return "=";
                case ExpressionType.NotEqual:
                    return "<>";
                case ExpressionType.GreaterThan:
                    return ">";
                case ExpressionType.GreaterThanOrEqual:
                    return ">=";
                case ExpressionType.LessThan:
                    return "<";
                case ExpressionType.LessThanOrEqual:
                    return "<=";
                case ExpressionType.Or:
                    return "|";
                case ExpressionType.OrElse:
                    return "OR";
                case ExpressionType.Subtract:
                    return "-";
                case ExpressionType.Multiply:
                    return "*";
                case ExpressionType.Divide:
                    return "/";
                case ExpressionType.Modulo:
                    return "MOD";
                case ExpressionType.Coalesce:
                    return "COALESCE";
                default:
                    return string.Empty;
            }
        }


        protected virtual string GetUnaryOperater(ExpressionType e)
        {
            switch (e)
            {
                case ExpressionType.Not:
                    return "NOT";
                default:
                    return string.Empty;
            }
        }

        #endregion
    }

    /// <summary>
    /// 表达式解析标识内容体
    /// </summary>
    public class SqlVistorFlag
    {
        /// <summary>
        /// SQL
        /// </summary>
        private readonly StringBuilder _SqlBuilder;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="vistorType">SQL访问类型</param>
        public SqlVistorFlag(SqlVistorType vistorType)
        {
            _SqlBuilder = new StringBuilder();
            UnaryType = (ExpressionType)(-1);
            VistorType = vistorType;
        }

        /// <summary>
        /// 是否是表达式右侧项，决定右侧树是否做参数化处理
        /// </summary>
        public bool IsRight { get; set; }

        /// <summary>
        /// 主要是MethodCall方法中解析时需要知晓上层一元运算符
        /// </summary>
        public ExpressionType UnaryType { get; set; }

        /// <summary>
        /// 参数辨识符号
        /// </summary>
        public string ParaPreToken { get; set; } = "@";

        /// <summary>
        /// 对应的SQL语句
        /// </summary>
        public string Sql => _SqlBuilder.ToString();

        /// <summary>
        /// 解析语段类型
        /// </summary>
        public SqlVistorType VistorType { get; set; }

        #region 语句拼接

        /// <summary>
        /// 是否拼接的第一个
        /// </summary>
        private bool _isStarted;

        /// <summary>
        /// 内部补充转义字符前缀，防止和数据库关键字名称冲突，如 `name`
        /// </summary>
        private static readonly string _ColPrefix = ConfigurationManager.AppSettings["ColPrefix"] ?? string.Empty;

        /// <summary>
        /// 内部补充转义字符后缀，防止和数据库关键字名称冲突，如 `name`
        /// </summary>
        private static readonly string _ColPostfix = ConfigurationManager.AppSettings["ColPostfix"] ?? string.Empty;

        /// <summary>
        /// 往语句中追加内容
        /// </summary>
        /// <param name="content"></param>
        /// <param name="needCheckSep"></param>
        /// <returns></returns>
        public SqlVistorFlag Append(string content, bool needCheckSep = false)
        {
            if (!_isStarted)
                _isStarted = true;
            else
            {
                if (needCheckSep)
                {
                    _SqlBuilder.Append(GetSeparate());
                }
            }

            _SqlBuilder.Append(content);
            return this;
        }

        /// <summary>
        /// 获取对应的参数名称
        /// 给参数添加前置符号
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public virtual string GetParaName(string name)
        {
            return string.Concat(ParaPreToken, name);
        }

        /// <summary>
        /// 常量参数下标
        /// </summary>
        private int _ConstantParaIndex;

        /// <summary>
        /// 获取定制常量参数名称
        /// 部分参数并无指定属性，这里自定义不重复参数
        /// </summary>
        /// <returns></returns>
        public virtual string GetCustomParaName()
        {
            const string parameterPre = "ConstPara";
            return string.Concat(ParaPreToken, parameterPre, _ConstantParaIndex++);
        }

        /// <summary>
        /// 获取属性列名称
        /// 内部补充转义字符，防止和数据库关键字名称冲突，如 `name`
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public virtual string GetColName(string name)
        {
            return string.Concat(_ColPrefix, name, _ColPostfix);
            //return string.Concat('`', name, '`');
            //return name;
        }

        /// <summary>
        /// 获取语句中的分割符
        /// </summary>
        /// <returns></returns>
        private string GetSeparate()
        {
            switch (VistorType)
            {
                case SqlVistorType.Select:
                    return ",";
                case SqlVistorType.Update:
                    return ",";
                case SqlVistorType.Where:
                    return " ";
                default:
                    return string.Empty;
            }
        }

        #endregion
    }

    /// <summary>
    /// SQL表达式访问器类型
    /// </summary>
    public enum SqlVistorType
    {
        /// <summary>
        /// Select 表达式解析类型
        /// </summary>
        Select,
        /// <summary>
        /// Update 表达式解析类型
        /// </summary>
        Update,
        /// <summary>
        /// Where 表达式解析类型
        /// </summary>
        Where
    }
}