package config

import (
	"context"
	"fmt"
	"github.com/spf13/viper"
	_ "github.com/spf13/viper/remote"
	"github.com/zeromicro/go-zero/zrpc"
	"go.etcd.io/etcd/api/v3/mvccpb"
	clientv3 "go.etcd.io/etcd/client/v3"
	"log"
	"os"
)

const (
	TypeKey  = "CONFIG_TYPE" // 配置文件类型
	TypeEtcd = "etcd"
	TypeEnv  = "env"

	Prefix      = "/youtu/config/" // 配置文件前缀
	EtcdAddrKey = "ETCD_ADDR"
)

type Redis struct {
	Host     string `json:"host"`
	Password string `json:"password"`
}

type AppAccount struct {
	AppId  string `json:"appId"`
	Secret string `json:"secret"`
	Type   string `json:"type"`
}

type Config struct {
	RpcServerConf zrpc.RpcServerConf
	Mode          string
	Mysql         string
	Redis         []Redis
}

func init() {
	etcdAddr := "192.168.0.47:2379"
	envEtcdAddr := os.Getenv(EtcdAddrKey)
	if envEtcdAddr != "" {
		etcdAddr = envEtcdAddr
	}

	viper.SetDefault(EtcdAddrKey, etcdAddr)
	viper.SetDefault(TypeKey, TypeEtcd)
}

func GetConfig(c *Config, serverName string) (err error) {
	err = viper.AddRemoteProvider("etcd3", viper.GetString(EtcdAddrKey), Prefix+serverName+".rpc")
	fmt.Println(Prefix + serverName + ".rpc")
	if err != nil {
		return
	}
	viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv"
	err = viper.ReadRemoteConfig()
	if err != nil {
		return
	}

	// 读取公共配置并合并
	serviceConfig := viper.New()
	err = serviceConfig.AddRemoteProvider("etcd3", viper.GetString(EtcdAddrKey), Prefix+"common.rpc")
	if err != nil {
		log.Fatalf("Failed to read service config: %v", err)
		return err
	}
	serviceConfig.SetConfigType("json")
	if err := serviceConfig.ReadRemoteConfig(); err != nil {
		log.Fatalf("Failed to read service config: %v", err)
	}
	err = viper.MergeConfigMap(serviceConfig.AllSettings())
	if err != nil {
		return err
	}

	err = viper.Unmarshal(&c)
	if err != nil {
		return
	}
	c.RpcServerConf.Name = serverName + ".rpc"
	c.RpcServerConf.Etcd.Key = serverName + ".rpc"
	c.RpcServerConf.ServiceConf.Mode = c.Mode
	return
}

func SetConfig(b, serverName string) (err error) {
	etcdCli, err := clientv3.NewFromURL(viper.GetString(EtcdAddrKey))
	if err != nil {
		return
	}
	_, err = etcdCli.Put(context.Background(), Prefix+serverName+".rpc", b)
	return
}

type WatchKV struct {
	Key   string
	Value []byte
	Type  mvccpb.Event_EventType
}

// EtcdGetOneAndWatch 获取etcd配置并监听对应的key
func EtcdGetOneAndWatch(ctx context.Context, cli *clientv3.Client, key string, ch chan WatchKV) {
	defer close(ch)
	b, err := cli.Get(ctx, key, clientv3.WithPrefix())
	if err != nil {
		return
	}

	for _, kv := range b.Kvs {
		ch <- WatchKV{Key: string(kv.Key), Value: kv.Value}
	}
	resCh := cli.Watch(ctx, key, clientv3.WithPrefix())
	for res := range resCh {
		for _, event := range res.Events {
			ch <- WatchKV{Key: string(event.Kv.Key), Value: event.Kv.Value, Type: event.Type}
		}
	}

	return
}