package ne_alarm_state_check_cmd

import (
	"encoding/json"
	"fmt"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"

	"be.ems/src/framework/constants"
	"be.ems/src/framework/cron"
	"be.ems/src/framework/logger"
	"be.ems/src/framework/utils/parse"
	neDataModel "be.ems/src/modules/network_data/model"
	neDataService "be.ems/src/modules/network_data/service"
	neModel "be.ems/src/modules/network_element/model"
	neService "be.ems/src/modules/network_element/service"
	wsService "be.ems/src/modules/ws/service"
)

var (
	triggerMax    int64         = 3               // 阈值：连续触发次数大于该值才会产生告警
	triggerCount  sync.Map                        // 阈值连续触发次数，存储每个事件的触发记录
	triggerWindow time.Duration = 5 * time.Second // 事件触发的时间窗口
)

var NewProcessor = &NeAlarmStateCheckCMDProcessor{
	neConfigBackupService: neService.NewNeConfigBackup,
	neInfoService:         neService.NewNeInfo,
	neStateService:        neDataService.NewNEState,
	alarmService:          neDataService.NewAlarm,
	wsSendService:         wsService.NewWSSend,
	count:                 0,
}

// NeAlarmStateCheckCMDProcessor 网元告警内存/CPU/磁盘检查
type NeAlarmStateCheckCMDProcessor struct {
	neConfigBackupService *neService.NeConfigBackup // 网元配置文件备份记录服务
	neInfoService         *neService.NeInfo         // 网元信息服务
	neStateService        *neDataService.NEState    // 网元状态信息服务
	alarmService          *neDataService.Alarm      // 告警信息服务
	wsSendService         *wsService.WSSend         // ws发送服务
	count                 int                       // 执行次数
}

// alarmParams 告警参数
type alarmParams struct {
	AlarmTitle        string `json:"alarmTitle"`        // NE State Check Alarm CPU/Menory/Disk
	AlarmType         string `json:"alarmType"`         // EquipmentAlarm=2
	OrigSeverity      string `json:"origSeverity"`      // Major=2
	SpecificProblem   string `json:"specificProblem"`   // Alarm Cause: CPU/Menory/Disk status received from target NE reaches the threshold
	SpecificProblemID string `json:"specificProblemId"` // AC10100
	AddInfo           string `json:"addInfo"`           // 告警补充信息
	CPUUseGt          int64  `json:"cpuUseGt"`          // CPU使用率大于， 范围0~100*CPU核心数
	MemUseGt          int64  `json:"memUseGt"`          // 内存使用率大于， 范围0~100%
	DiskUseGt         int64  `json:"diskUseGt"`         // 磁盘使用率大于， 范围0~100%

	// === 非参数字段 ===
	AlarmId string // 告警ID
}

