// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of Cilium

package utils

import (
	"fmt"
	"os"
	"regexp"
	"strings"

	"github.com/blang/semver/v4"

	"github.com/cilium/cilium-cli/defaults"
)

var versionRegexp = regexp.MustCompile(`^((v?(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[a-zA-Z0-9.]+)*|[a-zA-Z0-9-_.@:]*:[a-zA-Z0-9-_.@:]+)|([0-9a-fA-F]{40})|(latest))$`).MatchString

func CheckVersion(version string) error {
	if !versionRegexp(version) && version != "" {
		return fmt.Errorf("invalid syntax %q for image tag", version)
	}
	return nil
}

func ParseCiliumVersion(version string) (semver.Version, error) {
	return semver.ParseTolerant(version)
}

type ImagePathMode int

const (
	ImagePathExcludeDigest ImagePathMode = iota
	ImagePathIncludeDigest
	CLIModeVariableName = "CILIUM_CLI_MODE"
)

var imageRegexp = regexp.MustCompile(`\A(.*?)(?:(:.*?)(@sha256:[0-9a-f]{64})?)?\z`)

// BuildImagePath builds a fully-qualified image path from the given
// user image and version and default image.
//
// NOTE: Currently 'userVersion' is never passed as an empty string as
// it is defaulted on the CLI interface to the default version.
//
// If 'userVersion' can already contains a colon (':') it is simply
// concatenated with the image string. This is useful for using the
// "latest" image in testing with "--version :latest". Without the
// colon 'userVersion' is always prepended with 'v' if it is missing.
// This is also useful for postfixing the image name with "-ci", for
// example ("--version -ci:4fac771179959ca575eb6f993d566653d3bfa167").
//
// If imagePathMode is ImagePathIncludeDigest and the resulting image is well
// known (i.e. is in defaults.WellKnownImageDigests) then its digest is appended
// to the path.
func BuildImagePath(userImage, userVersion, defaultImage, defaultVersion string, imagePathMode ImagePathMode) string {
	var image string
	switch {
	case userImage == "" && userVersion == "":
		if strings.Contains(defaultVersion, ":") {
			image = defaultImage + defaultVersion
		} else {
			image = defaultImage + ":" + defaultVersion
		}
	case userImage == "" && strings.Contains(userVersion, ":"):
		// userVersion already contains the colon. Useful for ":latest",
		// or for "-ci:<hash>"
		image = defaultImage + userVersion
	case userImage == "" && !strings.HasPrefix(userVersion, "v"):
		image = defaultImage + ":v" + userVersion
	case userImage == "":
		image = defaultImage + ":" + userVersion
	case strings.Contains(userImage, ":"):
		// Fully-qualified userImage?
		image = userImage
	case strings.Contains(userVersion, ":"):
		// ':' in userVersion?
		image = userImage + userVersion
	default:
		image = userImage + ":" + userVersion
	}

	switch imagePathMode {
	case ImagePathIncludeDigest:
		image = image + defaults.WellKnownImageDigests[image]
	case ImagePathExcludeDigest:
		if m := imageRegexp.FindStringSubmatch(image); m != nil {
			image = m[1] + m[2]
		}
	}
	return image
}

func Contains(l []string, v string) bool {
	for _, s := range l {
		if s == v {
			return true
		}
	}
	return false
}

// IsInHelmMode returns true if cilium-cli is in "helm" mode. Otherwise, it returns false.
func IsInHelmMode() bool {
	return os.Getenv(CLIModeVariableName) != "classic"
}
