package cli

import (
	"context"
	"fmt"
	"os"
	"strings"

	"github.com/spf13/pflag"

	"tsne.dev/hopper/internal/log"
)

var _ command = (*mainCommand)(nil)

type mainCommand struct {
	flags *pflag.FlagSet

	zone          string
	endpoint      string
	accessKeyFile string
	accessKey     string
	verbose       bool
	quiet         bool
}

func newMainCommand() *mainCommand {
	cmd := &mainCommand{}
	cmd.flags = newFlagset("hopper")
	cmd.flags.SetInterspersed(false)
	cmd.flags.StringVar(&cmd.zone,
		"zone",
		lookupEnv("BUNNY_ZONE", ""),
		"Bunny storage zone name (required) (env: BUNNY_ZONE)",
	)
	cmd.flags.StringVar(&cmd.endpoint,
		"endpoint",
		lookupEnv("BUNNY_ENDPOINT", "storage.bunnycdn.com"),
		"Bunny storage endpoint hostname (env: BUNNY_ENDPOINT)",
	)
	cmd.flags.StringVar(&cmd.accessKeyFile,
		"access-key-file",
		lookupEnv("BUNNY_ACCESS_KEY_FILE", ""),
		"path to a file containing the storage access key (env: BUNNY_ACCESS_KEY_FILE)",
	)
	cmd.flags.BoolVar(
		&cmd.verbose,
		"verbose",
		false,
		"print more details",
	)
	cmd.flags.BoolVar(
		&cmd.quiet,
		"quiet",
		false,
		"print only the final summary and errors",
	)
	return cmd
}

func (cmd *mainCommand) Usage() string {
	return "Usage:" +
		"\n    hopper [global flags] ⟨command⟩ [command flags]" +
		"\n" +
		"\nCommands:" +
		"\n    push    upload a release to Bunny Storage" +
		"\n    prune   remove orphaned files from Bunny Storage" +
		"\n" +
		"\nGlobal flags:" +
		"\n" + cmd.flags.FlagUsages()
}

func (cmd *mainCommand) Run(ctx context.Context, args []string) error {
	parseFlags(cmd, cmd.flags, args)

	log.SetVerbose(cmd.verbose)
	log.SetQuiet(cmd.quiet)

	if cmd.accessKeyFile == "" {
		cmd.accessKey = os.Getenv("BUNNY_ACCESS_KEY")
	}

	switch {
	case cmd.zone == "":
		return usageError{"missing zone (provide --zone or BUNNY_ZONE)"}
	case cmd.endpoint == "":
		// --endpoint has a default, so an empty value here is always deliberate.
		return usageError{"the endpoint must not be empty"}
	case cmd.accessKey == "" && cmd.accessKeyFile == "":
		return usageError{"missing access key (provide --access-key-file, BUNNY_ACCESS_KEY_FILE, or BUNNY_ACCESS_KEY)"}
	}

	subargs := cmd.flags.Args()
	if len(subargs) == 0 || subargs[0] == "" {
		return usageError{"missing subcommand"}
	}

	var sub command
	switch subargs[0] {
	case "push":
		sub = newPushCommand(cmd)
	case "prune":
		sub = newPruneCommand(cmd)
	default:
		return usageError{fmt.Sprintf("unknown subcommand %q", subargs[0])}
	}

	return runCommand(ctx, sub, subargs[1:])
}

func (cmd *mainCommand) lookupAccessKey() error {
	if cmd.accessKey == "" {
		accessKey, err := os.ReadFile(cmd.accessKeyFile)
		if err != nil {
			return fmt.Errorf("read access key file: %w", err)
		}
		cmd.accessKey = strings.TrimSpace(string(accessKey))
	}
	if cmd.accessKey == "" {
		return usageError{"empty access key"}
	}
	return nil
}

func lookupEnv(env string, defaultValue string) string {
	if val := os.Getenv(env); val != "" {
		return val
	}
	return defaultValue
}