func (s *NeAlarmStateCheckCMDProcessor) Execute(data any) (any, error) {
	s.count++ // 执行次数加一
	options := data.(cron.JobData)
	sysJob := options.SysJob
	logger.Infof("重复:%v 任务ID:%s  执行次数:%d", options.Repeat, sysJob.JobID, s.count)
	// 返回结果，用于记录执行结果
	result := map[string]any{
		"count": s.count,
	}

	// 读取参数值
	var params alarmParams
	err := json.Unmarshal([]byte(sysJob.TargetParams), &params)
	if err != nil {
		return nil, fmt.Errorf("json params err: %v", err)
	}
	// 检查使用率
	numCPU := runtime.NumCPU()
	if params.CPUUseGt > int64(numCPU*100) || params.CPUUseGt < 0 {
		return nil, fmt.Errorf("cpuUseGt must be between 0 and 100 * NumCPU")
	}
	if params.MemUseGt > 100 || params.MemUseGt < 0 {
		return nil, fmt.Errorf("memUseGt must be between 0 and 100")
	}
	if params.DiskUseGt > 100 || params.DiskUseGt < 0 {
		return nil, fmt.Errorf("diskUseGt must be between 0 and 100")
	}

	neList := s.neInfoService.SelectList(neModel.NeInfo{}, true, false)
	for _, neInfo := range neList {
		if neInfo.CreateTime == 0 {
			continue
		}

		// 网元在线状态
		isOnline := parse.Boolean(neInfo.ServerState["online"])
		if !isOnline {
			continue
		}

		// 检查状态
		err := s.serverState(neInfo.ServerState, params.CPUUseGt, params.MemUseGt, params.DiskUseGt)
		if err == nil {
			continue
		}

		neTypeAndId := fmt.Sprintf("%s_%s", neInfo.NeType, neInfo.NeId)
		// 告警ID
		params.AlarmId = fmt.Sprintf("%d%d", constants.ALARM_CMD_CHECK, neInfo.CreateTime)
		// 检查网元告警ID是否唯一
		alarmIdArr := s.alarmService.Find(neDataModel.Alarm{
			NeType:  neInfo.NeType,
			NeId:    neInfo.RmUID,
			AlarmId: params.AlarmId,
		})
		// 告警状态, 存在的需要手动清除
		alarmStatus := ""
		if len(alarmIdArr) > 0 {
			alarmStatus = fmt.Sprint(alarmIdArr[0].AlarmStatus)
		}
		//  活动告警进行清除
		if alarmStatus == "1" {
			clearAlarm, err := s.alarmClear(neInfo, alarmIdArr[0])
			if err != nil {
				result[neTypeAndId] = err.Error()
				continue
			}
			groupID := fmt.Sprintf("%s_%s_%s", wsService.GROUP_ALARM, neInfo.NeType, neInfo.NeId)
			s.wsSendService.ByGroupID(groupID, clearAlarm)
			result[neTypeAndId] = "alarm clear"
			alarmStatus = "" // 标记为未记录再次发起新告警
		}
		// 未记录
		if alarmStatus == "" {
			addInfo := params.AddInfo
			if params.AddInfo != "" {
				params.AddInfo = params.AddInfo + ", " + err.Error()
			} else {
				params.AddInfo = err.Error()
			}
			// 进行新增
			newAlarm, err := s.alarmNew(neInfo, params)
			params.AddInfo = addInfo                       // 恢复附加信息
			triggerCount.Store(neTypeAndId, []time.Time{}) // 重置连续触发次数
			if err != nil {
				result[neTypeAndId] = err.Error()
				continue
			}
			groupID := fmt.Sprintf("%s_%s_%s", wsService.GROUP_ALARM, neInfo.NeType, neInfo.NeId)
			s.wsSendService.ByGroupID(groupID, newAlarm)
			result[neTypeAndId] = "alarm new"
		}
	}

	// 返回结果，用于记录执行结果
	return result, nil
}

// serverState 网元状态
func (s NeAlarmStateCheckCMDProcessor) serverState(state map[string]any, cpuUseGt, memUseGt, diskUseGt int64) error {
	// 网元CPU使用率
	var nfCpuUsage float64 = 0
	// CPU使用率
	var sysCpuUsage float64 = 0
	if state["cpu"] != nil {
		cpu := state["cpu"].(map[string]any)
		v := parse.Number(cpu["sysCpuUsage"])
		sysCpuUsage = float64(v) / 100
		nfv := parse.Number(cpu["nfCpuUsage"])
		nfCpuUsage = float64(nfv) / 100
	}

	// 网元内存使用KB
	var nfMemUsed int64 = 0
	// 内存使用率
	var sysMemUsage float64 = 0
	if state["mem"] != nil {
		mem := state["mem"].(map[string]any)
		v := parse.Number(mem["sysMemUsage"])
		sysMemUsage = float64(v) / 100
		nfMemUsed = parse.Number(mem["nfUsedMem"])
	}

	// 磁盘使用率
	var sysDiskUsage float64 = 0
	if state["disk"] != nil {
		mem := state["disk"].(map[string]any)
		disks := mem["partitionInfo"].([]any)
		sort.Slice(disks, func(i, j int) bool {
			iUsed := parse.Number(disks[i].(map[string]any)["used"])
			jUsed := parse.Number(disks[j].(map[string]any)["used"])
			return iUsed > jUsed
		})
		disk := disks[0].(map[string]any)
		total := parse.Number(disk["total"])
		used := parse.Number(disk["used"])
		sysDiskUsage = (float64(used) / float64(total)) * 100
		sysDiskUsage, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", sysDiskUsage), 64)
	}

	// 插入网元状态记录
	neState := neDataModel.NEState{
		NeType:       fmt.Sprint(state["neType"]),
		NeId:         fmt.Sprint(state["neId"]),
		Version:      fmt.Sprint(state["version"]),
		Capability:   parse.Number(state["capability"]),
		SerialNum:    fmt.Sprint(state["sn"]),
		ExpiryDate:   fmt.Sprint(state["expire"]),
		SysCpuUsage:  sysCpuUsage,
		SysMemUsage:  sysMemUsage,
		SysDiskUsage: sysDiskUsage,
		NfCpuUsage:   nfCpuUsage,
		NfMemUsed:    nfMemUsed,
		CreateTime:   parse.Number(state["refreshTime"]),
	}
	s.neStateService.Insert(neState)
	// 删除网元状态记录7天前
	s.neStateService.DeleteByTime(time.Now().UnixMilli() - 7*24*60*60*1000)
	// 推送ws消息
	groupID := fmt.Sprintf("%s_%s_%s", wsService.GROUP_NE_STATE, neState.NeType, neState.NeId)
	s.wsSendService.ByGroupID(groupID, neState)

	// 检查CPU/Menory/Disk使用率
	warnMsg := []string{}
	if int64(sysCpuUsage) >= cpuUseGt {
		warnMsg = append(warnMsg, fmt.Sprintf("cpu usage %.2f%%", sysCpuUsage))
	}
	if int64(sysMemUsage) >= memUseGt {
		warnMsg = append(warnMsg, fmt.Sprintf("memory usage %.2f%%", sysMemUsage))
	}
	if int64(sysDiskUsage) >= diskUseGt {
		warnMsg = append(warnMsg, fmt.Sprintf("disk usage %.2f%%", sysDiskUsage))
	}
	if len(warnMsg) > 0 {
		currentTime := time.Now()
		neTypeAndId := fmt.Sprintf("%s_%s", neState.NeType, neState.NeId)
		validTimes := []time.Time{}
		if v, ok := triggerCount.Load(neTypeAndId); ok {
			times := v.([]time.Time)
			// 清理过期的记录：10秒前的触发记录不再计入
			for _, t := range times {
				if currentTime.Sub(t) <= triggerWindow {
					validTimes = append(validTimes, t)
				}
			}
			validTimes = append(validTimes, currentTime)
			triggerCount.Store(neTypeAndId, validTimes)
		} else {
			// 事件第一次触发，初始化记录
			validTimes = append(validTimes, currentTime)
			triggerCount.Store(neTypeAndId, validTimes)
		}
		if int64(len(validTimes)) >= triggerMax {
			return fmt.Errorf("greater than %s", strings.Join(warnMsg, ", "))
		}
	}
	return nil
}

