// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>

package update_self

import (
	"fmt"
	"kitty"
	"os"
	"path/filepath"
	"runtime"

	"kitty/tools/cli"
	"kitty/tools/tty"
	"kitty/tools/tui"
	"kitty/tools/utils"

	"golang.org/x/sys/unix"
)

var _ = fmt.Print

type Options struct {
	FetchVersion string
}

func update_self(version string) (err error) {
	exe := ""
	exe, err = os.Executable()
	if err != nil {
		return fmt.Errorf("Failed to determine path to kitten: %w", err)
	}
	exe, err = filepath.EvalSymlinks(exe)
	if err != nil {
		return err
	}
	if !kitty.IsStandaloneBuild {
		return fmt.Errorf("This is not a standalone kitten executable. You must update all of kitty instead.")
	}
	rv := "v" + version
	if version == "nightly" {
		rv = version
	}
	url_base := fmt.Sprintf("https://github.com/kovidgoyal/kitty/releases/download/%s", rv)
	if version == "latest" {
		url_base = "https://github.com/kovidgoyal/kitty/releases/latest/download"
	}
	url := fmt.Sprintf("%s/kitten-%s-%s", url_base, runtime.GOOS, runtime.GOARCH)
	dest, err := os.CreateTemp(filepath.Dir(exe), "kitten.")
	if err != nil {
		return err
	}
	defer func() { os.Remove(dest.Name()) }()

	if !tty.IsTerminal(os.Stdout.Fd()) {
		fmt.Println("Downloading:", url)
		err = utils.DownloadToFile(exe, url, nil, nil)
		if err != nil {
			return err
		}
		fmt.Println("Downloaded to:", exe)
	} else {
		err = tui.DownloadFileWithProgress(exe, url, true)
		if err != nil {
			return err
		}
	}
	fmt.Print("Updated to: ")
	return unix.Exec(exe, []string{"kitten", "--version"}, os.Environ())
}

func EntryPoint(root *cli.Command) *cli.Command {
	sc := root.AddSubCommand(&cli.Command{
		Name:             "update-self",
		Usage:            "update-self [options ...]",
		ShortDescription: "Update this kitten binary",
		HelpText:         "Update this kitten binary in place to the latest available version.",
		Run: func(cmd *cli.Command, args []string) (ret int, err error) {
			if len(args) != 0 {
				return 1, fmt.Errorf("No command line arguments are allowed")
			}
			opts := &Options{}
			err = cmd.GetOptionValues(opts)
			if err != nil {
				return 1, err
			}
			return 0, update_self(opts.FetchVersion)
		},
	})
	sc.Add(cli.OptionSpec{
		Name:    "--fetch-version",
		Default: "latest",
		Help:    "The version to fetch. The special words :code:`latest` and :code:`nightly` fetch the latest stable and nightly release respectively. Other values can be, for example: 0.27.1.",
	})
	return sc
}
