package service

import (
	"fmt"

	"be.ems/src/framework/config"
	"be.ems/src/framework/constants"
	"be.ems/src/framework/utils/parse"
	"github.com/tsmask/go-oam"

	neService "be.ems/src/modules/ne/service"
	neDataModel "be.ems/src/modules/ne_data/model"
	neDataService "be.ems/src/modules/ne_data/service"
	notificationService "be.ems/src/modules/notification/service"
	traceService "be.ems/src/modules/trace/service"
	wsService "be.ems/src/modules/ws/service"
)

// 实例化服务层 Alarm 结构体
var NewAlarm = &Alarm{
	neInfoService:          neService.NewNeInfo,
	wsService:              wsService.NewWSSend,
	alarmService:           neDataService.NewAlarm,
	alarmEventService:      neDataService.NewAlarmEvent,
	alarmLogService:        neDataService.NewAlarmLog,
	alarmForwardLogService: neDataService.NewAlarmForwardLog,
}

// Alarm 消息处理
type Alarm struct {
	neInfoService          *neService.NeInfo
	wsService              *wsService.WSSend
	alarmService           *neDataService.Alarm
	alarmEventService      *neDataService.AlarmEvent
	alarmLogService        *neDataService.AlarmLog
	alarmForwardLogService *neDataService.AlarmForwardLog
}

// Resolve 接收处理
func (s *Alarm) Resolve(a oam.Alarm) error {
	// 是否存在网元
	neInfo := s.neInfoService.FindByRmuid(a.NeUid)
	if neInfo.NeType == "" || neInfo.RmUID != a.NeUid {
		return fmt.Errorf("resolve alarm network element does not exist %s", a.NeUid)
	}

	// seq 告警序号
	lastSeq := neDataService.NewAlarm.FindAlarmSeqLast(neInfo.NeType, neInfo.NeId)

	// 告警信息
	alarm := neDataModel.Alarm{
		NeType:            neInfo.NeType,
		NeId:              neInfo.NeId,
		NeName:            neInfo.NeName,
		Province:          neInfo.Province,
		PvFlag:            neInfo.PvFlag,
		AlarmSeq:          lastSeq + 1,
		AlarmId:           a.AlarmId,
		AlarmTitle:        a.AlarmTitle,
		AlarmCode:         int64(a.AlarmCode),
		EventTime:         a.AlarmTime,
		AlarmType:         a.AlarmType,
		OrigSeverity:      a.PerceivedSeverity,
		PerceivedSeverity: a.PerceivedSeverity,
		ObjectUid:         neInfo.RmUID,
		ObjectName:        neInfo.NeName,
		ObjectType:        neInfo.NeType,
		LocationInfo:      a.LocationInfo,
		AlarmStatus:       a.AlarmStatus,
		SpecificProblem:   a.SpecificProblem,
		SpecificProblemId: a.SpecificProblemID,
		AddInfo:           a.AddInfo,
	}

	// 进行清除
	if a.AlarmStatus == oam.ALARM_STATUS_CLEAR {
		if a.PerceivedSeverity == oam.ALARM_SEVERITY_EVENT {
			if err := s.clearEvent(alarm); err != nil {
				return err
			}
		} else {
			if err := s.clear(alarm); err != nil {
				return err
			}
		}

	}
	// 进行新增
	if a.AlarmStatus == oam.ALARM_STATUS_ACTIVE {
		if a.PerceivedSeverity == oam.ALARM_SEVERITY_EVENT {
			if err := s.addEvent(alarm); err != nil {
				return err
			}
		} else {
			if err := s.add(alarm); err != nil {
				return err
			}
		}
	}

	// 记录日志
	if err := s.saveLog(alarm); err != nil {
		return err
	}
	// 推送
	s.wsService.ByGroupID(fmt.Sprintf("%s_%s_%s", wsService.GROUP_ALARM, neInfo.NeType, neInfo.NeId), alarm)
	// 通知
	go s.notify(neInfo.IP, alarm)
	return nil
}

