package cvt

import (
	ccent "github.com/tidbcloud/devops/entity/cloudconfig"
	"github.com/tidbcloud/devops/pkg/infra/utils/pointer"

	ccproto "github.com/tidbcloud/config-center/proto/config_center"
	"google.golang.org/protobuf/types/known/structpb"
)

type toConfigCenter struct{}
type fromConfigCenter struct{}

func (c toConfigCenter) ClusterType(typ ccent.ClusterType) ccproto.ClusterType {
	m := map[ccent.ClusterType]ccproto.ClusterType{
		// ccent.ClusterTypeTiProxy:        ccproto.ClusterType_TIPROXY,
		ccent.ClusterTypeDedicatedTiDB:       ccproto.ClusterType_DEDICATED_TIDB,
		ccent.ClusterTypeTiCDC:               ccproto.ClusterType_TICDC,
		ccent.ClusterTypeChangefeed:          ccproto.ClusterType_CHANGEFEED,
		ccent.ClusterTypeSharedPool:          ccproto.ClusterType_SHARED_POOL,
		ccent.ClusterTypeLogicalCluster:      ccproto.ClusterType_LOGICAL_CLUSTER,
		ccent.ClusterTypeTiCDCNG:             ccproto.ClusterType_TICDC_NG,
		ccent.ClusterTypeChangefeedNG:        ccproto.ClusterType_CHANGEFEED_NG,
		ccent.ClusterTypeChangefeedEssential: ccproto.ClusterType_CHANGEFEED_ESSENTIAL,
	}
	t, exist := m[typ]
	if !exist {
		return ccproto.ClusterType_CLUSTER_TYPE_UNSPECIFIED
	}
	return t
}

func (c fromConfigCenter) ClusterType(typ ccproto.ClusterType) ccent.ClusterType {
	m := map[ccproto.ClusterType]ccent.ClusterType{
		// ccproto.ClusterType_TIPROXY:         ccent.ClusterTypeTiProxy,
		ccproto.ClusterType_DEDICATED_TIDB:       ccent.ClusterTypeDedicatedTiDB,
		ccproto.ClusterType_TICDC:                ccent.ClusterTypeTiCDC,
		ccproto.ClusterType_CHANGEFEED:           ccent.ClusterTypeChangefeed,
		ccproto.ClusterType_SHARED_POOL:          ccent.ClusterTypeSharedPool,
		ccproto.ClusterType_LOGICAL_CLUSTER:      ccent.ClusterTypeLogicalCluster,
		ccproto.ClusterType_TICDC_NG:             ccent.ClusterTypeTiCDCNG,
		ccproto.ClusterType_CHANGEFEED_NG:        ccent.ClusterTypeChangefeedNG,
		ccproto.ClusterType_CHANGEFEED_ESSENTIAL: ccent.ClusterTypeChangefeedEssential,
	}
	t, exist := m[typ]
	if !exist {
		return ccent.ClusterTypeUnknown
	}
	return t
}

func (c fromConfigCenter) Component(comp *ccproto.ClusterComponent) *ccent.Component {
	return &ccent.Component{
		Name: comp.ComponentName,
		CloudInstanceType: ccent.CloudInstanceType{
			CloudProvider: comp.CloudProvider,
			InstanceType:  comp.NodeInstance,
		},
		Version:           comp.Version,
		BaseImage:         comp.BaseImage,
		OverwriteImageTag: comp.OverwriteImageTag,
		EffectiveImageTag: comp.EffectiveImageTag,
	}
}

func (c toConfigCenter) NamedParameter(item ccent.NamedParameter) (*ccproto.ParamItem, error) {
	val, err := structpb.NewValue(item.Value)
	if err != nil {
		return nil, err
	}

	return &ccproto.ParamItem{
		Value:   val,
		Comment: item.Comment,
	}, nil
}

func (c fromConfigCenter) NamedParameter(name string, ori *ccproto.ParamItem) *ccent.NamedParameter {
	return &ccent.NamedParameter{
		Name: name,
		ParameterValue: ccent.ParameterValue{
			Value:   ori.Value.AsInterface(),
			Comment: ori.Comment,
		},
	}
}

func (c fromConfigCenter) ParameterType(typ ccproto.ConfigParamType) string {
	return ccproto.ConfigParamType_name[int32(typ)]
}

