package run

import (
	"os"
	"runtime"

	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	"go.linka.cloud/d2vm/pkg/qemu"
)

const (
	qemuNetworkingNone    string = "none"
	qemuNetworkingUser           = "user"
	qemuNetworkingTap            = "tap"
	qemuNetworkingBridge         = "bridge"
	qemuNetworkingDefault        = qemuNetworkingUser
)

var (
	defaultArch  string
	defaultAccel string
	enableGUI    bool
	disks        Disks
	data         string
	accel        string
	arch         string
	cpus         uint
	mem          uint
	qemuCmd      string
	qemuDetached bool
	networking   string
	publishFlags MultipleFlag

	QemuCmd = &cobra.Command{
		Use:   "qemu [options] [image-path]",
		Short: "Run the virtual machine image with qemu",
		Args:  cobra.ExactArgs(1),
		Run:   Qemu,
	}
)

func init() {
	switch runtime.GOARCH {
	case "arm64":
		defaultArch = "aarch64"
	case "amd64":
		defaultArch = "x86_64"
	case "s390x":
		defaultArch = "s390x"
	}
	switch {
	case runtime.GOARCH == "s390x":
		defaultAccel = "kvm"
	case haveKVM():
		defaultAccel = "kvm:tcg"
	case runtime.GOOS == "darwin":
		defaultAccel = "hvf:tcg"
	}
	flags := QemuCmd.Flags()

	flags.BoolVar(&enableGUI, "gui", false, "Set qemu to use video output instead of stdio")

	// Paths and settings for disks
	flags.Var(&disks, "disk", "Disk config, may be repeated. [file=]path[,size=1G][,format=qcow2]")

	// VM configuration
	flags.StringVar(&accel, "accel", defaultAccel, "Choose acceleration mode. Use 'tcg' to disable it.")
	flags.StringVar(&arch, "arch", defaultArch, "Type of architecture to use, e.g. x86_64, aarch64, s390x")
	flags.UintVar(&cpus, "cpus", 1, "Number of CPUs")
	flags.UintVar(&mem, "mem", 1024, "Amount of memory in MB")

	// Backend configuration
	flags.StringVar(&qemuCmd, "qemu", "", "Path to the qemu binary (otherwise look in $PATH)")
	flags.BoolVar(&qemuDetached, "detached", false, "Set qemu container to run in the background")

	// Networking
	flags.StringVar(&networking, "networking", qemuNetworkingDefault, "Networking mode. Valid options are 'default', 'user', 'bridge[,name]', tap[,name] and 'none'. 'user' uses QEMUs userspace networking. 'bridge' connects to a preexisting bridge. 'tap' uses a prexisting tap device. 'none' disables networking.`")

	flags.Var(&publishFlags, "publish", "Publish a vm's port(s) to the host (default [])")
}

func Qemu(cmd *cobra.Command, args []string) {
	path := args[0]

	if _, err := os.Stat(path); err != nil {
		log.Fatal(err)
	}
	var publishedPorts []PublishedPort
	for _, publish := range publishFlags {
		p, err := NewPublishedPort(publish)
		if err != nil {
			log.Fatal(err)
		}
		publishedPorts = append(publishedPorts, p)
	}
	opts := []qemu.Option{
		qemu.WithDisks(disks...),
		qemu.WithAccel(accel),
		qemu.WithArch(arch),
		qemu.WithCPUs(cpus),
		qemu.WithMemory(mem),
		qemu.WithNetworking(networking),
		qemu.WithStdin(os.Stdin),
		qemu.WithStdout(os.Stdout),
		qemu.WithStderr(os.Stderr),
	}
	if enableGUI {
		opts = append(opts, qemu.WithGUI())
	}
	if qemuDetached {
		opts = append(opts, qemu.WithDetached())
	}
	if err := qemu.Run(cmd.Context(), path, opts...); err != nil {
		log.Fatal(err)
	}
}

func haveKVM() bool {
	_, err := os.Stat("/dev/kvm")
	return !os.IsNotExist(err)
}
