using Infrastructure;
using Infrastructure.Attribute;
using Infrastructure.Model;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using NLog;
using System.Text.Encodings.Web;
using ZR.Common;
using ZR.Infrastructure.IPTools;
using ZR.Model.System;
using ZR.ServiceCore.Services;
using textJson = System.Text.Json;

namespace ZR.ServiceCore.Middleware
{
    /// <summary>
    /// 全局异常处理中间件
    /// 调用 app.UseMiddlewareGlobalExceptionMiddleware>();
    /// </summary>
    public class GlobalExceptionMiddleware
    {
        private readonly RequestDelegate next;
        private readonly ISysOperLogService SysOperLogService;

        static readonly Logger Logger = LogManager.GetCurrentClassLogger();
        private readonly textJson.JsonSerializerOptions options = new()
        {
            Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
            PropertyNamingPolicy = textJson.JsonNamingPolicy.CamelCase,
            WriteIndented = true
        };

        /// <summary>
        /// 
        /// </summary>
        /// <param name="next"></param>
        /// <param name="sysOperLog"></param>
        public GlobalExceptionMiddleware(RequestDelegate next, ISysOperLogService sysOperLog)
        {
            this.next = next;
            this.SysOperLogService = sysOperLog;
        }

        public async Task Invoke(HttpContext context)
        {
            try
            {
                await next(context);
            }
            catch (Exception ex)
            {
                await HandleExceptionAsync(context, ex);
            }
        }

        private async Task HandleExceptionAsync(HttpContext context, Exception ex)
        {
            LogLevel logLevel = LogLevel.Info;
            int code = (int)ResultCode.GLOBAL_ERROR;
            string msg;
            string error = string.Empty;
            bool notice = true;
            //自定义异常
            if (ex is CustomException customException)
            {
                code = customException.Code;
                msg = customException.Message;
                error = customException.LogMsg;
                notice = customException.Notice;
            }
            else if (ex is ArgumentException)//参数异常
            {
                code = (int)ResultCode.PARAM_ERROR;
                msg = ex.Message;
            }
            else
            {
                var q1 = "Exception has been thrown by the target of an invocation";
                var an1 = string.Empty;
                if (ex.Message.Contains(q1))
                {
                    an1 = $"====请查看issue：https://gitee.com/izory/ZrAdminNetCore/issues/I6S4DZ";
                }
                msg = "服务器好像出了点问题，请联系系统管理员...";
                error = $"异常原因：{ex.Message}{an1}";
                logLevel = LogLevel.Error;
                context.Response.StatusCode = 500;
            }

            ApiResult apiResult = new(code, msg);
#if DEBUG
            if (logLevel == LogLevel.Error)
            {
                apiResult.Add("error", "请在issue里面寻找答案或者官方文档查看常见问题：https://gitee.com/izory/ZrAdminNetCore/issues");
            }
#endif
            string responseResult = textJson.JsonSerializer.Serialize(apiResult, options);
            string ip = HttpContextExtension.GetClientUserIp(context);
            var ip_info = IpTool.Search(ip);

            SysOperLog sysOperLog = new()
            {
                Status = 1,
                OperIp = ip,
                OperUrl = HttpContextExtension.GetRequestUrl(context),
                RequestMethod = context.Request.Method,
                JsonResult = responseResult,
                ErrorMsg = string.IsNullOrEmpty(error) ? msg : error,
                OperName = HttpContextExtension.GetName(context),
                OperLocation = ip_info.Province + " " + ip_info.City,
                OperTime = DateTime.Now,
                OperParam = HttpContextExtension.GetRequestValue(context, context.Request.Method)
            };
            var endpoint = GetEndpoint(context);
            if (endpoint != null)
            {
                var logAttribute = endpoint.Metadata.GetMetadata<LogAttribute>();
                if (logAttribute != null)
                {
                    sysOperLog.BusinessType = (int)logAttribute.BusinessType;
                    sysOperLog.Title = logAttribute?.Title;
                    sysOperLog.OperParam = logAttribute.IsSaveRequestData ? sysOperLog.OperParam : "";
                    sysOperLog.JsonResult = logAttribute.IsSaveResponseData ? sysOperLog.JsonResult : "";
                }
            }
            LogEventInfo ei = new(logLevel, "GlobalExceptionMiddleware", error)
            {
                Exception = ex,
                Message = error
            };
            ei.Properties["status"] = 1;//走正常返回都是通过走GlobalExceptionFilter不通过
            ei.Properties["jsonResult"] = responseResult;
            ei.Properties["requestParam"] = sysOperLog.OperParam;
            ei.Properties["user"] = sysOperLog.OperName;

            Logger.Log(ei);
            context.Response.ContentType = "text/json;charset=utf-8";
            await context.Response.WriteAsync(responseResult, System.Text.Encoding.UTF8);

            string errorMsg = $"> 操作人：{sysOperLog.OperName}" +
                $"\n> 操作地区：{sysOperLog.OperIp}({sysOperLog.OperLocation})" +
                $"\n> 操作模块：{sysOperLog.Title}" +
                $"\n> 操作地址：{sysOperLog.OperUrl}" +
                $"\n> 错误信息：{msg}\n\n> {error}";

            SysOperLogService.InsertOperlog(sysOperLog);
            if (!notice) return;
            WxNoticeHelper.SendMsg("系统异常", errorMsg, msgType: WxNoticeHelper.MsgType.markdown);
        }

        public static Endpoint GetEndpoint(HttpContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            return context.Features.Get<IEndpointFeature>()?.Endpoint;
        }
    }
}
