package singleton

import (
	"cmp"
	"fmt"
	"log"
	"slices"
	"sync"
	"time"

	"github.com/nezhahq/nezha/model"
)

const (
	firstNotificationDelay = time.Minute * 15
)

// 通知方式
var (
	NotificationList       map[uint64]map[uint64]*model.Notification // [NotificationGroupID][NotificationID] -> model.Notification
	NotificationIDToGroups map[uint64]map[uint64]struct{}            // [NotificationID] -> NotificationGroupID

	NotificationMap        map[uint64]*model.Notification
	NotificationListSorted []*model.Notification
	NotificationGroup      map[uint64]string // [NotificationGroupID] -> [NotificationGroupName]

	NotificationsLock      sync.RWMutex
	NotificationSortedLock sync.RWMutex
	NotificationGroupLock  sync.RWMutex
)

// InitNotification 初始化 GroupID <-> ID <-> Notification 的映射
func InitNotification() {
	NotificationList = make(map[uint64]map[uint64]*model.Notification)
	NotificationIDToGroups = make(map[uint64]map[uint64]struct{})
	NotificationGroup = make(map[uint64]string)
}

// loadNotifications 从 DB 初始化通知方式相关参数
func loadNotifications() {
	InitNotification()
	NotificationsLock.Lock()

	groupNotifications := make(map[uint64][]uint64)
	var ngn []model.NotificationGroupNotification
	if err := DB.Find(&ngn).Error; err != nil {
		panic(err)
	}

	for _, n := range ngn {
		groupNotifications[n.NotificationGroupID] = append(groupNotifications[n.NotificationGroupID], n.NotificationID)
	}

	if err := DB.Find(&NotificationListSorted).Error; err != nil {
		panic(err)
	}

	NotificationMap = make(map[uint64]*model.Notification, len(NotificationListSorted))
	for i := range NotificationListSorted {
		NotificationMap[NotificationListSorted[i].ID] = NotificationListSorted[i]
	}

	for gid, nids := range groupNotifications {
		NotificationList[gid] = make(map[uint64]*model.Notification)
		for _, nid := range nids {
			if n, ok := NotificationMap[nid]; ok {
				NotificationList[gid][n.ID] = n

				if NotificationIDToGroups[n.ID] == nil {
					NotificationIDToGroups[n.ID] = make(map[uint64]struct{})
				}

				NotificationIDToGroups[n.ID][gid] = struct{}{}
			}
		}
	}

	NotificationsLock.Unlock()
}

func UpdateNotificationList() {
	NotificationsLock.RLock()
	defer NotificationsLock.RUnlock()

	NotificationSortedLock.Lock()
	defer NotificationSortedLock.Unlock()

	NotificationListSorted = make([]*model.Notification, 0, len(NotificationMap))
	for _, n := range NotificationMap {
		NotificationListSorted = append(NotificationListSorted, n)
	}
	slices.SortFunc(NotificationListSorted, func(a, b *model.Notification) int {
		return cmp.Compare(a.ID, b.ID)
	})
}

// OnRefreshOrAddNotificationGroup 刷新通知方式组相关参数
func OnRefreshOrAddNotificationGroup(ng *model.NotificationGroup, ngn []uint64) {
	NotificationsLock.Lock()
	defer NotificationsLock.Unlock()

	NotificationGroupLock.Lock()
	defer NotificationGroupLock.Unlock()
	var isEdit bool
	if _, ok := NotificationGroup[ng.ID]; ok {
		isEdit = true
	}

	if !isEdit {
		AddNotificationGroupToList(ng, ngn)
	} else {
		UpdateNotificationGroupInList(ng, ngn)
	}
}

// AddNotificationGroupToList 添加通知方式组到map中
func AddNotificationGroupToList(ng *model.NotificationGroup, ngn []uint64) {
	NotificationGroup[ng.ID] = ng.Name

	NotificationList[ng.ID] = make(map[uint64]*model.Notification, len(ngn))

	for _, n := range ngn {
		if NotificationIDToGroups[n] == nil {
			NotificationIDToGroups[n] = make(map[uint64]struct{})
		}
		NotificationIDToGroups[n][ng.ID] = struct{}{}
		NotificationList[ng.ID][n] = NotificationMap[n]
	}
}

// UpdateNotificationGroupInList 在 map 中更新通知方式组
func UpdateNotificationGroupInList(ng *model.NotificationGroup, ngn []uint64) {
	NotificationGroup[ng.ID] = ng.Name

	oldList := make(map[uint64]struct{})
	for nid := range NotificationList[ng.ID] {
		oldList[nid] = struct{}{}
	}

	NotificationList[ng.ID] = make(map[uint64]*model.Notification)
	for _, nid := range ngn {
		NotificationList[ng.ID][nid] = NotificationMap[nid]
		if NotificationIDToGroups[nid] == nil {
			NotificationIDToGroups[nid] = make(map[uint64]struct{})
		}
		NotificationIDToGroups[nid][ng.ID] = struct{}{}
	}

	for oldID := range oldList {
		if _, ok := NotificationList[ng.ID][oldID]; !ok {
			delete(NotificationIDToGroups[oldID], ng.ID)
			if len(NotificationIDToGroups[oldID]) == 0 {
				delete(NotificationIDToGroups, oldID)
			}
		}
	}
}

// UpdateNotificationGroupInList 删除通知方式组
func OnDeleteNotificationGroup(gids []uint64) {
	NotificationsLock.Lock()
	defer NotificationsLock.Unlock()

	for _, gid := range gids {
		delete(NotificationGroup, gid)
		delete(NotificationList, gid)
	}
}

