func (h *confHandler) GetReplication(w http.ResponseWriter, r *http.Request) {
	h.rd.JSON(w, http.StatusOK, h.svr.GetReplicationConfig())
}
@Tags config
@Summary Get replication config.
@Produce json
@Success 200 {object} config.ReplicationConfig
@Router /config/replicate [get]
package api
import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"reflect"
	"strconv"
	"strings"
	"time"
	"github.com/pingcap/errcode"
	"github.com/pingcap/errors"
	"github.com/pingcap/log"
	"github.com/tikv/pd/pkg/apiutil"
	"github.com/tikv/pd/pkg/logutil"
	"github.com/tikv/pd/server"
	"github.com/tikv/pd/server/config"
	"github.com/unrolled/render"
)
type confHandler struct {
	svr *server.Server
	rd  *render.Render
}
func newConfHandler(svr *server.Server, rd *render.Render) *confHandler {
	return &confHandler{
		svr: svr,
		rd:  rd,
	}
}
func (h *confHandler) Get(w http.ResponseWriter, r *http.Request) {
	h.rd.JSON(w, http.StatusOK, h.svr.GetConfig())
}
func (h *confHandler) GetDefault(w http.ResponseWriter, r *http.Request) {
	config := config.NewConfig()
	err := config.Adjust(nil, false)
	if err != nil {
		h.rd.JSON(w, http.StatusInternalServerError, err.Error())
	}
	h.rd.JSON(w, http.StatusOK, config)
}
func (h *confHandler) Post(w http.ResponseWriter, r *http.Request) {
	cfg := h.svr.GetConfig()
	data, err := io.ReadAll(r.Body)
	r.Body.Close()
	if err != nil {
		h.rd.JSON(w, http.StatusInternalServerError, err.Error())
		return
	}
	conf := make(map[string]interface{})
	if err := json.Unmarshal(data, &conf); err != nil {
		h.rd.JSON(w, http.StatusBadRequest, err.Error())
		return
	}
	var ttls int
	if ttlSec := r.URL.Query().Get("ttlSecond"); ttlSec != "" {
		var err error
		ttls, err = strconv.Atoi(ttlSec)
		if err != nil {
			h.rd.JSON(w, http.StatusBadRequest, err.Error())
			return
		}
	}
	if ttls > 0 {
		err := h.svr.SaveTTLConfig(conf, time.Duration(ttls)*time.Second)
		if err != nil {
			h.rd.JSON(w, http.StatusBadRequest, err.Error())
			return
		}
		h.rd.JSON(w, http.StatusOK, "The config is updated.")
		return
	}
	for k, v := range conf {
		if s := strings.Split(k, "."); len(s) > 1 {
			if err := h.updateConfig(cfg, k, v); err != nil {
				h.rd.JSON(w, http.StatusBadRequest, err.Error())
				return
			}
			continue
		}
		key := findTag(reflect.TypeOf(config.Config{}), k)
		if key == "" {
			h.rd.JSON(w, http.StatusBadRequest, fmt.Sprintf("config item %s not found", k))
			return
		}
		if err := h.updateConfig(cfg, key, v); err != nil {
			h.rd.JSON(w, http.StatusBadRequest, err.Error())
			return
		}
	}
	h.rd.JSON(w, http.StatusOK, "The config is updated.")
}
func (h *confHandler) updateConfig(cfg *config.Config, key string, value interface{}) error {
	kp := strings.Split(key, ".")
	switch kp[0] {
	case "schedule":
		return h.updateSchedule(cfg, kp[len(kp)-1], value)
	case "replication":
		return h.updateReplication(cfg, kp[len(kp)-1], value)
	case "replication-mode":
		if len(kp) < 2 {
			return errors.Errorf("cannot update config prefix %s", kp[0])
		}
		return h.updateReplicationModeConfig(cfg, kp[1:], value)
	case "pd-server":
		return h.updatePDServerConfig(cfg, kp[len(kp)-1], value)
	case "log":
		return h.updateLogLevel(kp, value)
	case "cluster-version":
		return h.updateClusterVersion(value)
	case "label-property":  
	}
	return errors.Errorf("config prefix %s not found", kp[0])
}
func findTag(t reflect.Type, tag string) string {
	for i := 0; i < t.NumField(); i++ {
		field := t.Field(i)
		column := field.Tag.Get("json")
		c := strings.Split(column, ",")
		if c[0] == tag {
			return c[0]
		}
		if field.Type.Kind() == reflect.Struct {
			path := findTag(field.Type, tag)
			if path == "" {
				continue
			}
			return field.Tag.Get("json") + "." + path
		}
	}
	return ""
}
func (h *confHandler) updateSchedule(config *config.Config, key string, value interface{}) error {
	data, err := json.Marshal(map[string]interface{}{key: value})
	if err != nil {
		return err
	}
	updated, found, err := h.mergeConfig(&config.Schedule, data)
	if err != nil {
		return err
	}
	if !found {
		return errors.Errorf("config item %s not found", key)
	}
	if updated {
		err = h.svr.SetScheduleConfig(config.Schedule)
	}
	return err
}
func (h *confHandler) updateReplication(config *config.Config, key string, value interface{}) error {
	data, err := json.Marshal(map[string]interface{}{key: value})
	if err != nil {
		return err
	}
	updated, found, err := h.mergeConfig(&config.Replication, data)
	if err != nil {
		return err
	}
	if !found {
		return errors.Errorf("config item %s not found", key)
	}
	if updated {
		err = h.svr.SetReplicationConfig(config.Replication)
	}
	return err
}
func (h *confHandler) updateReplicationModeConfig(config *config.Config, key []string, value interface{}) error {
	cfg := make(map[string]interface{})
	cfg = getConfigMap(cfg, key, value)
	data, err := json.Marshal(cfg)
	if err != nil {
		return err
	}
	updated, found, err := h.mergeConfig(&config.ReplicationMode, data)
	if err != nil {
		return err
	}
	if !found {
		return errors.Errorf("config item %s not found", key)
	}
	if updated {
		err = h.svr.SetReplicationModeConfig(config.ReplicationMode)
	}
	return err
}
func (h *confHandler) updatePDServerConfig(config *config.Config, key string, value interface{}) error {
	data, err := json.Marshal(map[string]interface{}{key: value})
	if err != nil {
		return err
	}
	updated, found, err := h.mergeConfig(&config.PDServerCfg, data)
	if err != nil {
		return err
	}
	if !found {
		return errors.Errorf("config item %s not found", key)
	}
	if updated {
		err = h.svr.SetPDServerConfig(config.PDServerCfg)
	}
	return err
}
func (h *confHandler) updateLogLevel(kp []string, value interface{}) error {
	if len(kp) != 2 || kp[1] != "level" {
		return errors.Errorf("only support changing log level")
	}
	if level, ok := value.(string); ok {
		err := h.svr.SetLogLevel(level)
		if err != nil {
			return err
		}
		log.SetLevel(logutil.StringToZapLogLevel(level))
		return nil
	}
	return errors.Errorf("input value %v is illegal", value)
}
func (h *confHandler) updateClusterVersion(value interface{}) error {
	if version, ok := value.(string); ok {
		err := h.svr.SetClusterVersion(version)
		if err != nil {
			return err
		}
		return nil
	}
	return errors.Errorf("input value %v is illegal", value)
}
func getConfigMap(cfg map[string]interface{}, key []string, value interface{}) map[string]interface{} {
	if len(key) == 1 {
		cfg[key[0]] = value
		return cfg
	}
	subConfig := make(map[string]interface{})
	cfg[key[0]] = getConfigMap(subConfig, key[1:], value)
	return cfg
}
func (h *confHandler) mergeConfig(v interface{}, data []byte) (updated bool, found bool, err error) {
	old, _ := json.Marshal(v)
	if err := json.Unmarshal(data, v); err != nil {
		return false, false, err
	}
	new, _ := json.Marshal(v)
	if !bytes.Equal(old, new) {
		return true, true, nil
	}
	m := make(map[string]interface{})
	if err := json.Unmarshal(data, &m); err != nil {
		return false, false, err
	}
	t := reflect.TypeOf(v).Elem()
	for i := 0; i < t.NumField(); i++ {
		jsonTag := t.Field(i).Tag.Get("json")
		if i := strings.Index(jsonTag, ","); i != -1 {  
			jsonTag = jsonTag[:i]
		}
		if _, ok := m[jsonTag]; ok {
			return false, true, nil
		}
	}
	return false, false, nil
}
func (h *confHandler) GetSchedule(w http.ResponseWriter, r *http.Request) {
	h.rd.JSON(w, http.StatusOK, h.svr.GetScheduleConfig())
}
func (h *confHandler) SetSchedule(w http.ResponseWriter, r *http.Request) {
	config := h.svr.GetScheduleConfig()
	if err := apiutil.ReadJSONRespondError(h.rd, w, r.Body, &config); err != nil {
		return
	}
	if err := h.svr.SetScheduleConfig(*config); err != nil {
		h.rd.JSON(w, http.StatusInternalServerError, err.Error())
		return
	}
	h.rd.JSON(w, http.StatusOK, "The config is updated.")
}