// saveLog 记录日志
func (s *Alarm) saveLog(alarm neDataModel.Alarm) error {
	alarmLog := neDataModel.AlarmLog{
		NeType:       alarm.NeType,
		NeId:         alarm.NeId,
		AlarmSeq:     alarm.AlarmSeq,
		AlarmId:      alarm.AlarmId,
		AlarmTitle:   alarm.AlarmTitle,
		AlarmCode:    alarm.AlarmCode,
		AlarmStatus:  alarm.AlarmStatus,
		AlarmType:    alarm.AlarmType,
		OrigSeverity: alarm.PerceivedSeverity,
		EventTime:    alarm.EventTime,
	}
	insertId := s.alarmLogService.Insert(alarmLog)
	if insertId <= 0 {
		return fmt.Errorf("save alarm log fail")
	}
	return nil
}

// add 新增告警
func (s *Alarm) add(alarm neDataModel.Alarm) error {
	// 检查网元告警ID是否唯一
	alarmIdArr := s.alarmService.Find(neDataModel.Alarm{
		NeType:  alarm.NeType,
		NeId:    alarm.NeId,
		AlarmId: alarm.AlarmId,
	})
	if len(alarmIdArr) > 0 {
		return fmt.Errorf("already exists alarmId:%s", alarm.AlarmId)
	}
	insertId := s.alarmService.Insert(alarm)
	if insertId > 0 {
		alarm.ID = insertId
		return nil
	}
	return fmt.Errorf("add alarm fail")
}

// clear 清除告警
func (s *Alarm) clear(alarm neDataModel.Alarm) error {
	// 检查网元告警ID是否唯一
	alarmIdArr := s.alarmService.Find(neDataModel.Alarm{
		NeType:  alarm.NeType,
		NeId:    alarm.NeId,
		AlarmId: alarm.AlarmId,
	})
	if len(alarmIdArr) != 1 {
		return fmt.Errorf("not exists alarmId:%s", alarm.AlarmId)
	}

	// 告警清除
	rows, _ := s.alarmService.ClearByIds([]int64{alarmIdArr[0].ID}, alarm.ObjectUid, constants.ALARM_CLEAR_TYPE_AUTO_CLEAR)
	if rows > 0 {
		return nil
	}
	return fmt.Errorf("clear fail alarmId:%s", alarm.AlarmId)
}

// addEvent 新增告警事件
func (s *Alarm) addEvent(alarm neDataModel.Alarm) error {
	// 检查网元告警ID是否唯一
	alarmIdArr := s.alarmEventService.Find(neDataModel.AlarmEvent{
		NeType:  alarm.NeType,
		NeId:    alarm.NeId,
		AlarmId: alarm.AlarmId,
	})
	if len(alarmIdArr) > 0 {
		return fmt.Errorf("event already exists alarmId:%s", alarm.AlarmId)
	}
	// seq 告警序号
	lastSeq := s.alarmEventService.FindAlarmEventSeqLast(alarm.NeType, alarm.NeId)

	alarmEvent := neDataModel.AlarmEvent{
		NeType:            alarm.NeType,
		NeId:              alarm.NeId,
		AlarmSeq:          lastSeq + 1,
		AlarmId:           alarm.AlarmId,
		AlarmTitle:        alarm.AlarmTitle,
		AlarmCode:         alarm.AlarmCode,
		EventTime:         alarm.EventTime,
		ObjectUid:         alarm.ObjectUid,
		ObjectName:        alarm.ObjectName,
		ObjectType:        alarm.ObjectType,
		LocationInfo:      alarm.LocationInfo,
		AlarmStatus:       alarm.AlarmStatus,
		SpecificProblem:   alarm.SpecificProblem,
		SpecificProblemId: alarm.SpecificProblemId,
		AddInfo:           alarm.AddInfo,
	}
	insertId := s.alarmEventService.Insert(alarmEvent)
	if insertId > 0 {
		alarmEvent.ID = insertId
		// 网元重启后，清除活动告警
		if alarm.AlarmCode == constants.ALARM_EVENT_REBOOT {
			rows := s.alarmService.Find(neDataModel.Alarm{
				NeType:      alarm.NeType,
				NeId:        alarm.NeId,
				AlarmStatus: oam.ALARM_STATUS_ACTIVE,
			})
			ids := make([]int64, 0)
			for _, v := range rows {
				ids = append(ids, v.ID)
			}
			s.alarmService.ClearByIds(ids, alarm.ObjectUid, constants.ALARM_CLEAR_TYPE_AUTO_CLEAR)
		}
		// 网元重启后，有跟踪任务的需要重新补发启动任务
		if alarm.AlarmCode == constants.ALARM_EVENT_REBOOT {
			traceService.NewTraceTask.RunUnstopped(alarm.NeType, alarm.NeId)
		}
		return nil
	}
	return fmt.Errorf("event add fail")
}