// OnRefreshOrAddNotification 刷新通知方式相关参数
func OnRefreshOrAddNotification(n *model.Notification) {
	NotificationsLock.Lock()
	defer NotificationsLock.Unlock()

	var isEdit bool
	_, ok := NotificationMap[n.ID]
	if ok {
		isEdit = true
	}
	if !isEdit {
		AddNotificationToList(n)
	} else {
		UpdateNotificationInList(n)
	}
}

// AddNotificationToList 添加通知方式到map中
func AddNotificationToList(n *model.Notification) {
	NotificationMap[n.ID] = n
}

// UpdateNotificationInList 在 map 中更新通知方式
func UpdateNotificationInList(n *model.Notification) {
	NotificationMap[n.ID] = n
	// 如果已经与通知组有绑定关系,更新
	if gids, ok := NotificationIDToGroups[n.ID]; ok {
		for gid := range gids {
			NotificationList[gid][n.ID] = n
		}
	}
}

// OnDeleteNotification 在map和表中删除通知方式
func OnDeleteNotification(id []uint64) {
	NotificationsLock.Lock()
	defer NotificationsLock.Unlock()

	for _, i := range id {
		delete(NotificationMap, i)
		// 如果绑定了通知组才删除
		if gids, ok := NotificationIDToGroups[i]; ok {
			for gid := range gids {
				delete(NotificationList[gid], i)
				delete(NotificationIDToGroups, i)
			}
		}
	}
}

func UnMuteNotification(notificationGroupID uint64, muteLabel *string) {
	fullMuteLabel := *NotificationMuteLabel.AppendNotificationGroupName(muteLabel, notificationGroupID)
	Cache.Delete(fullMuteLabel)
}

// SendNotification 向指定的通知方式组的所有通知方式发送通知
func SendNotification(notificationGroupID uint64, desc string, muteLabel *string, ext ...*model.Server) {
	if muteLabel != nil {
		// 将通知方式组名称加入静音标志
		muteLabel := *NotificationMuteLabel.AppendNotificationGroupName(muteLabel, notificationGroupID)
		// 通知防骚扰策略
		var flag bool
		if cacheN, has := Cache.Get(muteLabel); has {
			nHistory := cacheN.(NotificationHistory)
			// 每次提醒都增加一倍等待时间,最后每天最多提醒一次
			if time.Now().After(nHistory.Until) {
				flag = true
				nHistory.Duration *= 2
				if nHistory.Duration > time.Hour*24 {
					nHistory.Duration = time.Hour * 24
				}
				nHistory.Until = time.Now().Add(nHistory.Duration)
				// 缓存有效期加 10 分钟
				Cache.Set(muteLabel, nHistory, nHistory.Duration+time.Minute*10)
			}
		} else {
			// 新提醒直接通知
			flag = true
			Cache.Set(muteLabel, NotificationHistory{
				Duration: firstNotificationDelay,
				Until:    time.Now().Add(firstNotificationDelay),
			}, firstNotificationDelay+time.Minute*10)
		}

		if !flag {
			if Conf.Debug {
				log.Println("NEZHA>> 静音的重复通知:", desc, muteLabel)
			}
			return
		}
	}
	// 向该通知方式组的所有通知方式发出通知
	NotificationsLock.RLock()
	defer NotificationsLock.RUnlock()
	for _, n := range NotificationList[notificationGroupID] {
		log.Println("NEZHA>> 尝试通知", n.Name)
	}
	for _, n := range NotificationList[notificationGroupID] {
		ns := model.NotificationServerBundle{
			Notification: n,
			Server:       nil,
			Loc:          Loc,
		}
		if len(ext) > 0 {
			ns.Server = ext[0]
		}
		if err := ns.Send(desc); err != nil {
			log.Println("NEZHA>> 向 ", n.Name, " 发送通知失败:", err)
		} else {
			log.Println("NEZHA>> 向 ", n.Name, " 发送通知成功:")
		}
	}
}

type _NotificationMuteLabel struct{}

var NotificationMuteLabel _NotificationMuteLabel

func (_NotificationMuteLabel) IPChanged(serverId uint64) *string {
	label := fmt.Sprintf("bf::ic-%d", serverId)
	return &label
}

func (_NotificationMuteLabel) ServerIncident(alertId uint64, serverId uint64) *string {
	label := fmt.Sprintf("bf::sei-%d-%d", alertId, serverId)
	return &label
}

func (_NotificationMuteLabel) ServerIncidentResolved(alertId uint64, serverId uint64) *string {
	label := fmt.Sprintf("bf::seir-%d-%d", alertId, serverId)
	return &label
}

func (_NotificationMuteLabel) AppendNotificationGroupName(label *string, notificationGroupID uint64) *string {
	NotificationGroupLock.RLock()
	defer NotificationGroupLock.RUnlock()
	newLabel := fmt.Sprintf("%s:%s", *label, NotificationGroup[notificationGroupID])
	return &newLabel
}

func (_NotificationMuteLabel) ServiceLatencyMin(serviceId uint64) *string {
	label := fmt.Sprintf("bf::sln-%d", serviceId)
	return &label
}

func (_NotificationMuteLabel) ServiceLatencyMax(serviceId uint64) *string {
	label := fmt.Sprintf("bf::slm-%d", serviceId)
	return &label
}

func (_NotificationMuteLabel) ServiceStateChanged(serviceId uint64) *string {
	label := fmt.Sprintf("bf::ssc-%d", serviceId)
	return &label
}

func (_NotificationMuteLabel) ServiceTLS(serviceId uint64, extraInfo string) *string {
	label := fmt.Sprintf("bf::stls-%d-%s", serviceId, extraInfo)
	return &label
}