package model

import (
	"fmt"
	"strings"

	"github.com/labstack/gommon/log"
	"github.com/tidbcloud/pov-service/internal/common/constant"
)

type WorkloadField struct {
	Key      string `json:"key,omitempty"`
	Name     string `json:"name,omitempty"`
	Type     string `json:"type"`
	Desc     string `json:"description"`
	Required bool   `json:"required"`
	// INFO: it's always string now. Maybe optimize to real type later
	Value   any   `json:"value"`
	Options []any `json:"options,omitempty"`

	// INFO: string for now
	LowerLimit any `json:"lowerLimit,omitempty"`
	UpperLimit any `json:"upperLimit,omitempty"`
}

type WorkloadType string

const (
	WorkloadTypeHTAP WorkloadType = "HTAP"
	WorkloadTypeOLTP WorkloadType = "OLTP"
	WorkloadTypeOLAP WorkloadType = "OLAP"
)

func (w WorkloadType) IsValid() bool {
	return w == WorkloadTypeHTAP || w == WorkloadTypeOLTP || w == WorkloadTypeOLAP
}

// Workload maintains the metadata and template of a workload
// And once being triggered, it becomes the @WorkloadTest
type Workload struct {
	Name           string          `json:"name"`
	Description    string          `json:"description"`
	Tags           []string        `json:"tags"`
	Type           WorkloadType    `json:"type"`
	ParamsTemplate []WorkloadField `json:"paramsTemplate"`

	ID int64 `json:"id"`
}

func (w Workload) KeywordContext() string {
	return w.Name + w.Description + strings.Join(w.Tags, " ") + string(w.Type)
}

func (w Workload) DefaultParam(key string) *WorkloadField {
	for _, param := range w.ParamsTemplate {
		// TODO: set key to every param
		if param.Key == key || param.Name == key {
			return &param
		}
	}
	return nil
}

func (w Workload) DefaultParamValue(key string) any {
	field := w.DefaultParam(key)
	if field != nil {
		return field.Value
	}
	return nil
}

type WorkloadParam struct {
	Name  string `json:"name"`
	Value string `json:"value"`
	// We may maintain some internal params using the this struct,
	// But only user defined params should be visible on FE.
	IsUserDefined bool `json:"-"`
}

func (w WorkloadParam) IsValid() bool {
	return w.Name != ""
}

type WorkloadParams []WorkloadParam

func (w *WorkloadParams) Set(name, value string) {
	for i := range *w {
		if (*w)[i].Name == name {
			(*w)[i].Value = value
			return
		}
	}
	*w = append(*w, WorkloadParam{
		Name:  name,
		Value: value,
	})
}

func (w *WorkloadParams) Get(name string) *WorkloadParam {
	for i := range *w {
		if (*w)[i].Name == name {
			return &(*w)[i]
		}
	}
	return nil
}

func (w *WorkloadParams) GetClusterType() (ClusterType, error) {
	param := w.Get(string(constant.ClusterType))
	if param == nil || strings.TrimSpace(param.Value) == "" {
		return ClusterTypeTiDBCloudPremium, nil
	}

	switch param.Value {
	case string(ClusterTypeTiDBCloudPremium):
		return ClusterTypeTiDBCloudPremium, nil
	case string(ClusterTypeTiDBCloudEssential):
		return ClusterTypeTiDBCloudEssential, nil
	}

	log.Error("unknown cluster type: ", param.Value)
	return "", fmt.Errorf("unknown cluster type: %s", param.Value)
}

func (w *WorkloadParams) GetWorkloadName() string {
	typ := w.Get(string(constant.WorkloadName))
	if typ != nil {
		return typ.Value
	}
	return ""
}

func (w *WorkloadParams) GetPrepareParams() WorkloadParams {
	ret := make(WorkloadParams, 0, len(*w))

	// prepare_ prefixed params
	for _, value := range *w {
		if value.Name == "" {
			continue
		}

		if value.Name == "prepare_method" {
			ret = append(ret, WorkloadParam{
				Name:  value.Name,
				Value: value.Value,
			})
			continue
		}

		if trimmed, ok := strings.CutPrefix(value.Name, "prepare_"); ok {
			ret = append(ret, WorkloadParam{
				Name:  trimmed,
				Value: value.Value,
			})
		}
	}

	// whitelisted params
	if v := w.Get(string(constant.BrStorageURL)); v != nil && v.Value != "" {
		ret = append(ret, WorkloadParam{
			Name:  "storage_url",
			Value: v.Value,
		})
	}
	// TODO:  support config s3 endpoint
	ret = append(ret, WorkloadParam{
		Name:  "s3_endpoint",
		Value: "https://qa-workload-datasets.s3.us-west-2.amazonaws.com",
	})
	return ret
}
