package controllers

import (
	pingcapv1alpha1 "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
	operatorconfig "github.com/pingcap/tidb-operator/pkg/apis/util/config"
	configv1alpha1 "github.com/tidbcloud/config-service/api/v1alpha1"
	"github.com/tidbcloud/config-service/pkg/config"
	"github.com/tidbcloud/config-service/pkg/metric"
	"github.com/tidbcloud/config-service/sdk/api"
)

// combineConfig combine two parts of configuration and warns if there are overlap
func combineConfig(base *operatorconfig.GenericConfig, patch map[string]interface{}) *operatorconfig.GenericConfig {
	conf := base.DeepCopy()
	for k, v := range patch {
		if conf.Get(k) != nil {
			metric.ConfigConflictsCounter.WithLabelValues(k).Inc()
		}
		conf.Set(k, v)
	}
	return conf
}

func computeTiDBConfig(current *pingcapv1alpha1.TiDBConfigWraper, c *api.TomlConfig) *pingcapv1alpha1.TiDBConfigWraper {
	if current == nil {
		current = pingcapv1alpha1.NewTiDBConfig()
	}
	oc := operatorconfig.New(c.MP)
	conf := combineConfig(oc, GetPreConfig(current.GenericConfig, api.ComponentTiDB))
	return &pingcapv1alpha1.TiDBConfigWraper{GenericConfig: conf}
}

func computeTiKVConfig(current *pingcapv1alpha1.TiKVConfigWraper, c *api.TomlConfig) *pingcapv1alpha1.TiKVConfigWraper {
	if current == nil {
		current = pingcapv1alpha1.NewTiKVConfig()
	}
	oc := operatorconfig.New(c.MP)
	conf := combineConfig(oc, GetPreConfig(current.GenericConfig, api.ComponentTiKV))
	return &pingcapv1alpha1.TiKVConfigWraper{GenericConfig: conf}
}

func computePDConfig(current *pingcapv1alpha1.PDConfigWraper, c configv1alpha1.PDInstanceConfig) *pingcapv1alpha1.PDConfigWraper {
	if current == nil {
		current = pingcapv1alpha1.NewPDConfig()
	}
	oc := operatorconfig.New(c.MP)
	conf := combineConfig(oc, GetPreConfig(current.GenericConfig, api.ComponentPD))
	// to avoid unnecessary rolling-restart, we keep the cluster config untouched at instance level
	pcc := configv1alpha1.ExtractPDClusterConfig(current.GenericConfig.MP)
	conf = combineConfig(conf, pcc.MP)
	return &pingcapv1alpha1.PDConfigWraper{GenericConfig: conf}
}

func computeTiFlashConfig(current *pingcapv1alpha1.TiFlashCommonConfigWraper, c *api.TomlConfig) *pingcapv1alpha1.TiFlashCommonConfigWraper {
	if current == nil {
		current = pingcapv1alpha1.NewTiFlashCommonConfig()
	}
	oc := operatorconfig.New(c.MP)
	conf := combineConfig(oc, GetPreConfig(current.GenericConfig, api.ComponentTiFlash))
	return &pingcapv1alpha1.TiFlashCommonConfigWraper{GenericConfig: conf}
}

func computeTiFlashLearnerConfig(current *pingcapv1alpha1.TiFlashProxyConfigWraper, c *api.TomlConfig) *pingcapv1alpha1.TiFlashProxyConfigWraper {
	if current == nil {
		current = pingcapv1alpha1.NewTiFlashProxyConfig()
	}
	oc := operatorconfig.New(c.MP)
	conf := combineConfig(oc, GetPreConfig(current.GenericConfig, api.ComponentTiFlashLearner))
	return &pingcapv1alpha1.TiFlashProxyConfigWraper{GenericConfig: conf}
}

func GetPreConfig(c *operatorconfig.GenericConfig, name api.ComponentName) map[string]interface{} {
	keys := config.GetPreConfigKeys(name)
	preConfig := make(map[string]interface{})
	for _, key := range keys {
		value := c.Get(key)
		if value != nil {
			preConfig[key] = value.Interface()
		}
	}
	return preConfig
}
