// Package router ...
//
// Description : router ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2025-01-27 19:42
package router

import (
	"errors"
	"fmt"
	"net/http"
	"reflect"
	"sync"
	"time"

	"git.zhangdeman.cn/zhangdeman/consts"
	"git.zhangdeman.cn/zhangdeman/exception"
	"git.zhangdeman.cn/zhangdeman/gin/define"
	"git.zhangdeman.cn/zhangdeman/gin/logger"
	"git.zhangdeman.cn/zhangdeman/gin/request"
	"git.zhangdeman.cn/zhangdeman/gin/response"
	"git.zhangdeman.cn/zhangdeman/gin/util"
	loggerPkg "git.zhangdeman.cn/zhangdeman/logger"
	"git.zhangdeman.cn/zhangdeman/serialize"
	"git.zhangdeman.cn/zhangdeman/wrapper/op_string"
	"github.com/gin-gonic/gin"
	"github.com/mcuadros/go-defaults"
)

func (s *server) getFormInitValue(ctx *gin.Context, uriCfg define.UriConfig) (any, error) {
	var (
		formParam reflect.Value
		formValue any
		err       error
	)

	if uriCfg.FormDataType.Kind() == reflect.Ptr {
		formParam = reflect.New(uriCfg.FormDataType.Elem())
	} else {
		// 表单解析
		formParam = reflect.New(uriCfg.FormDataType)
	}
	formValue = formParam.Interface()
	if err = request.Form.Parse(ctx, formValue); nil != err {
		// 格式化验证错误的信息
		err = GetValidateErr(formValue, err)
		logger.Instance.Error("参数解析出现异常", loggerPkg.NewLogData(util.GinCtxToContext(ctx), logger.RecordType, logger.CodeParamValidateFailure, map[string]any{
			"err_msg": err.Error(),
		}).ToFieldList()...)
		return nil, err
	}

	// 非必传参数设置默认值
	defaults.SetDefaults(formValue)

	return formValue, nil
}

func (s *server) initRequest(ctx *gin.Context, uriCfg define.UriConfig) (any, reflect.Value, exception.IException) {
	var (
		err        error
		formValue  any
		inputValue reflect.Value
	)
	if formValue, err = s.getFormInitValue(ctx, uriCfg); nil != err {
		return nil, inputValue, exception.NewFromError(http.StatusBadRequest, err)
	}
	// 表单数据
	inputValue = reflect.ValueOf(formValue)
	if !uriCfg.ParamIsPtr {
		inputValue = inputValue.Elem()
	}

	// 注入公共参数
	if err = s.injectCommonParam(ctx, inputValue); nil != err {
		return nil, inputValue, exception.NewFromError(500, err)
	}

	// 默认请求失败
	ctx.Set(consts.GinRequestSuccess, false)
	// 初始化响应之后 logic
	logicAfterResponse := &define.LogicAfterResponse{
		SuccessHookFuncList: make([]func(ctx *gin.Context), 0),
		FailureHookFuncList: make([]func(ctx *gin.Context), 0),
		Lock:                &sync.RWMutex{},
	}
	// 此处暴露出去,是为了使用方可以获取到对应数据
	ctx.Set(consts.GinLogicAfterResponseKey, logicAfterResponse)
	return formValue, inputValue, nil
}

// callFunc 调用方法
func (s *server) callFunc(ctx *gin.Context, uriCfg define.UriConfig, inputValue reflect.Value) (any, exception.IException) {

	var (
		firstParam = reflect.ValueOf(ctx)
	)
	resList := uriCfg.ApiLogicFunc.Func.Call([]reflect.Value{uriCfg.ApiStructValue, firstParam, inputValue})
	if resList[1].IsNil() {
		// 请求成功, 更新标识
		ctx.Set(consts.GinRequestSuccess, true)
		return resList[0].Interface(), nil
	}

	return nil, s.formatError(ctx, resList[1].Interface())
}

// formatError 格式化错误
func (s *server) formatError(ctx *gin.Context, err any) exception.IException {
	if nil == err {
		return nil
	}
	var (
		ok       bool
		e        exception.IException
		innerErr error
	)

	// 请求失败
	if ok = errors.As(err.(error), &e); ok {
		// 本身就是exception.IException
		logger.Instance.Debug("请求结果err类型为 exception.IException, 无需特殊处理", loggerPkg.NewLogData(util.GinCtxToContext(ctx), logger.RecordType, logger.CodeLogicErrorWrapper, map[string]any{}).ToFieldList()...)
	} else if innerErr, ok = err.(error); ok {
		logger.Instance.Debug("请求结果err类型为 error, 包装为 exception.IException", loggerPkg.NewLogData(util.GinCtxToContext(ctx), logger.RecordType, logger.CodeLogicErrorWrapper, map[string]any{}).ToFieldList()...)
		e = exception.NewFromError(-1, innerErr)
	} else {
		logger.Instance.Debug("请求结果err类型 既不是 error 也不是 exception.IException, 包装为 exception.IException", loggerPkg.NewLogData(util.GinCtxToContext(ctx), logger.RecordType, logger.CodeLogicErrorWrapper, map[string]any{}).ToFieldList()...)
		e = exception.NewWithCodeAndData(-1, map[string]any{
			"err": err,
		})
	}
	return e
}

