// Package middleware ...
//
// Description : middleware ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022-07-14 10:53
package middleware

import (
	"strings"

	"git.zhangdeman.cn/zhangdeman/gin/request/parse_body"
	"git.zhangdeman.cn/zhangdeman/gin/util"

	"git.zhangdeman.cn/zhangdeman/consts"
	"git.zhangdeman.cn/zhangdeman/gin/request"
	"go.uber.org/zap"

	"git.zhangdeman.cn/zhangdeman/gin/define"

	"git.zhangdeman.cn/zhangdeman/logger"
	"github.com/gin-gonic/gin"
)

// fillCfg 填充默认配置
func fillCfg(cfg *AccessConfig) {
	if nil == cfg {
		return
	}
	if nil == cfg.IsRecordLog {
		cfg.IsRecordLog = defaultIsRecordLog
	}
	if nil == cfg.RequestHeaderList {
		cfg.RequestHeaderList = make([]string, 0)
	}
	if nil == cfg.ResponseHeaderList {
		cfg.ResponseHeaderList = make([]string, 0)
	}
}

// getLogRequestHeader 获取记录的请求header
func getLogRequestHeader(ctx *gin.Context, cfg *AccessConfig) map[string][]string {
	// 请求header
	headerTable := make(map[string][]string)
	if len(cfg.RequestHeaderList) == 0 {
		// 全部记录
		for headerKey := range ctx.Request.Header {
			cfg.RequestHeaderList = append(cfg.RequestHeaderList, headerKey)
		}
	}
	for _, key := range cfg.RequestHeaderList {
		headerTable[key] = ctx.Request.Header.Values(key)
	}
	return headerTable
}

// getLogResponseHeader 记录相应header
func getLogResponseHeader(ctx *gin.Context, cfg *AccessConfig) map[string][]string {
	// 响应header
	responseHeaderTable := make(map[string][]string)
	if len(cfg.ResponseHeaderList) == 0 {
		// 全部记录
		for headerKey := range ctx.Writer.Header() {
			cfg.ResponseHeaderList = append(cfg.ResponseHeaderList, headerKey)
		}
		zap.Any("pkg_gin_response_header", ctx.Writer.Header())
	}
	for _, key := range cfg.ResponseHeaderList {
		responseHeaderTable[key] = ctx.Writer.Header().Values(key)
	}
	return responseHeaderTable
}

// LogRequest 记录请求日志
func LogRequest(cfg *AccessConfig) gin.HandlerFunc {
	fillCfg(cfg)
	handleConfig := define.GetHttpHandleConfig()
	return func(ctx *gin.Context) {
		// 未传入配置或者未传入日志实例
		if nil == cfg || nil == cfg.Logger || (nil != cfg.IsRecordLog && !cfg.IsRecordLog(ctx)) {
			ctx.Next()
			return
		}
		startRequestTime := request.WrapperHandle.GetCtxIntData(ctx, handleConfig.StartRequestTimeField, 0)
		// 记录请求日志
		data := map[string]any{
			handleConfig.StartRequestTimeField: startRequestTime,                      // 开始请求时间
			"request_header":                   getLogRequestHeader(ctx, cfg),         // 请求header
			"request_query":                    request.WrapperHandle.ParseQuery(ctx), // 获取请求query
			"request_body":                     map[string]any{},                      // 请求body
		}
		data["request_body"], _ = parse_body.ExecuteForMap(ctx)
		logData := logger.NewLogData(util.GinCtxToContext(ctx), consts.LogTypeRequest, "", data)
		cfg.Logger.Info("接口请求日志记录", logData.ToFieldList()...)
		ctx.Next()
		// 结束时间
		finishRequestTime := request.WrapperHandle.GetCtxIntData(ctx, handleConfig.FinishRequestTimeField, 0)
		ctx.Set(handleConfig.FinishRequestTimeField, finishRequestTime)
		// 记录相应日志
		logResponseData := logger.NewLogData(util.GinCtxToContext(ctx), consts.LogTypeOutput, "", map[string]any{
			handleConfig.FinishRequestTimeField: finishRequestTime,                                                                            // 完成请求时间
			"request_cost":                      finishRequestTime - startRequestTime,                                                         // 请求耗时
			"response_body":                     request.WrapperHandle.GetResponseBody(ctx, handleConfig.ResponseDataField, map[string]any{}), // 响应body
			"response_header":                   getLogResponseHeader(ctx, cfg),                                                               // 响应header
		})
		if ctx.GetBool(define.GetHttpHandleConfig().RequestIsSuccessField) {
			cfg.Logger.Info("接口响应日志记录", logResponseData.ToFieldList()...)
		} else {
			cfg.Logger.Error("接口响应日志记录", logResponseData.ToFieldList()...)
		}
	}
}

// AccessConfig 访问记录的配置
type AccessConfig struct {
	Logger             *zap.Logger                 // 日志实例
	RequestHeaderList  []string                    // 要记录哪些header , 不传全部记录
	ResponseHeaderList []string                    // 要记录哪些响应header, 不传全部记录
	IsRecordLog        func(ctx *gin.Context) bool // 验证当前请求是否记录日志
}

// defaultIsRecordLog 默认仅记录 json api 日志
func defaultIsRecordLog(ctx *gin.Context) bool {
	return strings.Contains(ctx.Writer.Header().Get(consts.HeaderKeyContentType.String()), "application/json")
}
