// Package nacos 客户端
package nacos

import (
	"context"
	"fmt"
	"log" // com里面的log 用系统的log 免得循环import
	"os"
	"strings"
	"sync"
	"time"

	"github.com/alibaba/sentinel-golang/logging"

	"gitlab.galaxy-immi.com/Backend-group/go-com/event"

	"google.golang.org/grpc/balancer/roundrobin"

	"github.com/nacos-group/nacos-sdk-go/v2/clients/config_client"
	"github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client"
	"github.com/opentracing/opentracing-go"
	"github.com/uber/jaeger-client-go"
	jaegercfg "github.com/uber/jaeger-client-go/config"
	jaegerlog "github.com/uber/jaeger-client-go/log"
	"gitlab.galaxy-immi.com/Backend-group/go-com/tool"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"gopkg.in/yaml.v2"

	"github.com/nacos-group/nacos-sdk-go/v2/clients"
	"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
	"github.com/nacos-group/nacos-sdk-go/v2/vo"

	"gitlab.galaxy-immi.com/Backend-group/go-com/third/discovery"
	uyaml "gitlab.galaxy-immi.com/Backend-group/go-com/yaml"
)

var y uyaml.YConfig

var sc []constant.ServerConfig
var cc constant.ClientConfig
var NamingClient naming_client.INamingClient

func init() {
	y = uyaml.GetConf()

	// create ServerConfig
	sc = []constant.ServerConfig{
		*constant.NewServerConfig(y.Nacos.Host, 8848, constant.WithContextPath("/nacos")),
	}
	tmpDir := os.TempDir()
	// create ClientConfig
	cc = *constant.NewClientConfig(
		constant.WithUpdateCacheWhenEmpty(true), // fix: 上下线服务的时候不生效问题，缓存没有更新，必须开启这个选项
		constant.WithNamespaceId(""),
		constant.WithTimeoutMs(5000),
		constant.WithNotLoadCacheAtStart(true),
		constant.WithLogDir(tmpDir+"/log/"+tool.I32ToS(int32(os.Getpid()))),
		constant.WithCacheDir(tmpDir+"/nacos/cache"),
		constant.WithLogLevel("debug"),
		constant.WithUsername(y.Nacos.Name),
		constant.WithPassword(y.Nacos.Pass),
	)

	namingClient, err := clients.NewNamingClient(vo.NacosClientParam{
		ClientConfig:  &cc,
		ServerConfigs: sc,
	})
	if err != nil {
		log.Fatal("clients.NewNamingClient")
	}
	NamingClient = namingClient

	Get()

	listener := vo.ConfigParam{
		DataId: y.Nacos.DataId,
		Group:  y.Nacos.Group,
		OnChange: func(namespace, group, dataId, data string) {
			if env := os.Getenv("NACOS_DEBUG"); strings.ToLower(env) == "on" {
				logging.Info("[Nacos] Receive listened property", "namespace", namespace, "group", group, "dataId", dataId, "data", data)
			}
			SetConfig(data)
			// 配置更新广播
			event.GetBroadcaster(event.ConfigChangeEvent).Send("config change")
		},
	}

	ConfigClient.ListenConfig(listener)
}

var GlobalConfig Config
var ConfigClient config_client.IConfigClient
var once = sync.Once{}

// 会实时更新 和nacos同步
func GetConfig() Config {
	return GlobalConfig
}

// 这个不会实时更新
func Get() Config {
	// create config client
	once.Do(func() {
		println("New config client")
		client, err := clients.NewConfigClient(
			vo.NacosClientParam{
				ClientConfig:  &cc,
				ServerConfigs: sc,
			},
		)

		if err != nil {
			log.Fatal("链接nacos错误...")
		}
		ConfigClient = client
	})

	// get config
	content, err := ConfigClient.GetConfig(vo.ConfigParam{
		DataId: y.Nacos.DataId,
		Group:  y.Nacos.Group,
	})
	if err != nil {
		// 默认的配置
		// config key=dataId+group+namespaceId
		// _, err = client.PublishConfig(vo.ConfigParam{
		//	DataId:  y.Nacos.DataId,
		//	Group:   y.Nacos.Group,
		//	Content: DefaultConfig,
		// })
		//
		// content, err = client.GetConfig(vo.ConfigParam{
		//	DataId: y.Nacos.DataId,
		//	Group:  y.Nacos.Group,
		// })
		//
		// if err != nil {
		//	log.Fatalf("PublishConfig err:%+v \n", err)
		// }
	}
	// fmt.Printf("%s\n", content)
	SetConfig(content)

	// Listen config change,key=dataId+group+namespaceId.
	// err = client.ListenConfig(vo.ConfigParam{
	//	DataId: y.Nacos.DataId,
	//	Group:  y.Nacos.Group,
	//	OnChange: func(namespace, group, dataId, data string) {
	//		fmt.Println("config changed group:" + group + ", dataId:" + dataId + ", content:" + data)
	//	},
	// })

	return GlobalConfig
}

