package v1alpha1

import (
	"strings"

	"github.com/tidbcloud/config-service/sdk/api"
)

type ComponentSpec struct {
	BaseImage string
	Tag       *string
	Config    *api.TomlConfig
}

type PDClusterConfig struct {
	*api.TomlConfig
}

type PDInstanceConfig struct {
	*api.TomlConfig
}

func (cc *ClusterConfig) IsDryrun() bool {
	return cc.GetUpdatePolicy() == UpdatePolicyDryrun
}

// GetUpdatePolicy get the update policy
func (cc *ClusterConfig) GetUpdatePolicy() UpdatePolicy {
	if len(cc.Spec.UpdatePolicy) < 1 {
		// default value for backward compatibility
		return UpdatePolicyDryrun
	}
	return cc.Spec.UpdatePolicy
}

// GetPDClusterConfig returns the PD cluster-level configuration stored in shared storage
func (cc *ClusterConfig) GetPDClusterConfig(instanceType string) PDClusterConfig {
	conf := cc.Spec.PD.findByInstanceType(instanceType)
	clone := conf.DeepCopy()
	for k := range clone.MP {
		if !isPDClusterConfig(k) {
			delete(clone.MP, k)
		}
	}
	// special treatment for replication.location-labels
	if rep, ok := clone.MP["replication"]; ok {
		if repMap, ok := rep.(map[string]interface{}); ok {
			if ls, ok := repMap["location-labels"]; ok {
				if lList, ok := ls.([]interface{}); ok {
					sb := strings.Builder{}
					for i, s := range lList {
						if i != 0 {
							sb.WriteString(",")
						}
						sb.WriteString(s.(string))
					}
					repMap["location-labels"] = sb.String()
				}
			}
		}
	}
	return PDClusterConfig{clone}
}

// GetPDClusterConfig returns the PD instance-level configuration stored in per PD node
func (cc *ClusterConfig) GetPDInstanceConfig(instanceType string) PDInstanceConfig {
	conf := cc.Spec.PD.findByInstanceType(instanceType)
	clone := conf.DeepCopy()
	for k := range clone.MP {
		if isPDClusterConfig(k) {
			delete(clone.MP, k)
		}
	}
	return PDInstanceConfig{clone}
}

func (cc *ClusterConfig) GetPDSpec(instanceType string) *ComponentSpec {
	conf := cc.Spec.PD.findByInstanceType(instanceType)
	return &ComponentSpec{
		BaseImage: cc.Spec.PD.BaseImage,
		Tag:       cc.Spec.PD.Tag,
		Config:    conf,
	}
}

func (cc *ClusterConfig) GetTiKVSpec(instanceType string) *ComponentSpec {
	conf := cc.Spec.TiKV.findByInstanceType(instanceType)
	return &ComponentSpec{
		BaseImage: cc.Spec.TiKV.BaseImage,
		Tag:       cc.Spec.TiKV.Tag,
		Config:    conf,
	}
}

func (cc *ClusterConfig) GetTiProxySepc(instanceType string) *ComponentSpec {
	conf := cc.Spec.TiProxy.findByInstanceType(instanceType)
	return &ComponentSpec{
		BaseImage: cc.Spec.TiProxy.BaseImage,
		Tag:       cc.Spec.TiProxy.Tag,
		Config:    conf,
	}
}

func (cc *ClusterConfig) GetTiDBSpec(instanceType string) *ComponentSpec {
	conf := cc.Spec.TiDB.findByInstanceType(instanceType)
	return &ComponentSpec{
		BaseImage: cc.Spec.TiDB.BaseImage,
		Tag:       cc.Spec.TiDB.Tag,
		Config:    conf,
	}
}

func (cc *ClusterConfig) GetTiFlashSpec(instanceType string) *ComponentSpec {
	conf := cc.Spec.TiFlash.findByInstanceType(instanceType)
	return &ComponentSpec{
		BaseImage: cc.Spec.TiFlash.BaseImage,
		Tag:       cc.Spec.TiFlash.Tag,
		Config:    conf,
	}
}

func (cc *ClusterConfig) GetTiFlashLearnerSpec(instanceType string) *ComponentSpec {
	if cc.Spec.TiFlashLearner == nil {
		return nil
	}
	conf := cc.Spec.TiFlashLearner.findByInstanceType(instanceType)
	return &ComponentSpec{
		BaseImage: cc.Spec.TiFlashLearner.BaseImage,
		Tag:       cc.Spec.TiFlashLearner.Tag,
		Config:    conf,
	}
}

func (c *Configuration) findByInstanceType(instanceType string) *api.TomlConfig {
	for _, o := range c.Overlays {
		if o.InstanceType == instanceType {
			return o.Config
		}
	}
	return c.Config
}

func ExtractPDClusterConfig(m map[string]interface{}) *PDClusterConfig {
	if m == nil {
		return &PDClusterConfig{api.NewTomlConfig(map[string]interface{}{})}
	}
	out := map[string]interface{}{}
	for k := range m {
		if isPDClusterConfig(k) {
			out[k] = m[k]
		}
	}
	return &PDClusterConfig{api.NewTomlConfig(out)}
}

func isPDClusterConfig(k string) bool {
	return k == "schedule" || k == "replication"
}
