package types

import (
	"github.com/pingcap/schrodinger/util"
)

// CatStage is cat stage
type CatStage string

// CatStage
const (
	CatStageNew       CatStage = "NEW"
	CatStageCreating           = "CREATING"
	CatStageRunning            = "RUNNING"
	CatStageUpdating           = "UPDATING"
	CatStageBackuping          = "BACKUPING"
	CatStageRestoring          = "RESTORING"
)

// Default cat resource
const (
	PDRequestCPUCount = "2"
	PDLimitCPUCount   = "2"

	PDRequestMemoryCount = "4Gi"
	PDLimitMemoryCount   = "8Gi"

	TiDBRequestCPUCount = "4"
	TiDBLimitCPUCount   = "8"

	TiDBRequestMemoryCount = "32Gi"
	TiDBLimitMemoryCount   = "64Gi"

	TiKVRequestCPUCount = "4"
	TiKVLimitCPUCount   = "8"

	TiKVRequestMemoryCount = "16Gi"
	TiKVLimitMemoryCount   = "32Gi"
)

var (
	pdDefaultConfig = ResourceRequirements{
		Requests: &ResourceRequirement{
			CPU:    PDRequestCPUCount,
			Memory: PDRequestMemoryCount,
		},
		Limits: &ResourceRequirement{
			CPU:    PDLimitCPUCount,
			Memory: PDLimitMemoryCount,
		},
	}
	tidbDefaultConfig = ResourceRequirements{
		Requests: &ResourceRequirement{
			CPU:    TiDBRequestCPUCount,
			Memory: TiDBRequestMemoryCount,
		},
		Limits: &ResourceRequirement{
			CPU:    TiDBLimitCPUCount,
			Memory: TiDBLimitMemoryCount,
		},
	}
	tikvDefaultConfig = ResourceRequirements{
		Requests: &ResourceRequirement{
			CPU:    TiKVRequestCPUCount,
			Memory: TiKVRequestMemoryCount,
		},
		Limits: &ResourceRequirement{
			CPU:    TiKVLimitCPUCount,
			Memory: TiKVLimitMemoryCount,
		},
	}
)

// CatState is cat state
type CatState string

// CatState
const (
	CatStateNormal   CatState = "NORMAL"
	CatStateAbnormal          = "ABNORMAL"
)

// CatStatus is the combination of the stage and state of the cat
type CatStatus struct {
	Stage CatStage
	State CatState
}

// CatProcess is the cluster instance, experiment use this to create cluster
type CatProcess struct {
	*Meta

	ID             int64  `json:"id"`
	Owner          string `json:"owner"`
	Creator        string `json:"creator"`
	BoxID          int64  `json:"box_id"`
	BoxName        string `json:"box_name"`
	ExperimentName string `json:"experiment_name"`

	Ctime string `json:"create_time"`
	Utime string `json:"update_time"`

	Name      string `json:"name"`
	Namespace string `json:"namespace"`

	Destroyed bool `json:"destroyed"`

	Config     TiDBClusterConfig `json:"config"`
	RunVersion RunVersion        `json:"run_version"`

	Stage CatStage `json:"stage"`
	State CatState `json:"status"`

	Report *CatReport `json:"report"`

	UseBoxLabel  bool              `json:"user_box_label"`
	IgnoreLabels map[string]string `json:"ignore_labels"`

	// If we executes the backup / restore action, we set it with the special backup name.
	// we recover the the backup / restore aciton according to it.
	BackupName string `json:"backup_name"`
}

// RunVersion defines the information of version to run
type RunVersion struct {
	TiDBVersion VersionInfo `json:"tidb_version"`
	TiKVVersion VersionInfo `json:"tikv_version"`
	PDVersion   VersionInfo `json:"pd_version"`
}

// Valid checks the RunVersion
func (r *RunVersion) Valid() bool {
	for _, ver := range []VersionInfo{r.PDVersion, r.TiKVVersion, r.TiDBVersion} {
		if !ver.isValid() || len(ver.Value) < 8 {
			return false
		}
	}

	return true
}

// VersionInfo defines the information of git
type VersionInfo struct {
	Name          string `json:"name"`
	Type          string `json:"type"`
	Value         string `json:"value"`
	Platform      string `json:"platform"`
	CompileOption string `json:"compile_option"`
	BuildTime     string `json:"build_time"`
	CatProcessID  int64  `json:"cat_process"`
}