func SetConfig(content string) {
	err := yaml.Unmarshal([]byte(content), &GlobalConfig)
	if err != nil {
		fmt.Println(err)
	}
}

// UnRegisterService 程序退出的时候取消注册服务
func UnRegisterService(ip string, port uint64, serviceName string) {
	client, err := clients.NewNamingClient(
		vo.NacosClientParam{
			ClientConfig:  &cc,
			ServerConfigs: sc,
		},
	)
	if err != nil {
		panic(err)
	}
	deRegisterServiceInstance(client, vo.DeregisterInstanceParam{
		Ip:          ip,
		Port:        port,
		ServiceName: serviceName,
		GroupName:   constant.DEFAULT_GROUP,
	})
}

// 服务注册
func RegisterService(ip string, port uint64, serviceName string) {
	// create naming client
	client, err := clients.NewNamingClient(
		vo.NacosClientParam{
			ClientConfig:  &cc,
			ServerConfigs: sc,
		},
	)

	if err != nil {
		panic(err)
	}

	// 增加 K8S 服务发现能力
	name, _, _ := strings.Cut(serviceName, `-`)
	name = strings.ToUpper(name)
	k8sIp := os.Getenv(name + `_SVC_SERVICE_HOST`)
	if k8sIp != `` {
		ip = k8sIp
	}

	k8sPort := os.Getenv(name + `_SVC_SERVICE_PORT`)
	if k8sPort != `` {
		port = tool.SToUI64(k8sPort)
	}

	// Register
	registerServiceInstance(client, vo.RegisterInstanceParam{
		Ip:          ip,
		Port:        port,
		ServiceName: serviceName,
		GroupName:   y.Nacos.Group,
		ClusterName: y.Nacos.ClusterName,
		Weight:      10,
		Enable:      true,
		Healthy:     true,
		Ephemeral:   true,
		Metadata:    map[string]string{"idc": "shanghai"},
	})

	// go func() {
	//	for {
	//		svc, err := client.GetService(vo.GetServiceParam{
	//			Clusters:    []string{y.Nacos.ClusterName},
	//			ServiceName: serviceName,
	//			GroupName:   y.Nacos.Group,
	//		})
	//		if err != nil {
	//			panic(err)
	//		}
	//
	//		var isExist = false
	//		for _, host := range svc.Hosts {
	//			if host.Ip == ip && host.Port == port {
	//				isExist = true
	//				break
	//			}
	//		}
	//
	//		if !isExist {
	//			registerServiceInstance(client, vo.RegisterInstanceParam{
	//				Ip:          ip,
	//				Port:        port,
	//				ServiceName: serviceName,
	//				GroupName:   y.Nacos.Group,
	//				ClusterName: y.Nacos.ClusterName,
	//				Weight:      10,
	//				Enable:      true,
	//				Healthy:     true,
	//				Ephemeral:   true,
	//				Metadata:    map[string]string{"idc": "shanghai"},
	//			})
	//		}
	//
	//		time.Sleep(time.Second * 10)
	//	}
	// }()

	// DeRegister
	// deRegisterServiceInstance(client, vo.DeregisterInstanceParam{
	//	Ip:          "10.0.0.10",
	//	Port:        8848,
	//	ServiceName: "demo.go",
	//	GroupName:   "group-a",
	//	Cluster:     "cluster-a",
	//	Ephemeral:   true, //it must be true
	// })

	time.Sleep(1 * time.Second)

	// BatchRegister
	// batchRegisterServiceInstance(client, vo.BatchRegisterInstanceParam{
	//	ServiceName: "demo.go",
	//	GroupName:   "group-a",
	//	Instances: []vo.RegisterInstanceParam{{
	//		Ip:          "10.0.0.10",
	//		Port:        8848,
	//		Weight:      10,
	//		Enable:      true,
	//		Healthy:     true,
	//		Ephemeral:   true,
	//		ClusterName: "cluster-a",
	//		Metadata:    map[string]string{"idc": "shanghai"},
	//	}, {
	//		Ip:          "10.0.0.12",
	//		Port:        8848,
	//		Weight:      7,
	//		Enable:      true,
	//		Healthy:     true,
	//		Ephemeral:   true,
	//		ClusterName: "cluster-a",
	//		Metadata:    map[string]string{"idc": "shanghai"},
	//	}},
	// })

	// Get service with serviceName, groupName , clusters
	// getService(client, vo.GetServiceParam{
	//	ServiceName: "demo.go",
	//	GroupName:   y.Nacos.Group,
	//	Clusters:    []string{"cluster-a"},
	// })

	// SelectAllInstance
	// GroupName=DEFAULT_GROUP
	// selectAllInstances(client, vo.SelectAllInstancesParam{
	//	ServiceName: "demo.go",
	//	GroupName:   y.Nacos.Group,
	//	Clusters:    []string{"cluster-a"},
	// })

	// SelectInstances only return the instances of healthy=${HealthyOnly},enable=true and weight>0
	// ClusterName=DEFAULT,GroupName=DEFAULT_GROUP
	// selectInstances(client, vo.SelectInstancesParam{
	//	ServiceName: "demo.go",
	//	GroupName:   y.Nacos.Group,
	//	Clusters:    []string{"cluster-a"},
	//	HealthyOnly: true,
	// })

	// SelectOneHealthyInstance return one instance by WRR strategy for load balance
	// And the instance should be health=true,enable=true and weight>0
	// ClusterName=DEFAULT,GroupName=DEFAULT_GROUP
	// selectOneHealthyInstance(client, vo.SelectOneHealthInstanceParam{
	//	ServiceName: "demo.go",
	//	GroupName:   "group-a",
	//	Clusters:    []string{"cluster-a"},
	// })

	// Subscribe key=serviceName+groupName+cluster
	// Note:We call add multiple SubscribeCallback with the same key.
	// subscribeParam := &vo.SubscribeParam{
	//	ServiceName: "demo.go",
	//	GroupName:   "group-a",
	//	SubscribeCallback: func(services []model.Instance, err error) {
	//		fmt.Printf("callback return services:%s \n\n", util.ToJsonString(services))
	//	},
	// }
	// subscribe(client, subscribeParam)

	// updateServiceInstance(client, vo.UpdateInstanceParam{
	//	Ip:          "10.0.0.11", //update ip
	//	Port:        8848,
	//	ServiceName: "demo.go",
	//	GroupName:   "group-a",
	//	ClusterName: "cluster-a",
	//	Weight:      10,
	//	Enable:      true,
	//	Healthy:     true,
	//	Ephemeral:   true,
	//	Metadata:    map[string]string{"idc": "beijing1"}, //update metadata
	// })

	// UnSubscribe
	// unSubscribe(client, subscribeParam)

	// GeAllService will get the list of service name
	// NameSpace default value is public.If the client set the namespaceId, NameSpace will use it.
	// GroupName default value is DEFAULT_GROUP
	getAllService(client, vo.GetAllServiceInfoParam{
		GroupName: y.Nacos.Group,
		PageNo:    1,
		PageSize:  10,
	})
}

