package envflag

import (
	"flag"
	"log"
	"os"
	"strings"

	"github.com/VictoriaMetrics/VictoriaMetrics/lib/envtemplate"
)

var (
	enable = flag.Bool("envflag.enable", false, "Whether to enable reading flags from environment variables in addition to the command line. "+
		"Command line flag values have priority over values from environment vars. "+
		"Flags are read only from the command line if this flag isn't set. See https://docs.victoriametrics.com/#environment-variables for more details")
	prefix = flag.String("envflag.prefix", "", "Prefix for environment variables if -envflag.enable is set")
)

// Parse parses environment vars and command-line flags.
//
// Flags set via command-line override flags set via environment vars.
//
// This function must be called instead of flag.Parse() before using any flags in the program.
func Parse() {
	ParseFlagSet(flag.CommandLine, os.Args[1:])
}

// ParseFlagSet parses the given args into the given fs.
func ParseFlagSet(fs *flag.FlagSet, args []string) {
	args = expandArgs(args)
	if err := fs.Parse(args); err != nil {
		// Do not use lib/logger here, since it is uninitialized yet.
		log.Fatalf("cannot parse flags %q: %s", args, err)
	}
	if fs.NArg() > 0 {
		// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4845
		log.Fatalf("unprocessed command-line args left: %s; the most likely reason is missing `=` between boolean flag name and value; "+
			"see https://pkg.go.dev/flag#hdr-Command_line_flag_syntax", fs.Args())
	}
	if !*enable {
		return
	}
	// Remember explicitly set command-line flags.
	flagsSet := make(map[string]bool)
	fs.Visit(func(f *flag.Flag) {
		flagsSet[f.Name] = true
	})

	// Obtain the remaining flag values from environment vars.
	fs.VisitAll(func(f *flag.Flag) {
		if flagsSet[f.Name] {
			// The flag is explicitly set via command-line.
			return
		}
		// Get flag value from environment var.
		fname := getEnvFlagName(f.Name)
		if v, ok := envtemplate.LookupEnv(fname); ok {
			if err := fs.Set(f.Name, v); err != nil {
				// Do not use lib/logger here, since it is uninitialized yet.
				log.Fatalf("cannot set flag %s to %q, which is read from env var %q: %s", f.Name, v, fname, err)
			}
		}
	})
}

// expandArgs substitutes %{ENV_VAR} placeholders inside args
// with the corresponding environment variable values.
func expandArgs(args []string) []string {
	dstArgs := make([]string, 0, len(args))
	for _, arg := range args {
		s, err := envtemplate.ReplaceString(arg)
		if err != nil {
			// Do not use lib/logger here, since it is uninitialized yet.
			log.Fatalf("cannot process arg %q: %s", arg, err)
		}
		if len(s) > 0 {
			dstArgs = append(dstArgs, s)
		}
	}
	return dstArgs
}

func getEnvFlagName(s string) string {
	// Substitute dots with underscores, since env var names cannot contain dots.
	// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/311#issuecomment-586354129 for details.
	s = strings.ReplaceAll(s, ".", "_")
	return *prefix + s
}