func (c fromConfigCenter) ParameterAttribute(ori *ccproto.ConfigurationDictItem) *ccent.ParameterAttribute {
	// set default risk level to high
	riskLevel := ccent.RiskLevelHigh
	if ori.RiskLevel == string(ccent.RiskLevelLow) {
		riskLevel = ccent.RiskLevelLow
	}
	return &ccent.ParameterAttribute{
		ClusterType:  c.ClusterType(ori.GetClusterType()),
		Name:         ori.ParamKey,
		ConfigType:   ori.ConfigurationType,
		ApplyPolicy:  c.ParameterApplyPolicy(ori.HotReloadable),
		Version:      ori.Version,
		Type:         c.ParameterType(ori.Type),
		DefaultValue: ori.DefaultValue.AsInterface(),
		RiskLevel:    riskLevel,
		Description:  ori.Description,
		Validation:   ori.Validation,
		UpdatedBy:    ori.UpdatedBy,
	}
}

func (c toConfigCenter) ParameterAttribute(attr ccent.ParameterAttribute) (*ccproto.ConfigurationDictItem, error) {
	val, err := structpb.NewValue(attr.DefaultValue)
	if err != nil {
		return nil, err
	}

	return &ccproto.ConfigurationDictItem{
		ClusterType:       c.ClusterType(attr.ClusterType),
		ConfigurationType: attr.ConfigType,
		Version:           attr.Version,
		ParamKey:          attr.Name,
		DefaultValue:      val,
		Type:              ccproto.ConfigParamType(ccproto.ConfigParamType_value[attr.Type]),
		RiskLevel:         string(attr.RiskLevel),
		UpdatedBy:         attr.UpdatedBy,
		HotReloadable:     pointer.Pointer(c.ParameterApplyPolicy(attr.ApplyPolicy)),
		Description:       attr.Description,
		NeedTemplate:      nil,
		Validation:        attr.Validation,
	}, nil
}

func (c fromConfigCenter) ParameterApplyPolicy(hotreloadable *bool) ccent.ParameterApplyPolicy {
	if hotreloadable != nil && *hotreloadable {
		return ccent.ParameterApplyPolicyHotReload
	}
	return ccent.ParameterApplyPolicyRestart
}

func (c toConfigCenter) ParameterApplyPolicy(typ ccent.ParameterApplyPolicy) bool {
	return typ == ccent.ParameterApplyPolicyHotReload
}

func (c fromConfigCenter) Template(ori *ccproto.ConfigurationTemplate) *ccent.Template {
	tmpl := &ccent.Template{
		ID:          uint64(ori.TemplateId),
		Name:        ori.TemplateName,
		ConfigType:  ori.ConfigurationType,
		ClusterType: c.ClusterType(ori.ClusterType),
		Tags:        ori.Tags,
		Parameters:  make(map[string]*ccent.NamedParameter),
	}

	for name, data := range ori.Data {
		tmpl.Parameters[name] = c.NamedParameter(name, data)
	}

	return tmpl
}

func (c fromConfigCenter) TemplateDefaultCase(defCase *ccproto.DefaultCase) *ccent.TemplateDefaultCase {
	return &ccent.TemplateDefaultCase{
		CloudInstanceType: ccent.CloudInstanceType{
			CloudProvider: defCase.CloudProvider,
			InstanceType:  defCase.NodeInstance,
		},
		Version:    defCase.Version,
		ConfigType: defCase.ConfigurationType,
		Region:     defCase.Region,
		TenantID:   defCase.TenantId,
	}
}

func (c fromConfigCenter) TemplateDefaultCaseBinding(defCaseBinding *ccproto.TemplateDefaultCaseBinding) *ccent.TemplateDefaultCaseBinding {
	return &ccent.TemplateDefaultCaseBinding{
		DefaultCase: *c.TemplateDefaultCase(defCaseBinding.DefaultCase),
		ClusterType: c.ClusterType(defCaseBinding.ClusterType),
		TemplateID:  uint64(defCaseBinding.TemplateId),
	}
}

func (c fromConfigCenter) Format(typ ccproto.Format) string {
	return ccproto.Format_name[int32(typ)]
}

func (c fromConfigCenter) CloudInstanceType(ins *ccproto.UniqueInstance) *ccent.CloudInstanceType {
	return &ccent.CloudInstanceType{
		CloudProvider: ins.CloudProvider,
		InstanceType:  ins.NodeInstance,
	}
}
