package testtoken

import (
	"context"
	"fmt"
	"time"

	"github.com/pingcap/log"
	configServiceV1 "github.com/tidbcloud/config-service/api/v1alpha1"
	"github.com/tidbcloud/config-service/sdk/api"
	"go.uber.org/zap"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
)

const (
	NameSpaceGitOps = "gitops"
	TIME_FMT        = "2006-01-02-15:04:05"
	tiDBConfig      = `
alter-primary-key = true
enable-telemetry = false
new_collations_enabled_on_first_bootstrap = true

[log]
level = "info"

[performance]
max-procs = 0
tcp-keep-alive = true
`
	tiKVConfig = `
log-level = "info"

[raftstore]
hibernate-regions = true

[readpool]

[readpool.coprocessor]
use-unified-pool = true

[readpool.storage]
use-unified-pool = true

[server]
max-grpc-send-msg-len = 100000000
`
	tiFlashConfig = `
delta_index_cache_size = 3221225472
mark_cache_size = 2147483648
max_memory_usage = 10737418240
max_memory_usage_for_all_queries = 32212254720
minmax_index_cache_size = 2147483648
`
	pdConfig = `
[dashboard]
enable-telemetry = false
internal-proxy = true

[log]
level = "info"

[replication]
location-labels = ["failure-domain.beta.kubernetes.io/region", "failure-domain.beta.kubernetes.io/zone", "kubernetes.io/hostname"]
max-replicas = 3

[schedule]
enable-cross-table-merge = true
`
	backupConfig = `
concurrency = 32
`
	restoreConfig = ``
)

type CustomizedImage struct {
	TiKVImageURL    string
	TiKVImageTag    string
	TiDBImageURL    string
	TiDBImageTag    string
	TiFlashImageURL string
	TiFlashImageTag string
	PDImageURL      string
	PDImageTag      string
	BackupImageURL  string
	BackupImageTag  string
	RestoreImageURL string
	RestoreImageTag string
	GlobalVersion   string
}

func getConfigServiceLabels(tenantID uint64) map[string]string {
	return map[string]string{
		"tidbcloud.com/cluster":    "all",
		"tidbcloud.com/managed-by": "cicd",
		"tidbcloud.com/module":     "tidb-configuration",
		"tidbcloud.com/tenant":     fmt.Sprintf("%d", tenantID),
	}
}

type ConfigService interface {
	DescribeClusterConfiguration(*api.DescribeClusterConfigInput) (*api.DescribeClusterConfigOutput, error)
	DescribeBackupConfiguration(*api.DescribeBRConfigInput) (*api.DescribeBRConfigOutput, error)
	DescribeRestoreConfiguration(*api.DescribeBRConfigInput) (*api.DescribeBRConfigOutput, error)
}

func (m Model) GetCustomizedImage(tenantID uint64, configService ConfigService) (*CustomizedImage, error) {
	customizedImage := CustomizedImage{}

	components := []api.ComponentInput{
		{Component: api.ComponentTiKV},
		{Component: api.ComponentTiDB},
		{Component: api.ComponentTiFlash},
		{Component: api.ComponentPD},
	}
	log.Info("query cluster config")
	clusterOutput, err := configService.DescribeClusterConfiguration(&api.DescribeClusterConfigInput{TenantID: &tenantID, Components: components})
	if err != nil {
		log.Error("failed to get cluster image configuration", zap.Error(err))
		return nil, err
	}
	log.Info("query backup config")
	backupOutput, err := configService.DescribeBackupConfiguration(&api.DescribeBRConfigInput{TenantID: &tenantID})
	if err != nil {
		log.Error("failed to get backup configuration", zap.Error(err))
		return nil, err
	}
	log.Info("query restore config")
	restoreOutput, err := configService.DescribeRestoreConfiguration(&api.DescribeBRConfigInput{TenantID: &tenantID})
	if err != nil {
		log.Error("failed to get restore configuration", zap.Error(err))
		return nil, err
	}

	getTag := func(config *api.DescribeBRConfigOutput) string {
		tag := backupOutput.Version
		if config.Image.Tag != nil {
			tag = *config.Image.Tag
		}

		return tag
	}

	customizedImage.BackupImageURL = backupOutput.Image.BaseImage
	customizedImage.BackupImageTag = getTag(backupOutput)
	customizedImage.RestoreImageURL = restoreOutput.Image.BaseImage
	customizedImage.RestoreImageTag = getTag(restoreOutput)
	customizedImage.GlobalVersion = clusterOutput.Version

	for _, i := range clusterOutput.Components {
		tag := clusterOutput.Version
		if i.Image.Tag != nil {
			tag = *i.Image.Tag
		}

		switch i.Component {
		case api.ComponentTiKV:
			customizedImage.TiKVImageURL = i.Image.BaseImage
			customizedImage.TiKVImageTag = tag
		case api.ComponentTiDB:
			customizedImage.TiDBImageURL = i.Image.BaseImage
			customizedImage.TiDBImageTag = tag
		case api.ComponentTiFlash:
			customizedImage.TiFlashImageURL = i.Image.BaseImage
			customizedImage.TiFlashImageTag = tag
		case api.ComponentPD:
			customizedImage.PDImageURL = i.Image.BaseImage
			customizedImage.PDImageTag = tag
		default:
			log.Error("bad component", zap.Any("component", i.Component))
		}
	}

	return &customizedImage, nil
}

