package cookie

import (
	"crypto/sha1"
	"fmt"
	"strings"

	"github.com/traefik/traefik/v2/pkg/log"
)

const cookieNameLength = 6

// GetName of a cookie.
func GetName(cookieName, backendName string) string {
	if len(cookieName) != 0 {
		return sanitizeName(cookieName)
	}

	return GenerateName(backendName)
}

// GenerateName Generate a hashed name.
func GenerateName(backendName string) string {
	data := []byte("_TRAEFIK_BACKEND_" + backendName)

	hash := sha1.New()
	_, err := hash.Write(data)
	if err != nil {
		// Impossible case
		log.WithoutContext().Errorf("Fail to create cookie name: %v", err)
	}

	return fmt.Sprintf("_%x", hash.Sum(nil))[:cookieNameLength]
}

// sanitizeName According to [RFC 2616](https://www.ietf.org/rfc/rfc2616.txt) section 2.2.
func sanitizeName(backend string) string {
	sanitizer := func(r rune) rune {
		switch r {
		case '!', '#', '$', '%', '&', '\'', '*', '+', '-', '.', '^', '`', '|', '~':
			return r
		}

		switch {
		case 'a' <= r && r <= 'z':
			fallthrough
		case 'A' <= r && r <= 'Z':
			fallthrough
		case '0' <= r && r <= '9':
			return r
		default:
			return '_'
		}
	}

	return strings.Map(sanitizer, backend)
}
