package main

import (
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/fatih/color"
	"github.com/influxdata/influx-cli/v2/pkg/cli/middleware"
	"github.com/urfave/cli"
)

// Fields set via ldflags at build time.
var (
	version = "dev"
	commit  = "none"
	date    = ""
)

func init() {
	if len(date) == 0 {
		date = time.Now().UTC().Format(time.RFC3339)
	}

	cli.VersionFlag = nil
}

func cloudOnlyCommands() []cli.Command {
	// Include commands that are only intended to work on an InfluxDB Cloud host in this list. A specific error message
	// will be returned if these commands are run on an InfluxDB OSS host.
	cmds := []cli.Command{
		newBucketSchemaCmd(),
		newScriptsCmd(),
	}

	return middleware.AddMWToCmds(cmds, middleware.CloudOnly)
}

func ossOnlyCommands() []cli.Command {
	// Include commands that are only intended to work on an InfluxDB OSS host in this list. A specific error message will
	// be returned if these commands are run on an InfluxDB Cloud host.
	cmds := []cli.Command{
		newPingCmd(),
		newSetupCmd(),
		newBackupCmd(),
		newRestoreCmd(),
		newRemoteCmd(),
		newReplicationCmd(),
		newServerConfigCommand(),
	}

	return middleware.AddMWToCmds(cmds, middleware.OSSOnly)
}

func allCommands() []cli.Command {
	// Commands which should work with an InfluxDB Cloud or InfluxDB OSS host should be included in this list.
	commonCmds := []cli.Command{
		newVersionCmd(),
		newWriteCmd(),
		newBucketCmd(),
		newCompletionCmd(),
		newQueryCmd(),
		newConfigCmd(),
		newOrgCmd(),
		newDeleteCmd(),
		newUserCmd(),
		newTaskCommand(),
		newTelegrafsCommand(),
		newDashboardsCommand(),
		newExportCmd(),
		newSecretCommand(),
		newV1SubCommand(),
		newAuthCommand(),
		newApplyCmd(),
		newStacksCmd(),
		newTemplateCmd(),
	}
	specificCmds := append(cloudOnlyCommands(), ossOnlyCommands()...)

	return append(commonCmds, specificCmds...)
}

func newApp() cli.App {
	return cli.App{
		Name:                 "influx",
		Usage:                "Influx Client",
		UsageText:            "influx [command]\n\nHINT: If you are looking for the InfluxQL shell from 1.x, run \"influx v1 shell\"",
		EnableBashCompletion: true,
		BashComplete:         cli.DefaultAppComplete,
		Commands:             allCommands(),
		Before:               middleware.WithBeforeFns(withContext(), middleware.NoArgs),
		ExitErrHandler:       middleware.HandleExit,
	}
}

// This creates a new slice and replaces `-t "-FOO-TOKEN"` with `-t=-FOO-TOKEN`
// This is necessary to do because the command line arg:
// `-t "-FOO-TOKEN"“ will be parsed as two separate flags instead of a flag and token value.
func ReplaceTokenArg(args []string) []string {
	if len(args) == 0 {
		return []string{}
	}
	newArgs := make([]string, len(args))
	copy(newArgs, args)
	for i, arg := range newArgs[:len(newArgs)-1] {
		switch arg {
		case "--token", "-t":
			if strings.HasPrefix(newArgs[i+1], "-") {
				color.HiYellow("warning: %[1]s %[2]s interpreted as %[1]s=%[2]s, consider using %[1]s=%[2]s syntax when tokens start with a hyphen",
					newArgs[i], newArgs[i+1],
				)
			}
			newArgs[i] = strings.Join(newArgs[i:i+2], "=")
			newArgs = append(newArgs[:i+1], newArgs[i+2:]...)
		}
	}
	return newArgs
}

func main() {
	app := newApp()
	args := ReplaceTokenArg(os.Args)
	if err := app.Run(args); err != nil {
		// Errors will normally be handled by cli.HandleExitCoder via ExitErrHandler set on app. Any error not implementing
		// the cli.ExitCoder interface can be handled here.
		_, _ = fmt.Fprintf(os.Stderr, "Error: %v\n", err)
		os.Exit(1)
	}
}