func (m Model) SetCustomizedImage(image CustomizedImage, tenantID uint64) error {
	convert := func(baseURL, tag, config string) *configServiceV1.Configuration {
		tomlConfig := api.TomlConfig{}
		if err := tomlConfig.UnmarshalTOML([]byte(config)); err != nil {
			log.Error("failed to unmarshal toml config", zap.Error(err), zap.String("config", config))
		}

		return &configServiceV1.Configuration{
			BaseImage: baseURL,
			Tag:       &tag,
			Config:    &tomlConfig,
		}
	}

	now := time.Now()
	configMeta := configServiceV1.ConfigMeta{
		GitCommitID:   fmt.Sprintf("gen-for-qa-%s", now.Format(TIME_FMT)),
		ConfigVersion: uint64(now.Unix()),
	}
	obj := configServiceV1.ClusterConfig{
		ObjectMeta: v1.ObjectMeta{
			Namespace: NameSpaceGitOps,
			Name:      fmt.Sprintf("base--%d", tenantID),
			Labels:    getConfigServiceLabels(tenantID),
		},
		Spec: configServiceV1.ClusterConfigSpec{
			Version:    image.GlobalVersion,
			ConfigMeta: &configMeta,
			TiDB:       convert(image.TiDBImageURL, image.TiDBImageTag, tiDBConfig),
			TiKV:       convert(image.TiKVImageURL, image.TiKVImageTag, tiKVConfig),
			TiFlash:    convert(image.TiFlashImageURL, image.TiFlashImageTag, tiFlashConfig),
			PD:         convert(image.PDImageURL, image.PDImageTag, pdConfig),
			Backup:     convert(image.BackupImageURL, image.BackupImageTag, backupConfig),
			Restore:    convert(image.RestoreImageURL, image.RestoreImageTag, restoreConfig),
		},
	}

	if err := m.K8sClient.Create(context.TODO(), &obj); err != nil {
		log.Error("failed to create cluster config", zap.Error(err))
		return err
	}

	return nil
}

func (m Model) DeleteCustomizedImage(tenantID uint64) error {
	obj := configServiceV1.ClusterConfig{
		ObjectMeta: v1.ObjectMeta{
			Namespace: NameSpaceGitOps,
			Name:      fmt.Sprintf("base--%d", tenantID),
			Labels:    getConfigServiceLabels(tenantID),
		},
	}

	obj.SetGroupVersionKind(schema.GroupVersionKind{
		Group:   configServiceV1.GroupVersion.Group,
		Version: configServiceV1.GroupVersion.Version,
		Kind:    configServiceV1.ClusterConfigKind,
	})

	if err := m.K8sClient.Delete(context.TODO(), &obj); err != nil {
		log.Error("failed to create cluster config", zap.Error(err))
		return err
	}

	return nil
}
