// Copyright 2017 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.package spec

package util

import (
	"fmt"
	"net/http"
	"os"
	"time"

	"github.com/pingcap/tidb-operator/pkg/apis/pingcap.com/v1"
	"github.com/pingcap/tidb-operator/pkg/util/label"
	"github.com/pingcap/tidb-operator/pkg/util/pdutil"
	corev1 "k8s.io/api/core/v1"
)

const (
	// TiKVStoreDir is the sub directory under hostPath for tikv
	TiKVStoreDir = "tikv"

	// TiDBScheduler is our scheduler extender name
	TiDBScheduler = "tidb-scheduler"

	// NodeUnreachablePodReason is defined in k8s.io/kubernetes/pkg/util/node
	// but not in client-go and apimachinery, so we define it here
	NodeUnreachablePodReason = "NodeLost"

	// TaintNodeNotReady is defined in k8s.io/kubernetes/plugin/pkg/scheduler/algorithm
	// when feature-gates TaintBasedEvictions=true is enabled for k8s,
	// this taint will be added to node when node is not ready
	// and removed when node becomes ready
	TaintNodeNotReady = "node.alpha.kubernetes.io/notReady"
	// TaintNodeUnreachable is defined in k8s.io/kubernetes/pluign/pkg/scheduler/algorithm
	// when feature-gates TaintBasedEvictions=true is enabled for k8s,
	// this taint will be added to node when node is unreachable
	// and removed when node becomes reachable
	TaintNodeUnreachable = "node.alpha.kubernetes.io/unreachable"

	// ClientCfgQPS configures the Operator go client QPS.
	ClientCfgQPS = 1000
	// ClientCfgBurst configures the Operator go client Burst.
	ClientCfgBurst = 1000
)

// GetPodNames returns the pods' name
func GetPodNames(pods []*corev1.Pod) (names []string) {
	for _, p := range pods {
		names = append(names, p.Name)
	}
	return
}

// ContainsString checks if a given slice of strings contains the provided string.
func ContainsString(slice []string, str string) bool {
	for _, item := range slice {
		if item == str {
			return true
		}
	}
	return false
}

// StartPprofServer starts a http server for pprof
func StartPprofServer() error {
	return http.ListenAndServe(":6060", nil)
}

// NewPDClientForCluster creates a new PD client from PD service for a tidb-cluster
func NewPDClientForCluster(ns string, clusterName string, timeout time.Duration) *pdutil.Client {
	return pdutil.New(fmt.Sprintf("http://%s-pd.%s:2379", clusterName, ns), timeout)
}

// AnnProm adds annotations for prometheus scraping metrics
// port is determined by container port in pod spec with name metrics
func AnnProm(ann map[string]string, path string) map[string]string {
	if ann == nil {
		ann = make(map[string]string)
	}
	ann["prometheus.io/scrape"] = "true"
	ann["prometheus.io/path"] = path
	return ann
}

// AnnPaused adds annotations for whether to pause all controllers
func AnnPaused(ann map[string]string, cluster *v1.TidbCluster) map[string]string {
	if ann == nil {
		ann = make(map[string]string)
	}
	if cluster.Spec.Paused {
		ann[label.AnnPaused] = "true"
	} else {
		ann[label.AnnPaused] = "false"
	}
	return ann
}

// FilterSlice filter slice b 's element from slice a
func FilterSlice(a []string, b []string) []string {
	result := a[:0]
	for _, valA := range a {
		contain := false
		for _, valB := range b {
			if valA == valB {
				contain = true
				break
			}
		}
		if !contain {
			result = append(result, valA)
		}
	}

	return result
}

// MkdirP creates a directory named path, along with any necessary parents, and returns nil,
// or else returns an error.
func MkdirP(path string, erm os.FileMode) error {
	_, err := os.Stat(path)
	if err != nil {
		if !os.IsNotExist(err) {
			return err
		}
		return os.MkdirAll(path, erm)
	}

	return nil
}
