using Hh.Mes.Common;
using Hh.Mes.Common.config;
using Hh.Mes.Common.Http;
using Hh.Mes.Common.Infrastructure;
using Hh.Mes.Common.Json;
using Hh.Mes.Common.log;
using Hh.Mes.Common.Redis;
using Hh.Mes.Common.Request;
using Hh.Mes.Pojo.System;
using Hh.Mes.POJO.Entity;
using Hh.Mes.POJO.EnumEntitys;
using Hh.Mes.POJO.Response;
using Hh.Mes.Service.Logs;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using Org.BouncyCastle.Crypto;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Net;
using System.Reflection;
using System.Text;

namespace Hh.Mes.Service.Repository
{
    /// <summary>
    /// SqlSugar
    /// http://www.codeisbug.com/Home/Doc?typeId=1228 
    /// C#拼接SQL中in条件 https://www.cnblogs.com/zhuyuchao/p/10412031.html
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class RepositorySqlSugar<T> : SimpleClient<T> where T : class, new()
    {
        #region 属性
        /// <summary>
        /// 用户信息web(xxxServer注入的时候传入)
        /// </summary>
        public dynamic sysWebUser { get; set; }

        /// <summary>
        /// api接口身份信息(类混乱结果)
        /// </summary>
        public UserAuthSession sysUserApi { get; set; }

        /// <summary>
        /// 上传文件夹目录
        /// </summary>
        public string uploadFolder { get; set; }

        /// <summary>
        ///动态值
        /// </summary>
        public dynamic ObjValue { get; set; }
        #endregion

        public RepositorySqlSugar(ISqlSugarClient context = null) : base(context)//注意这里要有默认值等于null
        {
            if (context == null)
            {
                var conStr = ConfigRead.GetInstance.GetAppsetConnection().BaseDBContext;
                base.Context = new SqlSugarClient(new ConnectionConfig()
                {
                    //数据库类型
                    DbType = DbType.SqlServer,
                    //从特性读取主键自增信息
                    InitKeyType = InitKeyType.Attribute,
                    //自动释放和关闭数据库连接,如果有事务事务结束时关闭,否则每次操作后关闭
                    IsAutoCloseConnection = true,
                    //连接符字串
                    ConnectionString = conStr,
                    //增加对C#自带的实体特性支持,比如实体的[Table]特性,字段的[Key]特性
                    ConfigureExternalServices = SetExternalServices(),
                    MoreSettings = new ConnMoreSettings()
                    {
                        IsWithNoLockQuery = true//全局 With(nolock) 
                    },
                });
                //OnError 写日志
                base.Context.Aop.OnError = ExceptionLog;
            }
            uploadFolder = "UploadFile";
        }

        /// <summary>
        /// 设置自定义特性(增加对C#自带的实体特性支持,比如实体的[Table]特性,字段的[Key]特性等)
        /// </summary>
        /// <returns></returns>
        private static ConfigureExternalServices SetExternalServices()
        {
            return new ConfigureExternalServices()
            {
                //设置实体的SqlSugar特性,从而把C#自带的[Table]特性变成SqlSugar特性
                EntityNameService = (type, entity) =>
                {
                    var attributes = type.GetCustomAttributes(true);
                    if (attributes.Any(it => it is TableAttribute))
                    {
                        entity.DbTableName = (attributes.First(it => it is TableAttribute) as TableAttribute).Name;
                    }
                },
                //设置字段的SqlSugar特性,从而把C#自带的字段特性变成SqlSugar特性
                EntityService = (property, column) =>
                {
                    //获取所有特性
                    var attributes = property.GetCustomAttributes(true);//get all attributes 
                    // 如果特性含有字段的[Key]特性,就设置字段SqlSugar的IsPrimaryKey特性
                    if (attributes.Any(it => it is KeyAttribute))
                    {
                        column.IsPrimarykey = true;
                    }
                    // 如果特性含有字段的[NotMapped]特性,就设置字段SqlSugar的IsIgnore特性
                    if (attributes.Any(it => it is NotMappedAttribute))
                    {
                        column.IsIgnore = true;
                    }
                    // 如果特性含有字段的[Column]特性,就设置字段SqlSugar的DbColumnName特性
                    if (attributes.Any(it => it is ColumnAttribute))
                    {
                        column.DbColumnName = (attributes.First(it => it is ColumnAttribute) as ColumnAttribute).Name;
                    }
                }
            };
        }

        /// <summary>
        /// 记录错误SQL日志 第一次启动失败判断 不写入数据库日志
        /// </summary>
        /// <param name="exp">SqlSugar异常实体</param>
        private static void ExceptionLog(SqlSugarException exp)
        {
            if (exp.Message.IndexOf("连接数据库过程中发生错误") > -1) return;
            StringBuilder sqlString = new StringBuilder();
            StringBuilder parameterString = new StringBuilder();
            sqlString.Append(exp.Sql);
            SugarParameter[] sugarParameters = exp.Parametres as SugarParameter[];
            if (sugarParameters != null && sugarParameters.Length > 0)
            {
                //参数从长到短来循环,这样替换sql中的参数的时候,避免@Parame11被@Parame给替换的情况
                foreach (var item in sugarParameters.OrderByDescending(t => t.ParameterName.Length))
                {
                    if (item.DbType.ToString().Contains("String") || item.DbType.ToString().Contains("Date") ||
                        item.DbType == System.Data.DbType.Guid || item.DbType == System.Data.DbType.Time ||
                        item.DbType == System.Data.DbType.Object || item.DbType == System.Data.DbType.Xml)
                    {
                        sqlString.Replace(item.ParameterName, "'" + item.Value.ToString() + "'");
                        parameterString.Append(item.ParameterName + " = '" + item.Value + "',");
                    }
                    else
                    {
                        sqlString.Replace(item.ParameterName, item.Value.ToString());
                        parameterString.Append(item.ParameterName + " = " + item.Value + ",");
                    }
                }
                //删除逗号
                parameterString.Remove(parameterString.Length - 1, 1);
            }

            var errorMsg = $"【SqlSugar】执行SQL语句【{sqlString}】,参数【{parameterString}】,发生错误【{exp.Message}】";
            QueueInterLog.GetInstance.EnqueueInterLog(null, null, EnumLog.AOP全局错误.ToString(), errorMsg, 0, user: "AOP", sysTitle: EnumLog.AOP全局错误.ToString());
            Log4NetHelper.Instance.Error(errorMsg);
        }

        /// <summary>
        /// 写入数据  封装方法 主子表写入参考 :MateLoadTemplateService (打包提交默认带有事务:一些自定义的sql或者存储过程一起打包可能会不支持)
        /// </summary>
        /// <param name="obj">model,或者list,或者 Dictionary </param>
        /// <param name="isTransaction">true开启事务</param>
        public virtual bool Add(dynamic obj, bool isTransaction = false)
        {
            if (isTransaction)
            {
                Context.Insertable(obj).AddQueue();
                return Context.SaveQueuesAsync().Result > 0;
            }
            return Context.Insertable(obj).ExecuteCommand() > 0;
        }

        /// <summary>
        /// Update 封装方法
        /// </summary>
        public virtual bool Update<M>(M m, Expression<Func<M, bool>> expression) where M : class, new()
        {
            return Context.Updateable<M>(m).Where(expression).ExecuteCommand() > 0;
        }

        /// <summary>
        /// 删除 封装方法
        /// </summary>
        public virtual bool Delete<M>(M m, Expression<Func<M, bool>> expression) where M : class, new()
        {
            return Context.Deleteable(m).Where(expression).ExecuteCommand() > 0;
        }

        /// <summary>
        /// 上传文件,写入附件表sys_File targetTableName,targetId 必填
        /// </summary>
        public bool UploadFile(IFormFileCollection files, IHostingEnvironment environment, List<sys_File> sysFileModel)
        {
            if (files.Count != sysFileModel.Count || files.Count == 0) return false;
            var upFloder = Path.Combine(environment.ContentRootPath, uploadFolder);
            if (!Directory.Exists(upFloder)) Directory.CreateDirectory(upFloder);

            for (int i = 0; i < files.Count; i++)
            {
                if (files[i].Length == 0) continue;

                #region 文件保存磁盘
                var suffix = Path.GetExtension(files[i].FileName);
                var fileName = Path.GetFileName(files[i].FileName);
                var newFileName = Guid.NewGuid().ToString("N") + suffix;
                var filePath = Path.Combine(upFloder, newFileName);
                using (var fs = File.Create(filePath))
                {
                    files[i].CopyTo(fs);
                    fs.Flush();
                }
                #endregion


                #region 保存文件记录到数据库
                var relativePaths = $"/{uploadFolder}/" + newFileName;
                var config = ConfigRead.GetInstance.GetAppsetConnection();
                var ip = ComputerHelp.GetAddressIP();
                var httpsPort = config.HttpPort == 0? config.HttpsPort : config.HttpPort;
                sysFileModel[i].host = ip + ":" + httpsPort;
                sysFileModel[i].suffix = suffix;
                sysFileModel[i].createTime = DateTime.Now;
                sysFileModel[i].createBy = sysWebUser == null ? sysUserApi.Account : sysWebUser?.Account;
                sysFileModel[i].isDelete = 0;
                sysFileModel[i].fileName = fileName;
                sysFileModel[i].url = ObjValue + "://" + sysFileModel[i].host + relativePaths;
                #endregion
            }
            Context.Insertable(sysFileModel).IgnoreColumns().AddQueue();
            return Context.SaveQueues() > 0;
        }

        /// <summary>
        /// 保存文件
        /// </summary>
        public void SaveFile(byte[] files, string path)
        {
            BinaryWriter bw = new BinaryWriter(System.IO.File.Open(path, FileMode.OpenOrCreate));
            bw.Write(files);
            bw.Close();
        }

        /// <summary>
        /// 获取最大值
        /// </summary>
        /// <param name="maxField"></param>
        /// <returns></returns>
        public int GetTableMaxValue<M>(string maxField) where M : class, new()
        {
            var value = Context.Queryable<M>().Max<int>(maxField);
            return value == 0 ? 1 : value + 1;
        }

        /// <summary>
        ///  获取字典 select * from[dbo].[sys_dict_data] where dictType = 'sysCode'
        /// </summary>
        /// <param name="dictLabel">值</param>
        /// <param name="dictType">类型</param>
        /// <returns>返回dictValue 字段值</returns>
        public virtual string GetDictionaryDictValue(string dictLabel, string dictType = "sysCode")
        {
            return Context.Queryable<sys_dict_data>().With(SqlWith.NoLock).Where(x => x.dictType == dictType && x.dictLabel == dictLabel).First()?.dictValue;
        }

        /// <summary>
        /// 拼接Order条件
        /// </summary>
        /// <param name="pageReq">PageReq类型的分页实体</param>
        /// <param name="entity">查询对象的实体</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public Expression<Func<T, object>> LinqOrder(PageReq pageReq, T entity)
        {
            try
            {
                string propertyName = "id";
                if (pageReq != null && pageReq.field != null)
                {
                    PropertyInfo propertyInfo = entity.GetType().GetProperty(pageReq.field);
                    if (propertyInfo != null)
                    {
                        propertyName = pageReq.field;
                    }
                }
                var memberExpression = Expression.PropertyOrField(Expression.Constant(entity), propertyName);
                var expression = Expression.Lambda<Func<T, object>>(memberExpression);
                return expression;
            }
            catch (Exception ex)
            {
                throw new Exception($"{ex.Message}");
            }
        }

        /// <summary>
        /// 执行SqlSugar的队列 封装方法(取代打包的执行方法,因为打包的执行不能超过200个变量,很容易超出)
        /// </summary>
        public int ExecuteQueues(ISqlSugarClient context)
        {
            int count = 0;
            try
            {
                if (context.Queues.Count == 0)
                {
                    return count;
                }
                #region 将context.Queues中的sql语句和参数,一条一条取出来,然后再拼接成大SQL,减少发送次数。
                //StringBuilder sb = new StringBuilder();
                //List<SugarParameter> paramList = new List<SugarParameter>();
                //List<Tuple<string, List<SugarParameter>>> sugarCommandList = new List<Tuple<string, List<SugarParameter>>>();

                //for (var i = 0; i < context.Queues.Count; i++)
                //{
                //    int sqlStringCount = sb.Length + context.Queues[i].Sql.Length;
                //    int paramCount = paramList.Count + context.Queues[i].Parameters.Length;
                //    //SQL Server数据库的SQL语句,参数不能多于2000个,字符几乎无限就暂定为200万字(4M的大小)
                //    if (sqlStringCount > 2 * 1024 * 1024 || paramCount > 1999)
                //    {
                //        //超出了一条SQL语句的最大范围,就把SQL放到sugarCommandList中,然后再新实例化新的对象
                //        sugarCommandList.Add(new Tuple<string, List<SugarParameter>>(sb.ToString(), paramList));
                //        sb = new StringBuilder();
                //        paramList = new List<SugarParameter>();
                //    }

                //    var row = i + 1;
                //    string sqlString = context.Queues[i].Sql;
                //    // 先排序是因为变量有id, id0等,要把字符短的在前,长的放在后面,不然先替换id0,再替换id,就会产生多次替换
                //    SugarParameter[] sugarParameters = context.Queues[i].Parameters;
                //    foreach (var item in sugarParameters)
                //    {
                //        //因为传入的有List类型,所以每个变量名都是一样的,拼接后改为"变量_#$行号",这样每个变量名就唯一了。
                //        string ParameterNewName = item.ParameterName + "_#$" + row;
                //        //将 ("\b"表示非字符,包括空格,换行等)
                //        sqlString = sqlString.Replace(item.ParameterName + "\b" , ParameterNewName);
                //        item.ParameterName = ParameterNewName;
                //    }
                //    //将替换好的SQL和参数存放起来
                //    sb.AppendLine(sqlString);
                //    paramList.AddRange(sugarParameters);

                //    if (i == context.Queues.Count - 1)
                //    {
                //        sugarCommandList.Add(new Tuple<string, List<SugarParameter>>(sb.ToString(), paramList));
                //    }
                //}
                // 从sugarCommandList中取出拼接好的SQL,来执行。
                //context.Ado.BeginTran();
                //foreach (var item in sugarCommandList)
                //{
                //    if (!string.IsNullOrWhiteSpace(item.Item1))
                //    {
                //        count += context.Ado.ExecuteCommand(item.Item1, item.Item2);
                //    }
                //}
                //context.Ado.CommitTran();
                #endregion 


                context.Ado.BeginTran();
                foreach (var item in context.Queues)
                {
                    if (!string.IsNullOrWhiteSpace(item.Sql))
                    {
                        count += context.Ado.ExecuteCommand(item.Sql, item.Parameters);
                    }
                }
                context.Ado.CommitTran();
            }
            catch (Exception ex)
            {
                context.Ado.RollbackTran();
                throw ex;
            }
            finally
            {
                context.Queues.Clear();
            }
            return count;
        }

        /// <summary>
        /// 多个上下文调用次方法
        /// 执行SqlSugar的队列 封装方法
        /// </summary>
        public int ExecuteQueues(List<ISqlSugarClient> contextList)
        {
            int count = 0;
            try
            {
                if (contextList.Count == 0)
                {
                    return count;
                }
                if (!contextList.Exists(t => t.Queues.Count > 0))
                {
                    return count;
                }

                base.Context.Ado.BeginTran();
                foreach (var context in contextList)
                {
                    foreach (var item in context.Queues)
                    {
                        if (!string.IsNullOrWhiteSpace(item.Sql))
                        {
                            count += base.Context.Ado.ExecuteCommand(item.Sql, item.Parameters);
                        }
                    }
                }
                //base.Context.Ado.RollbackTran();
                base.Context.Ado.CommitTran();
            }
            catch (Exception ex)
            {
                base.Context.Ado.RollbackTran();
                throw ex;
            }
            finally
            {
                contextList.ForEach(x =>
                {
                    x.Queues.Clear();
                });
            }
            return count;
        }

        /// <summary>
        /// 清空当前用户的redis授权缓存,这样用的时候就会自动从数据库更新一次
        /// </summary>
        public void ClearCurrentUserRedis()
        {
            var account = sysWebUser.Account;
            var cl = new RedisBase();
            if (cl.redisClient.Exists(account))
            {
                cl.redisClient.Del(account);
            }
        }

        /// <summary>
        /// 清空所有登录用户的redis授权缓存,这样用的时候就会自动从数据库更新一次
        /// </summary>
        public void ClearOnlineUserRedis()
        {
            //清空被删除用户的Redis缓存
            var userOnlineList = Context.Queryable<sys_user_online>().Where(t => true).ToList();
            var cl = new RedisBase();
            foreach (var item in userOnlineList)
            {
                if (cl.redisClient.Exists(item.account))
                {
                    cl.redisClient.Del(item.account);
                }
            }
        }

        /// <summary>
        /// 根据ip找工位
        /// </summary>
        /// <param name="ip"></param>
        /// <returns></returns>
        public Response<base_work_station> GetStationByIP(string ip)
            {
                var result = new Response<base_work_station>();
                if (ip == "127.0.0.1")
                {
                    result.Status = false;
                    result.Code = 500;
                    result.Message = "ip【127.0.0.1】请检查本地ip,确认是否连接网络,请于工位中设置Ip地址!";
                    return result;
                }
                var stations = Context.Queryable<base_work_station>().Where(u => u.monitorIP == ip).First();
                if (stations == null || string.IsNullOrEmpty(stations.lineCode))
                {
                    result.Status = false;
                    result.Code = 500;
                    result.Message = $"根据ip【{ip}】找到对应工位和线体失败,请检查工位信息ip是否配置成功,请于工位中设置Ip地址!";
                    return result;
                };
                result.Status = true;
                result.Code = 200;
                result.Result = stations;
                return result;
            }

        /// <summary>
        /// http  
        /// </summary>
        public Response HttpMan(string url, dynamic requestData, string title , string method = "post", string contentType = "application/json",string token=null)
        {
            var stopwatch = new Stopwatch();
            stopwatch.Start();
            var response = new Response(false);
            var httpItem = new HttpItem
            {
                URL = url,
                Method = method,
                ContentType = contentType,
                Postdata = JsonConvert.SerializeObject(requestData),
                Timeout = 80000
            };

            if(!string.IsNullOrEmpty(token))
            {
                httpItem.Header.Add("Authorization", token);
            }

            var httpHelper = new HttpHelper();
            var httpResult = httpHelper.GetHtml(httpItem);
            response.Result = httpResult.Html;
            stopwatch.Stop();
            var resultMsg = title + ",失败原因:" + httpResult.Html;
            var user = sysUserApi == null ? (sysWebUser == null ? "定时器" : sysWebUser?.Account) : sysUserApi.Account;
            QueueInterLog.GetInstance.EnqueueInterLog(httpItem, httpResult, title, resultMsg, stopwatch.Elapsed.TotalMilliseconds, user: user, sysTitle: title);

            if (httpResult.StatusCode == HttpStatusCode.OK)
            {
                //根据实际情况处理
                var resultTemp = httpResult.Html.TrimStart('[').TrimEnd(']');
                dynamic result = DynamicJson.Parse(resultTemp);
                if (result == null)
                {
                    QueueInterLog.GetInstance.EnqueueInterLog(httpItem, httpResult, title, "异常错误:" + resultMsg, stopwatch.Elapsed.TotalMilliseconds, user: user, sysTitle: title);
                    return response.ResponseError(resultMsg);
                }

                //请求返回成功
                if (result.code == "0") return response.ResponseSuccess("请求返回成功");
            }
            if (httpResult.StatusCode == HttpStatusCode.InternalServerError)
            {
                response.Message = resultMsg;
                QueueInterLog.GetInstance.EnqueueInterLog(httpItem, httpResult, title, "异常错误:"+ response.Message, stopwatch.Elapsed.TotalMilliseconds, user: user, sysTitle: title);
                return response;
            }
            if (httpResult.StatusCode == HttpStatusCode.NotFound)
            {
                response.Message = "url:" + httpItem.URL + "错误、请求地址未找到404" + resultMsg;
                QueueInterLog.GetInstance.EnqueueInterLog(httpItem, httpResult, title, "异常错误:" + response.Message, stopwatch.Elapsed.TotalMilliseconds, user: user, sysTitle: title);
                return response;
            }
            QueueInterLog.GetInstance.EnqueueInterLog(httpItem, httpResult, title, "异常错误:" + resultMsg, stopwatch.Elapsed.TotalMilliseconds, user: user, sysTitle: title);
            response.Message = resultMsg;
            return response;
        }
    
    }
}