package monitor

import (
	"leafstalk/log"
	"leafstalk/otherutils"
	"reflect"
	"runtime"
	"time"
)

type Alarm struct {
	Type      int64  `json:"type"`
	Name      string `json:"name"`
	Args      string `json:"args"`
	CostTime  int64  `json:"costTime"`
	CreatedAt int64  `json:"createdAt"`
}

var (
	reportFunc func(alarm *Alarm)
)

func SetReportFunc(f func(alarm *Alarm)) {
	reportFunc = f
}

func reportMonitorAlarm(alarm *Alarm) {
	if reportFunc != nil {
		reportFunc(alarm)
	} else {
		log.Warnf("monitor.reportMonitorAlarm:%v", otherutils.DumpToJSON(alarm))
	}
}

// TimeoutWarn 超时报警
// 这个函数的几个参数说明如下：
// funName、args 表示超时发生位置的两个字符串参数
// start 程序开始执行的时间
// timeLimit  函数执行超时阀值，单位是毫秒
func TimeoutWarn(funName string, args any, typ int64, start time.Time, tl ...int64) {
	timeLimit := DefaultAlarmThreshold
	if len(tl) > 0 {
		timeLimit = tl[0]
	}
	ct := time.Since(start).Milliseconds()
	if ct > timeLimit {
		// 上报报警数据
		alarm := new(Alarm)
		alarm.Type = typ
		alarm.Name = funName
		alarm.Args = otherutils.DumpToJSON(args)
		alarm.CostTime = ct
		alarm.CreatedAt = start.Unix()
		reportMonitorAlarm(alarm)
	}
}

func GoExecTimeoutWarn(fn any, args any, start time.Time, tl ...int64) {
	funName := functionName(fn)
	if funName == "" {
		return
	}
	TimeoutWarn(funName, args, AlarmTypeGoExec, start, tl...)
}

func GoLoadTimeoutWarn(funName string, playerId int64, start time.Time, tl ...int64) {
	TimeoutWarn(funName, playerId, AlarmTypeGoLoad, start, tl...)
}

func GoLoadTimeoutWarn2(funName string, playerId string, start time.Time, tl ...int64) {
	TimeoutWarn(funName, playerId, AlarmTypeGoLoad, start, tl...)
}

func MySqlExecTimeoutWarn(funName string, args any, start time.Time, tl ...int64) {
	TimeoutWarn(funName, args, AlarmTypeMySqlExec, start, tl...)
}

func RedisDoTimeoutWarn(funName string, args any, start time.Time, tl ...int64) {
	TimeoutWarn(funName, args, AlarmTypeRedisDo, start, tl...)
}

func TimeoutWarnCall(f func(), funName string, args any, typ int64, start time.Time, tl ...int64) {
	defer TimeoutWarn(funName, args, typ, start, tl...)
	f()
}

func functionName(fn interface{}) string {
	fullName := runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()

	// 在完整名称内查找函数名
	lastDotIndex := 0
	for i := len(fullName) - 1; i >= 0; i-- {
		if fullName[i] == '.' {
			lastDotIndex = i
			break
		}
	}

	funcName := fullName[lastDotIndex+1:]
	return funcName
}
