package discover

import (
	"common/db/etcd"
	"common/discover/common"
	"common/log"
	"common/utils"
	"context"
	"fmt"
	"go.etcd.io/etcd/api/v3/mvccpb"
	clientv3 "go.etcd.io/etcd/client/v3"
	"strconv"
	"strings"
	"sync"
)

var (
	wg         = &sync.WaitGroup{}
	listenerMU = &sync.RWMutex{}
	listener   = make(map[common.ListenerType][]func(data any))
	stopFunc   context.CancelFunc
)

// RegisterListener 注册服务变动监听
func RegisterListener(t common.ListenerType, cb func(data any)) {
	listenerMU.Lock()
	defer listenerMU.Unlock()
	arr := listener[t]
	if arr == nil {
		arr = make([]func(data any), 0)
	}
	arr = append(arr, cb)
	listener[t] = arr
}

// 根据类型触发回调
func onCBByType(t common.ListenerType, data any) {
	listenerMU.RLock()
	defer listenerMU.RUnlock()
	for _, f := range listener[t] {
		f(data)
	}
}

func Listen() {
	var stopCtx context.Context
	stopCtx, stopFunc = context.WithCancel(context.Background())
	wg.Add(1)
	go func() {
		defer wg.Done()
		log.Infof(fmt.Sprintf("Discover start listen..."))
		// 服务
		serviceAll, _ := etcd.GetClient().Get(common.KeyDiscoverService, clientv3.WithPrefix())
		for _, kv := range serviceAll.Kvs {
			onServerChange(clientv3.EventTypePut, string(kv.Key), string(kv.Value))
		}
		chService := etcd.GetClient().Watch(common.KeyDiscoverService, clientv3.WithPrefix(), clientv3.WithRev(serviceAll.Header.Revision+1))
		// 副本
		instanceAll, _ := etcd.GetClient().Get(common.KeyDiscoverInstance, clientv3.WithPrefix())
		for _, kv := range instanceAll.Kvs {
			onInstanceChange(clientv3.EventTypePut, string(kv.Key), string(kv.Value), nil)
		}
		chInstance := etcd.GetClient().Watch(common.KeyDiscoverScene, clientv3.WithPrefix(), clientv3.WithRev(instanceAll.Header.Revision+1), clientv3.WithPrevKV())
		for {
			select {
			case msg := <-chService:
				for _, event := range msg.Events {
					onServerChange(event.Type, string(event.Kv.Key), string(event.Kv.Value))
				}
			case msg := <-chInstance:
				for _, event := range msg.Events {
					onInstanceChange(event.Type, string(event.Kv.Key), string(event.Kv.Value), event.PrevKv)
				}
			case <-stopCtx.Done():
				return
			}
		}
	}()
}

// 服务发生变化
func onServerChange(t mvccpb.Event_EventType, key, value string) {
	split := strings.Split(key, "/")
	if len(split) != 4 {
		return
	}
	switch t {
	case clientv3.EventTypePut:
		onCBByType(common.ListenerTypeNewServer, &common.ServiceProvider{
			Target: common.KeyDiscoverService + "/" + split[2],
			SID:    utils.StringToInt64(split[3]),
			Addr:   value,
		})
	case clientv3.EventTypeDelete:
		onCBByType(common.ListenerTypeCloseServer, &common.ServiceProvider{
			Target: common.KeyDiscoverService + "/" + split[2],
			SID:    utils.StringToInt64(split[3]),
		})
	}
}

// 副本发生变化
func onInstanceChange(t mvccpb.Event_EventType, key, value string, preKv *mvccpb.KeyValue) {
	split := strings.Split(key, "/")
	if len(split) != 4 {
		return
	}
	instanceID, _ := strconv.Atoi(split[2])
	switch t {
	case clientv3.EventTypePut:
		onCBByType(common.ListenerTypeNewInstance, &common.InstanceProvider{
			InstanceID: instanceID,
			UniqueNo:   utils.StringToInt64(split[3]),
			SID:        value,
		})
	case clientv3.EventTypeDelete:
		onCBByType(common.ListenerTypeCloseInstance, &common.InstanceProvider{
			InstanceID: instanceID,
			UniqueNo:   utils.StringToInt64(split[3]),
			SID:        string(preKv.Value),
		})
	}
}

func Close() {
	if stopFunc != nil {
		stopFunc()
		wg.Wait()
	}
}
