// Package cli provides the interface for the command-line.
package cli

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path"

	"coopcloud.tech/abra/cli/app"
	"coopcloud.tech/abra/cli/catalogue"
	"coopcloud.tech/abra/cli/internal"
	"coopcloud.tech/abra/cli/recipe"
	"coopcloud.tech/abra/cli/record"
	"coopcloud.tech/abra/cli/server"
	"coopcloud.tech/abra/pkg/autocomplete"
	"coopcloud.tech/abra/pkg/config"
	"coopcloud.tech/abra/pkg/web"
	"github.com/sirupsen/logrus"
	"github.com/urfave/cli"
)

// AutoCompleteCommand helps people set up auto-complete in their shells
var AutoCompleteCommand = cli.Command{
	Name:    "autocomplete",
	Aliases: []string{"ac"},
	Usage:   "Configure shell autocompletion (recommended)",
	Description: `
Set up auto-completion in your shell by downloading the relevant files and
laying out what additional information must be loaded. Supported shells are as
follows: bash, fizsh & zsh.

Example:

    abra autocomplete bash
`,
	ArgsUsage: "<shell>",
	Flags: []cli.Flag{
		internal.DebugFlag,
	},
	Action: func(c *cli.Context) error {
		shellType := c.Args().First()

		if shellType == "" {
			internal.ShowSubcommandHelpAndError(c, errors.New("no shell provided"))
		}

		supportedShells := map[string]bool{
			"bash":  true,
			"zsh":   true,
			"fizsh": true,
		}

		if _, ok := supportedShells[shellType]; !ok {
			logrus.Fatalf("%s is not a supported shell right now, sorry", shellType)
		}

		if shellType == "fizsh" {
			shellType = "zsh" // handled the same on the autocompletion side
		}

		autocompletionDir := path.Join(config.ABRA_DIR, "autocompletion")
		if err := os.Mkdir(autocompletionDir, 0764); err != nil {
			if !os.IsExist(err) {
				logrus.Fatal(err)
			}
			logrus.Debugf("%s already created", autocompletionDir)
		}

		autocompletionFile := path.Join(config.ABRA_DIR, "autocompletion", shellType)
		if _, err := os.Stat(autocompletionFile); err != nil && os.IsNotExist(err) {
			url := fmt.Sprintf("https://git.coopcloud.tech/coop-cloud/abra/raw/branch/main/scripts/autocomplete/%s", shellType)
			logrus.Infof("fetching %s", url)
			if err := web.GetFile(autocompletionFile, url); err != nil {
				logrus.Fatal(err)
			}
		}

		switch shellType {
		case "bash":
			fmt.Println(fmt.Sprintf(`
# Run the following commands to install auto-completion
sudo mkdir /etc/bash_completion.d/
sudo cp %s /etc/bash_completion.d/abra
echo "source /etc/bash_completion.d/abra" >> ~/.bashrc
# And finally run "abra app ps <hit tab key>" to test things are working, you should see app domains listed!
`, autocompletionFile))
		case "zsh":
			fmt.Println(fmt.Sprintf(`
# Run the following commands to install auto-completion
sudo mkdir /etc/zsh/completion.d/
sudo cp %s /etc/zsh/completion.d/abra
echo "PROG=abra\n_CLI_ZSH_AUTOCOMPLETE_HACK=1\nsource /etc/zsh/completion.d/abra" >> ~/.zshrc
# And finally run "abra app ps <hit tab key>" to test things are working, you should see app domains listed!
`, autocompletionFile))
		}

		return nil
	},
}

// UpgradeCommand upgrades abra in-place.
var UpgradeCommand = cli.Command{
	Name:    "upgrade",
	Aliases: []string{"u"},
	Usage:   "Upgrade Abra itself",
	Description: `
Upgrade Abra in-place with the latest stable or release candidate.

Pass "-r/--rc" to install the latest release candidate. Please bear in mind
that it may contain catastrophic bugs. Thank you very much for the testing
efforts!
`,
	Flags: []cli.Flag{internal.RCFlag},
	Action: func(c *cli.Context) error {
		mainURL := "https://install.abra.coopcloud.tech"
		cmd := exec.Command("bash", "-c", fmt.Sprintf("wget -q -O- %s | bash", mainURL))

		if internal.RC {
			releaseCandidateURL := "https://git.coopcloud.tech/coop-cloud/abra/raw/branch/main/scripts/installer/installer"
			cmd = exec.Command("bash", "-c", fmt.Sprintf("wget -q -O- %s | bash -s -- --rc", releaseCandidateURL))
		}

		logrus.Debugf("attempting to run %s", cmd)

		if err := internal.RunCmd(cmd); err != nil {
			logrus.Fatal(err)
		}

		return nil
	},
}

func newAbraApp(version, commit string) *cli.App {
	app := &cli.App{
		Name: "abra",
		Usage: `The Co-op Cloud command-line utility belt 🎩🐇
    ____                           ____ _                 _
   / ___|___         ___  _ __    / ___| | ___  _   _  __| |
  | |   / _ \ _____ / _ \| '_ \  | |   | |/ _ \| | | |/ _' |
  | |__| (_) |_____| (_) | |_) | | |___| | (_) | |_| | (_| |
   \____\___/       \___/| .__/   \____|_|\___/ \__,_|\__,_|
                         |_|
`,
		Version: fmt.Sprintf("%s-%s", version, commit[:7]),
		Commands: []cli.Command{
			app.AppCommand,
			server.ServerCommand,
			recipe.RecipeCommand,
			catalogue.CatalogueCommand,
			record.RecordCommand,
			UpgradeCommand,
			AutoCompleteCommand,
		},
		BashComplete: autocomplete.SubcommandComplete,
	}

	app.EnableBashCompletion = true

	app.Before = func(c *cli.Context) error {
		paths := []string{
			config.ABRA_DIR,
			path.Join(config.SERVERS_DIR),
			path.Join(config.RECIPES_DIR),
			path.Join(config.VENDOR_DIR),
			path.Join(config.BACKUP_DIR),
		}

		for _, path := range paths {
			if err := os.Mkdir(path, 0764); err != nil {
				if !os.IsExist(err) {
					logrus.Fatal(err)
				}
				continue
			}
		}

		logrus.Debugf("abra version %s, commit %s", version, commit)

		return nil
	}
	return app
}

// RunApp runs CLI abra app.
func RunApp(version, commit string) {
	app := newAbraApp(version, commit)

	if err := app.Run(os.Args); err != nil {
		logrus.Fatal(err)
	}
}