// GetBranchTagHash return the value of branch / tag / hash
func (v *VersionInfo) GetBranchTagHash() (string, string, string) {
	switch v.Type {
	case util.GitBranch:
		return v.Value, "", ""
	case util.GitTag:
		return "", v.Value, ""
	case util.GitHash:
		return "", "", v.Value
	default:
		return "", "", ""
	}
}

func (v *VersionInfo) isValid() bool {
	return v.Type == util.GitHash || v.Type == util.GitBranch || v.Type == util.GitTag
}

// ResourceRequirements is a set resource requirements, including the requests resource and limit resource.
type ResourceRequirements struct {
	Requests *ResourceRequirement `json:"requests" yaml:"requests"`
	Limits   *ResourceRequirement `json:"limits"   yaml:"requests"`
}

// SetDefaultValue sets default values for ResourceRequirements
func (r *ResourceRequirements) SetDefaultValue(defaultRes ResourceRequirements) {
	if r.Requests == nil {
		r.Requests = defaultRes.Requests
	}
	r.Requests.SetDefaultValue(defaultRes.Requests.CPU, defaultRes.Requests.Memory)

	if r.Limits == nil {
		r.Limits = defaultRes.Limits
	}
	r.Limits.SetDefaultValue(defaultRes.Limits.CPU, defaultRes.Limits.Memory)
}

// ResourceRequirement is resource requirements for a pod
type ResourceRequirement struct {
	// CPU is how many cores a pod requires
	CPU string `json:"cpu,omitempty" yaml:"cpu,omitempty"`
	// Memory is how much memory a pod requires
	Memory string `json:"memory,omitempty" yaml:"memory,omitempty"`
	// Storage is storage size a pod requires
	StorageClass string `json:"storage_class,omitempty" yaml:"memory,omitempty"`
}

// SetDefaultValue sets default values for ResourceRequirement
func (r *ResourceRequirement) SetDefaultValue(cpu, memory string) {
	if util.IsEmpty(r.CPU) {
		r.CPU = cpu
	}

	if util.IsEmpty(r.Memory) {
		r.Memory = memory
	}
}

// Clone clones CatProcess
func (c *CatProcess) Clone() *CatProcess {
	catProcessClone := &CatProcess{}
	if err := util.Clone(catProcessClone, c); err != nil {
		return nil
	}
	return catProcessClone
}

// SetStage sets cat's stage
func (c *CatProcess) SetStage(stage CatStage) {
	c.Stage = stage
}

// IsNew returns true if cat is new
func (c *CatProcess) IsNew() bool {
	return c.Stage == CatStageNew
}

// IsCreating returns true if cat is creating
func (c *CatProcess) IsCreating() bool {
	return c.Stage == CatStageCreating
}

// IsRunning returns true if cat is running
func (c *CatProcess) IsRunning() bool {
	return c.Stage == CatStageRunning
}

// IsUpdating returns true if cat is updating
func (c *CatProcess) IsUpdating() bool {
	return c.Stage == CatStageUpdating
}

// SetState sets cat's state
func (c *CatProcess) SetState(state CatState) {
	c.State = state
}

// IsStateNormal returns true if cat is normal
func (c *CatProcess) IsStateNormal() bool {
	return c.State == CatStateNormal
}

// IsStateAbNormal returns true if cat is abnormal
func (c *CatProcess) IsStateAbNormal() bool {
	return c.State == CatStateAbnormal
}

// IsDestroyed returns true if experiment is destroyed
func (c *CatProcess) IsDestroyed() bool {
	return c.Destroyed
}

// BeDestroyed sets true for destroyed
func (c *CatProcess) BeDestroyed() {
	c.Destroyed = true
}

// CatProcesses is an array of CatProcess
type CatProcesses []*CatProcess

// implement sort.Interface
func (catProcesses CatProcesses) Len() int { return len(catProcesses) }
func (catProcesses CatProcesses) Swap(i, j int) {
	catProcesses[i], catProcesses[j] = catProcesses[j], catProcesses[i]
}
func (catProcesses CatProcesses) Less(i, j int) bool {
	return catProcesses[i].Utime > catProcesses[j].Utime
}

// Names return the names of the CatProcessex
func (catProcesses CatProcesses) Names() []string {
	var names []string

	for _, cp := range catProcesses {
		names = append(names, cp.Name)
	}

	return names
}

// TiDBClusterConfig is the tidb cluster version when cat run
type TiDBClusterConfig struct {
	PDConfig   ComponentConfig `json:"pd_config"`
	TiDBConfig ComponentConfig `json:"tidb_config"`
	TiKVConfig ComponentConfig `json:"tikv_config"`
}
