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

import (
	"reflect"
	"strings"

	"git.zhangdeman.cn/zhangdeman/gin/define"
	"git.zhangdeman.cn/zhangdeman/gin/logger"
	"git.zhangdeman.cn/zhangdeman/util"
)

// controller 解析controller有哪些方法要注册为接口
type controllerParser struct {
}

// Parse 执行解析
//
// 符合注册为借口的方法具有如下特征:
//
// 1. 函数接受两个入参, 第一个参数为 gin.Context , 第二个参数为 任意结构体指针, 但是必须声明 Meta 相关信息, 否则会报错
//
// 2. 函数有两个返回值, 第一个返回值为任意结构体/结构体指针(限制死不能为map/slice, 方便后续统一标准化) , 第二个返回值为 error , 代表处理的异常, 会自动适配 exception.IException 类型
func (c controllerParser) Parse(inputController any) map[string]define.UriConfig {
	parseRes := make(map[string]define.UriConfig)
	if nil == inputController {
		return parseRes
	}
	controllerType := reflect.TypeOf(inputController)
	inputController = reflect.New(controllerType).Interface()
	controllerType = reflect.TypeOf(inputController)
	controllerValue := reflect.ValueOf(inputController)
	for methodIdx := 0; methodIdx < controllerType.NumMethod(); methodIdx++ {
		uriCfg, needRegister := c.methodConfig(controllerType.Method(methodIdx))
		if !needRegister {
			continue
		}
		uriCfg.ApiStructValue = controllerValue
		parseRes[uriCfg.Path] = uriCfg
	}
	return parseRes
}

// preCheckMethod 预检查方法是否可以注册为接口
func (c controllerParser) preCheckMethod(reflectMethod reflect.Method, config *define.UriConfig) (bool, reflect.Type, reflect.StructField) {
	var (
		metaField      reflect.StructField
		metaFieldExist bool
	)

	methodType := reflectMethod.Type
	// num0: 函数声明
	// num1: 第一个参数
	// num2: 第二个参数
	if methodType.NumIn() != 3 {
		return false, nil, metaField
	}
	// 第一个参数必须是 *gin.Context 或者 *define.Context
	paramOne := methodType.In(1).String()
	if paramOne != GinContextType {
		return false, nil, metaField
	}

	// 解析第二个参数是组合 Meta 的form表单
	formType := methodType.In(2)
	if formType.Kind() == reflect.Ptr {
		config.ParamIsPtr = true
		formType = formType.Elem()
	}

	if metaField, metaFieldExist = formType.FieldByName(FieldNameMeta); !metaFieldExist {
		return false, nil, metaField
	}
	return true, formType, metaField
}

// methodConfig 解析方法配置, 要求函数格式， 两个参数, 两个返回值, 格式 : func(ctx *gin.Context, formData anyStruct[组合Meta]) (anyStruct|map[response], error)
//
// 参数 : 方法反射结果
//
// 返回值 : 第一个 -> 解析出的接口配置 第二个 -> 是否要注册为接口
func (c controllerParser) methodConfig(reflectMethod reflect.Method) (define.UriConfig, bool) {
	var (
		needRegister bool
		metaField    reflect.StructField
		cfg          define.UriConfig
	)

	methodType := reflectMethod.Type
	// num0: 函数声明
	// num1: 第一个参数
	// num2: 第二个参数
	if needRegister, cfg.FormDataType, metaField = c.preCheckMethod(reflectMethod, &cfg); !needRegister {
		logger.Instance.Info("接口方法不符合要求, 不注册为接口, 方法名: " + reflectMethod.Name + ", 方法签名: " + methodType.String())
		return cfg, false
	}

	cfg.ResultDataType = methodType.Out(0)
	if methodType.Out(1).Kind().String() != ErrorType {
		// 判断是否是实现 error接口的方法
		outputErrParse := false
		for j := 0; j < methodType.Out(1).NumMethod(); j++ {
			if methodType.Out(1).Method(j).Name == ErrorInterfaceFuncName && // 实现 Error 方法
				methodType.Out(1).Method(j).Type.NumIn() == 0 && // 没有任何参数
				methodType.Out(1).Method(j).Type.NumOut() == 1 && // 一个返回值
				methodType.Out(1).Method(j).Type.Out(0).Kind().String() == reflect.String.String() {
				outputErrParse = true
				break
			}
		}
		if !outputErrParse {
			needRegister = false
			return cfg, needRegister
		}
	}
	c.setUriMeta(metaField, &cfg)

	if cfg.OutputStrict {
		// 开启输出严格模式校验
		if methodType.Out(0).Kind() != reflect.Struct && methodType.Out(0).Kind() != reflect.Map {
			panic(cfg.Path + " : 接口配置输出严格校验, 输出数据类型必须为 struct 或 *struct 或 map, 实际返回数据类型 : " + methodType.Out(0).Kind().String())
		}
	}

	// 解析参数配置
	//cfg.ParamList = c.parseParamConfig(formType)
	cfg.ApiLogicFunc = reflectMethod
	needRegister = true
	return cfg, needRegister
}