// RequestHandler 获取请求处理方法
func (s *server) RequestHandler(uriCfg define.UriConfig) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		defer s.hook(ctx, uriCfg)
		var (
			e            exception.IException
			handleResult any
			inputValue   reflect.Value
		)

		if _, inputValue, e = s.initRequest(ctx, uriCfg); nil != e {
			response.SendWithException(ctx, e, &define.ResponseOption{
				ContentType: consts.MimeTypeJson,
			})
			ctx.Abort()
			return
		}

		if handleResult, e = s.callFunc(ctx, uriCfg, inputValue); nil == e {
			response.SuccessWithExtension(ctx, handleResult, &define.ResponseOption{ContentType: consts.MimeTypeJson})
			return
		}

		response.SendWithException(ctx, e, &define.ResponseOption{
			ContentType: consts.MimeTypeJson,
		})
	}
}

// SseHandler sse连接请求
func (s *server) SseHandler(uriCfg define.UriConfig) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		var (
			err        error
			e          exception.IException
			inputValue reflect.Value
			firstParam reflect.Value
		)

		if _, inputValue, err = s.initRequest(ctx, uriCfg); nil != err {
			e = exception.NewFromError(http.StatusBadRequest, err)
			response.SendWithException(ctx, e, &define.ResponseOption{
				ContentType: consts.MimeTypeJson,
			})
			ctx.Abort()
			return
		}
		defer s.hook(ctx, uriCfg) // 执行 Logic 之后的相关逻辑

		ctx.Writer.Header().Set(consts.HeaderKeyContentType.String(), "text/event-stream")
		ctx.Writer.Header().Set(consts.HeaderKeyCacheControl.String(), "no-cache")
		ctx.Writer.Header().Set(consts.HeaderKeyConnection.String(), "keep-alive")
		ctx.Writer.Header().Set(consts.HeaderKeyXAccelBuffering.String(), "no")
		flusher, _ := ctx.Writer.(http.Flusher)
		// 发送连接就绪消息
		if _, err = fmt.Fprintf(ctx.Writer, define.SseMsgFormat, -1, "system", serialize.JSON.MarshalForStringIgnoreError(define.SseData{
			ID:        op_string.Random(8, ""),
			Object:    "system",
			Created:   time.Now().Unix(),
			Choices:   []map[string]any{},
			EventType: "connected",
		})); nil != err {
			// 无法推送数据, 等价于结束, 如有必要, 让客户端发起重连
			logger.Instance.Error("SSE 连接建立成功后, 发送链接成功消息出现异常", loggerPkg.NewLogData(util.GinCtxToContext(ctx), logger.RecordType, logger.CodeLogicSseInitError, map[string]any{
				"err_msg": err.Error(),
			}).ToFieldList()...)
			return
		}
		flusher.Flush()
		defer func() {
			// 发送连接关闭消息
			if _, err = fmt.Fprintf(ctx.Writer, define.SseMsgFormat, -3, "system", serialize.JSON.MarshalForStringIgnoreError(define.SseData{
				ID:        op_string.Random(8, ""),
				Object:    "system",
				Created:   time.Now().Unix(),
				Choices:   []map[string]any{},
				EventType: "closed",
			})); nil != err {
				// 无法推送数据, 等价于结束, 如有必要, 让客户端发起重连
				logger.Instance.Error("SSE 连接断开前, 发送链接断开消息出现异常", loggerPkg.NewLogData(util.GinCtxToContext(ctx), logger.RecordType, logger.CodeLogicSseClosedError, map[string]any{
					"err_msg": err.Error(),
				}).ToFieldList()...)
				return
			}
			flusher.Flush()
		}()
		firstParam = reflect.ValueOf(ctx)
		resList := uriCfg.ApiLogicFunc.Func.Call([]reflect.Value{uriCfg.ApiStructValue, firstParam, inputValue})
		if resList[1].IsNil() {
			// 请求成功, 更新标识
			ctx.Set(consts.GinRequestSuccess, true)
			response.SuccessWithExtension(ctx, resList[0].Interface(), &define.ResponseOption{ContentType: consts.MimeTypeJson})
			return
		}
		// 请求失败
		e = s.formatError(ctx, resList[1].Interface())
		if nil != e {
			// 异常终止
			if _, err = fmt.Fprintf(ctx.Writer, define.SseMsgFormat, -3, "system", serialize.JSON.MarshalForStringIgnoreError(define.SseData{
				ID:      op_string.Random(8, ""),
				Object:  "system",
				Created: time.Now().Unix(),
				Choices: []map[string]any{
					{
						"err_msg":  e.Message(),
						"err_code": e.Code(),
						"err_data": e.Data(),
					},
				},
				EventType: "failure",
			})); nil != err {
				// 无法推送数据, 等价于结束, 如有必要, 让客户端发起重连
				logger.Instance.Error("SSE 业务处理完成, 发送业务处理失败消息出现异常", loggerPkg.NewLogData(util.GinCtxToContext(ctx), logger.RecordType, logger.CodeLogicSseClosedError, map[string]any{
					"err_msg": err.Error(),
				}).ToFieldList()...)
				return
			}
		} else {
			// 正常终止
			if _, err = fmt.Fprintf(ctx.Writer, define.SseMsgFormat, -3, "system", serialize.JSON.MarshalForStringIgnoreError(define.SseData{
				ID:        op_string.Random(8, ""),
				Object:    "system",
				Created:   time.Now().Unix(),
				Choices:   []map[string]any{},
				EventType: "success",
			})); nil != err {
				// 无法推送数据, 等价于结束, 如有必要, 让客户端发起重连
				logger.Instance.Error("SSE 业务处理完成, 发送业务处理成功消息出现异常", loggerPkg.NewLogData(util.GinCtxToContext(ctx), logger.RecordType, logger.CodeLogicSseClosedError, map[string]any{
					"err_msg": err.Error(),
				}).ToFieldList()...)
				return
			}
		}
	}
}