// clearEvent 清除告警事件
func (s *Alarm) clearEvent(alarm neDataModel.Alarm) error {
	alarmEventService := neDataService.NewAlarmEvent
	// 检查网元告警ID是否唯一
	alarmIdArr := alarmEventService.Find(neDataModel.AlarmEvent{
		NeType:  alarm.NeType,
		NeId:    alarm.NeId,
		AlarmId: alarm.AlarmId,
	})
	if len(alarmIdArr) != 1 {
		return fmt.Errorf("event not exists alarmId:%s", alarm.AlarmId)
	}

	// 告警清除
	rows, _ := s.alarmEventService.ClearByIds([]int64{alarmIdArr[0].ID}, alarm.ObjectUid, constants.ALARM_CLEAR_TYPE_AUTO_CLEAR)
	if rows > 0 {
		return nil
	}
	return fmt.Errorf("event clear fail alarmId:%s", alarm.AlarmId)
}

// notify 通知
func (s *Alarm) notify(neIp string, alarm neDataModel.Alarm) {
	// 邮箱
	emailEnable := parse.Boolean(config.Get("notification.email.enable"))
	if emailEnable {
		emailList := fmt.Sprint(config.Get("notification.email.emailList"))
		emailResult := "Sent Successfully!"
		emailErr := notificationService.EmailAlarm(alarm, neIp)
		if emailErr != nil {
			emailResult = emailErr.Error()
		}
		s.notifyLog(alarm, "EMAIL", emailList, emailResult)
	}

	// 短信
	smscEnable := parse.Boolean(config.Get("notification.smsc.enable"))
	if smscEnable {
		mobileList := fmt.Sprint(config.Get("notification.smsc.mobileList"))
		smscResult := "Sent Successfully!"
		smscErr := notificationService.SMSCAlarm(alarm, neIp)
		if smscErr != nil {
			smscResult = smscErr.Error()
		}
		s.notifyLog(alarm, "SMSC", mobileList, smscResult)
	}
}

// notifyLog 通知日志
func (s *Alarm) notifyLog(alarm neDataModel.Alarm, forwardBy, toUser, result string) error {
	alarmForwardLog := neDataModel.AlarmForwardLog{
		NeType:       alarm.NeType,
		NeId:         alarm.NeId,
		AlarmSeq:     alarm.AlarmSeq,
		AlarmId:      alarm.AlarmId,
		AlarmTitle:   alarm.AlarmTitle,
		AlarmCode:    alarm.AlarmCode,
		AlarmStatus:  alarm.AlarmStatus,
		AlarmType:    alarm.AlarmType,
		OrigSeverity: alarm.OrigSeverity,
		EventTime:    alarm.EventTime,
		Type:         forwardBy,
		Target:       toUser,
		Result:       result,
	}
	// 记录日志
	insertId := s.alarmForwardLogService.Insert(alarmForwardLog)
	if insertId <= 0 {
		return fmt.Errorf("notify alarm log fail")
	}
	return nil
}
