/*
Copyright 2016 The Rook Authors. All rights reserved.

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,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package k8sutil for Kubernetes helpers.
package k8sutil

import (
	"fmt"
	"os"
	"strings"

	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/util/validation"
)

const (
	BinariesMountPath = "/rook"
)

// PathToVolumeName converts a path to a valid volume name
func PathToVolumeName(path string) string {
	// kubernetes volume names must match this regex: [a-z0-9]([-a-z0-9]*[a-z0-9])?

	sanitized := make([]rune, 0, len(path))
	for _, c := range path {
		switch {
		case '0' <= c && c <= '9':
			fallthrough
		case 'a' <= c && c <= 'z':
			sanitized = append(sanitized, c)
			continue
		case 'A' <= c && c <= 'Z':
			// convert upper to lower case
			sanitized = append(sanitized, c+('a'-'A'))
		default:
			// convert any non alphanum char to a hyphen
			sanitized = append(sanitized, '-')
		}
	}
	volumeName := string(sanitized)

	// trim any leading/trailing hyphens
	volumeName = strings.TrimPrefix(volumeName, "-")
	volumeName = strings.TrimSuffix(volumeName, "-")

	if len(volumeName) > validation.DNS1123LabelMaxLength {
		// keep an equal sample of the original name from both the beginning and from the end,
		// and add some characters from a hash of the full name to help prevent name collisions.
		// Make room for 3 hyphens in the middle (~ellipsis) and 1 hyphen to separate the hash.
		hashLength := 8
		sampleLength := int((validation.DNS1123LabelMaxLength - hashLength - 3 - 1) / 2)
		first := volumeName[0:sampleLength]
		last := volumeName[len(volumeName)-sampleLength:]
		hash := Hash(volumeName)
		volumeName = fmt.Sprintf("%s---%s-%s", first, last, hash[0:hashLength])
	}

	return volumeName
}

// NodeConfigURI returns the node config URI path for this node
func NodeConfigURI() (string, error) {
	nodeName := os.Getenv(NodeNameEnvVar)
	if nodeName == "" {
		return "", fmt.Errorf("cannot detect the node name. Please provide using the downward API in the rook operator manifest file")
	}
	return fmt.Sprintf("api/v1/nodes/%s/proxy/configz", nodeName), nil
}

func BinariesMountInfo() (v1.EnvVar, v1.Volume, v1.VolumeMount) {
	// To get rook inside the container, the config init container needs to copy "tini" and "rook" binaries into a volume.
	// Set the config flag so rook will copy the binaries.
	// Create the volume and mount that will be shared between the init container and the daemon container
	volumeName := "rookbinaries"
	e := v1.EnvVar{Name: "ROOK_COPY_BINARIES_PATH", Value: BinariesMountPath}
	v := v1.Volume{Name: volumeName, VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}}}
	m := v1.VolumeMount{Name: volumeName, MountPath: BinariesMountPath}
	return e, v, m
}
