package cat

import (
	"fmt"

	"github.com/pingcap/schrodinger/config"
	"github.com/pingcap/schrodinger/pkg/storage"
	"github.com/pingcap/schrodinger/pkg/types"
	"github.com/pingcap/schrodinger/util"
	"github.com/pingcap/tidb-operator/pkg/apis/pingcap.com/v1alpha1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// getNodeLabels gets node labels
func getNodeLabels(target types.Label) map[string]string {
	s := types.NewSchedule([]string{"node"})
	return s.Schedule(target)
}

func getClusterSpec(store *storage.Storage, c *types.CatProcess, labels map[string]map[string]string) *types.Cluster {
	pdSpec := v1alpha1.PDSpec{
		ContainerSpec:        genComponentContainerSpec("pd", c.RunVersion.PDVersion, c.Config.PDConfig),
		Replicas:             c.Config.PDConfig.Size,
		NodeSelector:         labels["pd"],
		NodeSelectorRequired: true,
		StorageClassName:     c.Config.PDConfig.ResourceConfig.Requests.StorageClass,
	}

	tikvSpec := v1alpha1.TiKVSpec{
		ContainerSpec:        genComponentContainerSpec("tikv", c.RunVersion.TiKVVersion, c.Config.TiKVConfig),
		Replicas:             c.Config.TiKVConfig.Size,
		NodeSelector:         labels["tikv"],
		NodeSelectorRequired: true,
		StorageClassName:     c.Config.TiKVConfig.ResourceConfig.Requests.StorageClass,
	}

	tidbSpec := v1alpha1.TiDBSpec{
		ContainerSpec:        genComponentContainerSpec("tidb", c.RunVersion.TiDBVersion, c.Config.TiDBConfig),
		Replicas:             c.Config.TiDBConfig.Size,
		NodeSelector:         labels["tidb"],
		NodeSelectorRequired: true,
	}

	clusterSpec := v1alpha1.TidbClusterSpec{
		PD:       pdSpec,
		TiDB:     tidbSpec,
		TiKV:     tikvSpec,
		Services: getServices(),
	}

	tidbCluster := &v1alpha1.TidbCluster{
		TypeMeta: metav1.TypeMeta{
			Kind: "TidbCluster",
		},
		ObjectMeta: metav1.ObjectMeta{
			Namespace:   c.Namespace,
			Name:        "tidb-cluster",
			ClusterName: "tidb-cluster",
		},
		Spec: clusterSpec,
	}

	cluster := &types.Cluster{
		TidbCluster: tidbCluster,
		Meta:        c.Meta,
		TiDBConfig:  genConfigContent(store, c.Config.TiDBConfig),
		TiKVConfig:  genConfigContent(store, c.Config.TiKVConfig),
		PDConfig:    genConfigContent(store, c.Config.PDConfig),
		ConfigSet:   make(map[string]string),
	}

	boxID := c.BoxID
	if boxID == 0 {
		return cluster
	}

	box, ok := store.GetBox(boxID)
	if !ok {
		return cluster
	}

	if box.Misc.Type == types.BoxTypeGeneral {
		cluster.ConfigSet["open-general-log"] = "true"
	}

	return cluster
}

func genComponentContainerSpec(kind string, version types.VersionInfo, cfg types.ComponentConfig) v1alpha1.ContainerSpec {
	return v1alpha1.ContainerSpec{
		Image: getImage(kind, version, cfg.Fuse),
		Requests: &v1alpha1.ResourceRequirement{
			CPU:    cfg.ResourceConfig.Requests.CPU,
			Memory: cfg.ResourceConfig.Requests.Memory,
		},
		Limits: &v1alpha1.ResourceRequirement{
			CPU:    cfg.ResourceConfig.Limits.CPU,
			Memory: cfg.ResourceConfig.Limits.Memory,
		},
	}
}

func genConfigContent(store *storage.Storage, cfg types.ComponentConfig) types.ConfigContent {
	configContent := types.ConfigContent{
		LogLevel: cfg.LogLevel,
		Args:     cfg.Args,
	}
	tpl, ok := store.GetTiDBClusterConfigTemplateByName(cfg.ConfigTemplateName)
	if ok {
		configContent.Content = tpl.Content
	}

	return configContent
}

func getServices() []v1alpha1.Service {
	var services []v1alpha1.Service

	for _, name := range []string{"pd", "tidb", "prometheus", "grafana"} {
		services = append(services, v1alpha1.Service{
			Name: name,
			Type: "NodePort",
		})
	}

	return services
}

func getImage(name string, version types.VersionInfo, fuse bool) string {
	return fmt.Sprintf("%s/%s:%s", config.GetGlobalConfig().Image.DockerhubPrefix, name, getTag(version, fuse))
}

func getTag(version types.VersionInfo, fuse bool) string {
	if util.IsEmpty(version.CompileOption) {
		if !fuse {
			return fmt.Sprintf("%s-%s", version.Value[:7], version.Platform)
		}
		return fmt.Sprintf("%s-%s-fuse100", version.Value[:7], version.Platform)
	}
	if !fuse {
		return fmt.Sprintf("%s-%s-%s", version.Value[:7], version.Platform, version.CompileOption)
	}
	return fmt.Sprintf("%s-%s-%s-fuse100", version.Value[:7], version.Platform, version.CompileOption)
}