// setUriMeta 设置接口的 meta 信息
func (c controllerParser) setUriMeta(metaField reflect.StructField, cfg *define.UriConfig) {
	// 解析 meta 信息
	cfg.Path = metaField.Tag.Get(TagNamePath)                                                 // 接口路由
	cfg.RequestMethod = strings.Split(strings.ToUpper(metaField.Tag.Get(TagNameMethod)), ",") // 请求方法
	cfg.Desc = metaField.Tag.Get(TagNameDesc)                                                 // 接口描述
	cfg.TagList = strings.Split(metaField.Tag.Get(TagNameUriTag), ",")                        // 接口标签
	// 以下是bool类型的配置解析
	var boolMetaParse = func(tagName string) bool {
		val := strings.ToLower(metaField.Tag.Get(tagName))
		return val == "1" || val == "true"
	}
	cfg.OutputStrict = boolMetaParse(TagNameOutputStrict) // 配置接口数据是否严格要求是对象
	cfg.HookSync = boolMetaParse(TagNameHookSync)         // 同步执行判断
	cfg.IsWebsocket = boolMetaParse(TagNameIsWebsocket)   // 是否是 websocket 接口
	cfg.IsSse = boolMetaParse(TagNameIsSse)               // 是否是 sse 接口
	cfg.NoLogin = boolMetaParse(TagNameNoLogin)           // 是否需要登录

	// 最大执行时间
	cfg.MaxExecTime = int64(0)
	if tagVal := strings.ToLower(metaField.Tag.Get(TagNameMaxExecTime)); tagVal != "" {
		if err := util.ConvertAssign(&cfg.MaxExecTime, tagVal); nil != err {
			panic(cfg.Path + " : 最大执行时间配置错误(配置的值必须是无符号整型), 请检查配置 : " + err.Error())
		}
	}

	// 流控配置, 格式: {单次消耗次数}/{总次数}/{时间间隔}
	rateLimitStr := strings.TrimSpace(metaField.Tag.Get(TagNameRateLimit))
	if rateLimitStr != "" {
		cfgArr := strings.Split(rateLimitStr, "/")
		if len(cfgArr) != 3 {
			panic(cfg.Path + " : 流控配置错误(配置的值必须为3个用 / 分隔的数字, {单次消耗次数}/{总次数}/{时间间隔}), 请检查配置 : " + rateLimitStr)
		}
		if err := util.ConvertAssign(&cfg.RateLimitConfig.Use, cfgArr[0]); nil != err {
			panic(cfg.Path + " : 流控单次消耗次数不是一个合法数字 : " + err.Error())
		}
		if err := util.ConvertAssign(&cfg.RateLimitConfig.Total, cfgArr[1]); nil != err {
			panic(cfg.Path + " : 流控总次数不是一个合法数字 : " + err.Error())
		}
		if cfg.RateLimitConfig.Total < cfg.RateLimitConfig.Use {
			panic(cfg.Path + " : 流控总次数不能小于单次消耗次数 : " + rateLimitStr)
		}
		if err := util.ConvertAssign(&cfg.RateLimitConfig.TimeInterval, cfgArr[2]); nil != err {
			panic(cfg.Path + " : 流控时间间隔不是一个合法数字 : " + err.Error())
		}
	}
}