// alarmClear 清除告警
func (s NeAlarmStateCheckCMDProcessor) alarmClear(neInfo neModel.NeInfo, v neDataModel.Alarm) (neDataModel.Alarm, error) {
	// 变更告警ID为告警清除ID
	v.AlarmId = fmt.Sprintf("%s%d", v.AlarmCode, v.EventTime.UnixMilli())
	v.AlarmStatus = "0"
	// 告警清除
	clearTime := time.UnixMilli(neInfo.UpdateTime)
	v.ClearType = "1"
	v.ClearTime = &clearTime
	v.ClearUser = "system"
	rows := s.alarmService.Update(v)
	if rows > 0 {
		return v, nil
	}
	return neDataModel.Alarm{}, fmt.Errorf("clear alarm fail")
}

// alarmNew 新增告警
func (s NeAlarmStateCheckCMDProcessor) alarmNew(neInfo neModel.NeInfo, v alarmParams) (neDataModel.Alarm, error) {
	// seq 告警序号
	lastSeq := s.alarmService.FindAlarmSeqLast(neInfo.NeType, neInfo.RmUID)
	lastTime := neInfo.UpdateTime // 网元最后更新时间
	if lastTime < neInfo.CreateTime {
		lastTime = time.Now().UnixMilli()
	}
	alarm := neDataModel.Alarm{
		NeType:            neInfo.NeType,
		NeId:              neInfo.RmUID, // neInfo.NeId,
		NeName:            neInfo.NeName,
		Province:          neInfo.Province,
		PvFlag:            neInfo.PvFlag,
		AlarmSeq:          fmt.Sprint(lastSeq + 1),
		AlarmId:           v.AlarmId,
		AlarmTitle:        v.AlarmTitle,
		AlarmCode:         fmt.Sprint(constants.ALARM_CMD_CHECK),
		EventTime:         time.UnixMilli(lastTime),
		AlarmType:         v.AlarmType,
		OrigSeverity:      v.OrigSeverity,
		PerceivedSeverity: v.OrigSeverity,
		ObjectUid:         neInfo.RmUID,
		ObjectName:        "NE CPU/Menory/Disk",
		ObjectType:        "cmd",
		LocationInfo:      "NE CPU/Menory/Disk: Heartbeat",
		AlarmStatus:       "1", // 活动告警
		SpecificProblem:   v.SpecificProblem,
		SpecificProblemId: v.SpecificProblemID,
		AddInfo:           v.AddInfo,
		Counter:           "0",
		AckState:          "0",
		ClearType:         "0",
	}
	insertId := s.alarmService.InsertAndForword(alarm)
	if insertId != "" {
		alarm.ID = insertId
		return alarm, nil
	}
	return neDataModel.Alarm{}, fmt.Errorf("new alarm fail")
}
