package node_check

import (
	"context"
	"git.quantgroup.cn/DevOps/enoch/service/dingding"
	"git.quantgroup.cn/DevOps/enoch/service/registry"
	"github.com/vrg0/go-common/ding"
	"github.com/vrg0/go-common/logger"
	"net/http"
	"os"
	"sync"
	"time"
)

const (
	Passing  = "passing"
	Critical = "critical"
)

var HandlerMap = new(sync.Map)
var HttpGetRetryCount = 3
var HttpTimeOut = time.Second * 10
var IgnoreServiceMap = make(map[string]struct{})

func init() {
	//TODO 灵活配置
	HandlerMap.Store("aaaa", "http://www.baidasdfasdfasdfasdfu.com/")
	HandlerMap.Store("heimdallr", "http://172.20.6.33:8989/service-down")
	//	HandlerMap.Store("aaaa", "http://172.20.6.33:8989/service-down")

	//TODO 灵活配置
	IgnoreServiceMap["vcc-talos"] = struct{}{}
	IgnoreServiceMap["aaaa"] = struct{}{}
}

func httpGet(url string, timeout time.Duration) (*http.Response, error) {
	ctx, cancel := context.WithCancel(context.TODO())

	req, e := http.NewRequest("GET", url, nil)
	if e != nil {
		return nil, e
	} else {
		req = req.WithContext(ctx)
	}

	_ = time.AfterFunc(timeout, func() {
		cancel()
	})

	return http.DefaultClient.Do(req)
}

func handler(serviceName string) {
	if url, ok := HandlerMap.Load(serviceName); ok {
		for i := 0; i < HttpGetRetryCount; i++ {
			if resp, e := httpGet(url.(string), HttpTimeOut); e != nil {
				logger.Error(" handler service: ", serviceName, " ", e)
			} else {
				logger.Info(" handler service: ", serviceName, " ", resp.StatusCode)
				break
			}
		}
	} else {
		logger.Info(" handler service: ", serviceName, " ", "not found handler hook api")
	}
}

type watch struct{}

func (w watch) DeleteService(serviceName string) {
	//pass
	servicesStatusLock.Lock()
	defer servicesStatusLock.Unlock()

	delete(servicesStatus, serviceName)
}

func serviceStatus(service *registry.Service) bool {
	for _, node := range service.NodeMap {
		if node.Status == Passing {
			return true
		}
	}

	return false
}

func serviceStr(service *registry.Service) string {
	rtn := service.Name + " "

	for _, node := range service.NodeMap {
		rtn += node.Id + ":" + node.Status + " "
	}

	return rtn
}

func (w watch) UpdateNodes(service *registry.Service) {
	servicesStatusLock.Lock()
	defer servicesStatusLock.Unlock()

	//单个节点挂了告警
	if oldService, ok := servicesStatus[service.Name]; ok {
		for _, node := range service.NodeMap {
			if oldNode, ok := oldService.NodeMap[node.Id]; ok {
				if oldNode.Status == Passing && node.Status == Critical {
					logger.Warn(service.Name, " ", node.Id, "---!!!node critical!!!---")
					if _, ok := IgnoreServiceMap[service.Name]; !ok {
						_ = ding.SendText(service.Name+" "+node.Id+" "+"---!!!node critical!!!---", dingding.DefaultDingURL...)
					}
				}
			}
		}
	}

	//整个服务挂了告警
	//如果 服务存在，并且服务的old状态为passing，并且服务的now状态为critical，则报警，否贼记录服务状态
	serviceString := serviceStr(service)
	if oldService, ok := servicesStatus[service.Name]; ok && serviceStatus(oldService) && !serviceStatus(service) {
		logger.Warn(serviceString, "---!!!service critical!!!---")
		if _, ok := IgnoreServiceMap[service.Name]; !ok {
			_ = ding.SendText(serviceString+"---!!!service critical!!!---", dingding.DefaultDingURL...)
		}
		handler(service.Name)
	} else {
		logger.Info(serviceString)
	}

	//更新服务状态
	//深拷贝对象
	newService := registry.NewService(service.Name)
	for kk, vv := range service.NodeMap {
		newNode := registry.Node{
			ServiceName: vv.ServiceName,
			Id:          vv.Id,
			Port:        vv.Port,
			Address:     vv.Address,
			Status:      vv.Status,
			Meta:        make(map[string]string),
		}
		for x, y := range vv.Meta {
			newNode.Meta[x] = y
		}
		newService.NodeMap[kk] = &newNode
	}

	servicesStatus[service.Name] = newService
}

func (w watch) AddNode(node *registry.Node) {
	//pass
}

func (w watch) DelNode(node *registry.Node) {
	//pass
}

var (
	servicesStatus     = make(map[string]*registry.Service)
	servicesStatusLock = new(sync.Mutex)
)

func InitServiceStatus() {
	servicesStatus = registry.GetServiceMap()
}

func NodeCheck() {
	defer func() {
		/*
			if e := recover(); e != nil {
				logger.Error("node check panic: ", e)
				_ = ding.SendText("node check panic!", dingding.DefaultDingURL...)
				time.Sleep(time.Second * 1)
				NodeCheck()
			}
		*/
	}()

	//注册器初始化
	dc := "3c"
	cluster := []string{"172.30.12.2:8500", "172.30.12.3:8500", "172.30.12.4:8500"}
	if e := registry.Init("consul", map[string]interface{}{"dc": dc, "cluster": cluster}); e != nil {
		logger.Info("registry init error:", e)
		os.Exit(-1)
	}
	time.Sleep(time.Second * 1)

	//服务状态初始化
	InitServiceStatus()

	//设置观察者
	if e := registry.SetObserver("watch", &watch{}); e != nil {
		logger.Info("set observer error:", e)
		os.Exit(-1)
	}

	select {}
}
