package config

import (
	"github.com/derailed/k9s/internal/client"
)

const (
	defaultRefreshRate  = 2
	defaultMaxConnRetry = 5
)

// K9s tracks K9s configuration options.
type K9s struct {
	RefreshRate         int                 `yaml:"refreshRate"`
	MaxConnRetry        int                 `yaml:"maxConnRetry"`
	EnableMouse         bool                `yaml:"enableMouse"`
	Headless            bool                `yaml:"headless"`
	Logoless            bool                `yaml:"logoless"`
	Crumbsless          bool                `yaml:"crumbsless"`
	ReadOnly            bool                `yaml:"readOnly"`
	NoExitOnCtrlC       bool                `yaml:"noExitOnCtrlC"`
	NoIcons             bool                `yaml:"noIcons"`
	Logger              *Logger             `yaml:"logger"`
	CurrentContext      string              `yaml:"currentContext"`
	CurrentCluster      string              `yaml:"currentCluster"`
	Clusters            map[string]*Cluster `yaml:"clusters,omitempty"`
	Thresholds          Threshold           `yaml:"thresholds"`
	ScreenDumpDir       string              `yaml:"screenDumpDir"`
	manualRefreshRate   int
	manualHeadless      *bool
	manualLogoless      *bool
	manualCrumbsless    *bool
	manualReadOnly      *bool
	manualCommand       *string
	manualScreenDumpDir *string
}

// NewK9s create a new K9s configuration.
func NewK9s() *K9s {
	return &K9s{
		RefreshRate:   defaultRefreshRate,
		MaxConnRetry:  defaultMaxConnRetry,
		Logger:        NewLogger(),
		Clusters:      make(map[string]*Cluster),
		Thresholds:    NewThreshold(),
		ScreenDumpDir: K9sDefaultScreenDumpDir,
	}
}

func (k *K9s) CurrentContextDir() string {
	return SanitizeFilename(k.CurrentContext)
}

// ActivateCluster initializes the active cluster is not present.
func (k *K9s) ActivateCluster(ns string) {
	if _, ok := k.Clusters[k.CurrentCluster]; ok {
		return
	}
	cl := NewCluster()
	cl.Namespace.Active = ns
	k.Clusters[k.CurrentCluster] = cl
}

// OverrideRefreshRate set the refresh rate manually.
func (k *K9s) OverrideRefreshRate(r int) {
	k.manualRefreshRate = r
}

// OverrideHeadless toggle the header manually.
func (k *K9s) OverrideHeadless(b bool) {
	k.manualHeadless = &b
}

// OverrideLogoless toggle the k9s logo manually.
func (k *K9s) OverrideLogoless(b bool) {
	k.manualLogoless = &b
}

// OverrideCrumbsless tooh the crumbslessness manually.
func (k *K9s) OverrideCrumbsless(b bool) {
	k.manualCrumbsless = &b
}

// OverrideReadOnly set the readonly mode manually.
func (k *K9s) OverrideReadOnly(b bool) {
	if b {
		k.manualReadOnly = &b
	}
}

// OverrideWrite set the write mode manually.
func (k *K9s) OverrideWrite(b bool) {
	if b {
		var flag bool
		k.manualReadOnly = &flag
	}
}

// OverrideCommand set the command manually.
func (k *K9s) OverrideCommand(cmd string) {
	k.manualCommand = &cmd
}

// OverrideScreenDumpDir set the screen dump dir manually.
func (k *K9s) OverrideScreenDumpDir(dir string) {
	k.manualScreenDumpDir = &dir
}

// IsHeadless returns headless setting.
func (k *K9s) IsHeadless() bool {
	h := k.Headless
	if k.manualHeadless != nil && *k.manualHeadless {
		h = *k.manualHeadless
	}

	return h
}

// IsLogoless returns logoless setting.
func (k *K9s) IsLogoless() bool {
	h := k.Logoless
	if k.manualLogoless != nil && *k.manualLogoless {
		h = *k.manualLogoless
	}

	return h
}

// IsCrumbsless returns crumbsless setting.
func (k *K9s) IsCrumbsless() bool {
	h := k.Crumbsless
	if k.manualCrumbsless != nil && *k.manualCrumbsless {
		h = *k.manualCrumbsless
	}

	return h
}

// GetRefreshRate returns the current refresh rate.
func (k *K9s) GetRefreshRate() int {
	rate := k.RefreshRate
	if k.manualRefreshRate != 0 {
		rate = k.manualRefreshRate
	}

	return rate
}

// IsReadOnly returns the readonly setting.
func (k *K9s) IsReadOnly() bool {
	readOnly := k.ReadOnly
	if k.manualReadOnly != nil {
		readOnly = *k.manualReadOnly
	}

	return readOnly
}

// ActiveCluster returns the currently active cluster.
func (k *K9s) ActiveCluster() *Cluster {
	if k.Clusters == nil {
		k.Clusters = map[string]*Cluster{}
	}
	if c, ok := k.Clusters[k.CurrentCluster]; ok {
		return c
	}
	k.Clusters[k.CurrentCluster] = NewCluster()

	return k.Clusters[k.CurrentCluster]
}

func (k *K9s) GetScreenDumpDir() string {
	screenDumpDir := k.ScreenDumpDir
	if k.manualScreenDumpDir != nil && *k.manualScreenDumpDir != "" {
		screenDumpDir = *k.manualScreenDumpDir
	}

	if screenDumpDir == "" {
		return K9sDefaultScreenDumpDir
	}

	return screenDumpDir
}

func (k *K9s) validateDefaults() {
	if k.RefreshRate <= 0 {
		k.RefreshRate = defaultRefreshRate
	}
	if k.MaxConnRetry <= 0 {
		k.MaxConnRetry = defaultMaxConnRetry
	}
	if k.ScreenDumpDir == "" {
		k.ScreenDumpDir = K9sDefaultScreenDumpDir
	}
}

func (k *K9s) validateClusters(c client.Connection, ks KubeSettings) {
	cc, err := ks.ClusterNames()
	if err != nil {
		return
	}
	for key, cluster := range k.Clusters {
		cluster.Validate(c, ks)
		if _, ok := cc[key]; ok {
			continue
		}
		if k.CurrentCluster == key {
			k.CurrentCluster = ""
		}
		delete(k.Clusters, key)
	}
}

// Validate the current configuration.
func (k *K9s) Validate(c client.Connection, ks KubeSettings) {
	k.validateDefaults()
	if k.Clusters == nil {
		k.Clusters = map[string]*Cluster{}
	}
	k.validateClusters(c, ks)

	if k.Logger == nil {
		k.Logger = NewLogger()
	} else {
		k.Logger.Validate(c, ks)
	}
	if k.Thresholds == nil {
		k.Thresholds = NewThreshold()
	}
	k.Thresholds.Validate(c, ks)

	if context, err := ks.CurrentContextName(); err == nil && len(k.CurrentContext) == 0 {
		k.CurrentContext = context
		k.CurrentCluster = ""
	}

	if cl, err := ks.CurrentClusterName(); err == nil && len(k.CurrentCluster) == 0 {
		k.CurrentCluster = cl
	}

	if _, ok := k.Clusters[k.CurrentCluster]; !ok {
		k.Clusters[k.CurrentCluster] = NewCluster()
	}
	k.Clusters[k.CurrentCluster].Validate(c, ks)
}