const (
	mb = 1024 * 1024
)

// GetGRPC 获取grpc服务的链接，供后续调用使用
func GetGRPC(ctx context.Context, serviceName string) (*grpc.ClientConn, error) {
	if os.Getenv(`release`) == `galaxy-develop` {
		return ETCDGetGRPC(ctx, serviceName)
	}
	return grpc.DialContext(
		ctx,
		fmt.Sprintf(`service:///%s.GRPC`, serviceName),
		grpc.WithResolvers(
			discovery.NewBuilder(
				NamingClient,
				discovery.GroupName(y.Nacos.Group),
				discovery.ClusterName(y.Nacos.ClusterName),
			),
		),
		grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"loadBalancingPolicy": "%s"}`, roundrobin.Name)),
		grpc.WithCredentialsBundle(insecure.NewBundle()),
		grpc.WithUnaryInterceptor(tool.ClientInterceptor),
		// fix: 设置最大的报文长度,20mb
		grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(20*mb)),
	)
}

// ETCDGetGRPC 获取grpc服务的链接，供后续调用使用
func ETCDGetGRPC(ctx context.Context, serviceName string) (*grpc.ClientConn, error) {
	name, _, _ := strings.Cut(serviceName, `-`)
	name = strings.ToLower(name)
	ns := os.Getenv(`ns`)
	if ns == `` {
		ns = `galaxy-develop`
	}

	host := name + `-svc.` + ns + `.svc.cluster.local`
	port := strings.ToUpper(name) + `_SVC_SERVICE_PORT`

	return grpc.DialContext(
		ctx,
		fmt.Sprintf(`%s:%s`, host, os.Getenv(port)),
		grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"loadBalancingPolicy": "%s"}`, roundrobin.Name)),
		grpc.WithCredentialsBundle(insecure.NewBundle()),
		grpc.WithUnaryInterceptor(tool.ClientInterceptor),
		// fix: 设置最大的报文长度,20mb
		grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(20*mb)),
	)
}

// InitJaeger 初始化Jaeger
func InitJaeger(serviceName string) {
	conf := GetConfig()

	cfg := jaegercfg.Configuration{
		ServiceName: conf.Env + `:` + serviceName,
		Sampler:     &jaegercfg.SamplerConfig{Type: jaeger.SamplerTypeConst, Param: 1},
		Reporter:    &jaegercfg.ReporterConfig{CollectorEndpoint: conf.Jaeger.Endpoint},
	}

	jLogger := jaegerlog.StdLogger
	tracer, _, err := cfg.NewTracer(
		jaegercfg.Logger(jLogger),
	)
	if err != nil {
		panic(err)
	}

	opentracing.SetGlobalTracer(tracer)
}
