package planner

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"

	_ "github.com/go-sql-driver/mysql"
	host "github.com/pingcap/endless/pkg/workloadnode/tiup"
	"github.com/pingcap/log"
	_ "github.com/pingcap/test-infra/sdk/resource/impl/k8s"

	backoff "github.com/cenkalti/backoff/v4"
	"go.uber.org/zap"
	"sigs.k8s.io/yaml"
)

func deployCluster(tc *host.TiUPCluster, version string, ignoreConfigCheck bool) error {
	deployBackOff := backoff.NewExponentialBackOff()
	deployBackOff.InitialInterval = 5 * time.Second
	deployBackOff.MaxInterval = 3 * time.Minute
	deployBackOff.Multiplier = 3
	deployBackOff.MaxElapsedTime = 5 * time.Minute

	retryable := func() error {
		var err error
		if ignoreConfigCheck {
			err = tc.Deploy(version, "--ignore-config-check", "true", "--format", "json")
		} else {
			err = tc.Deploy(version, "--format", "json")
		}
		if err != nil {
			log.Info("deploy error: ", zap.Error(err))
			return err
		}
		return nil
	}
	err := backoff.Retry(retryable, deployBackOff)
	return err
}

func processConfig(config, componentType string) (string, error) {
	// default configration
	// tidb: mem-quota-query
	// tikv: storage.block-cache.capacity": 30GB import.num-threads: 64
	memQuotaQuery := uint64(1073741824)
	configMap := make(map[string]interface{})
	err := yaml.Unmarshal([]byte(config), &configMap)
	if err != nil {
		return "", err
	}

	if componentType == "tidb" {
		// provide default mem-quota-query if it is not specified
		if _, ok := configMap["mem-quota-query"]; !ok {
			configMap["mem-quota-query"] = memQuotaQuery
		}
	}

	if componentType == "tikv" {
		// provide default mem-quota-query if it is not specified
		if _, ok := configMap["storage"]; ok {
			map1 := configMap["storage"].(map[string]interface{})
			if _, ok2 := map1["block-cache"]; ok2 {
				map2 := map1["block-cache"].(map[string]interface{})
				if _, ok3 := map2["capacity"]; !ok3 {
					configMap["storage.block-cache.capacity"] = blockCache
				}
			} else {
				configMap["storage.block-cache.capacity"] = blockCache
			}
		} else {
			configMap["storage.block-cache.capacity"] = blockCache
		}
		// provide default mem-quota-query if it is not specified
		if _, ok := configMap["import"]; ok {
			map1 := configMap["import"].(map[string]interface{})
			if _, ok2 := map1["num-threads"]; !ok2 {
				configMap["import.num-threads"] = numThreads
			}
		} else {
			configMap["import.num-threads"] = numThreads
		}
	}

	configStr, err := MapToJson(configMap)
	if err != nil {
		return "", err
	}
	return string(configStr), nil
}

func renderTopo(tidbs int, tikvs int, tiflashs int) string {
	tidbPrefix := "tidb_servers:"
	tikvPrefix := "tikv_servers:"
	tiflashPrefix := "tiflash_servers:"

	topoList := []string{}
	if tidbs > 0 {
		tidbTpl :=
			`  - host: tidb-%d-peer`
		tidbList := []string{tidbPrefix}
		for i := 1; i <= tidbs; i++ {
			tidbList = append(tidbList, fmt.Sprintf(tidbTpl, i))
		}
		topoList = append(topoList, strings.Join(tidbList, "\n"))
	}
	if tikvs > 0 {
		tikvTpl :=
			`  - host: tikv-%d-peer`
		tikvList := []string{tikvPrefix}
		for i := 1; i <= tikvs; i++ {
			tikvList = append(tikvList, fmt.Sprintf(tikvTpl, i))
		}
		topoList = append(topoList, strings.Join(tikvList, "\n"))
	}
	if tiflashs > 0 {
		tiflashTpl :=
			`  - host: tiflash-%d-peer`
		tiflashList := []string{tiflashPrefix}
		for i := 1; i <= tiflashs; i++ {
			tiflashList = append(tiflashList, fmt.Sprintf(tiflashTpl, i))
		}
		topoList = append(topoList, strings.Join(tiflashList, "\n"))
	}
	return strings.Join(topoList, "\n")
}

// Convert map json string
func MapToJson(m map[string]interface{}) ([]byte, error) {
	jsonByte, err := json.Marshal(m)
	if err != nil {
		return []byte{}, nil
	}

	return jsonByte, nil
}
