package daemon

import (
	"context"
	"strings"

	"banger/internal/model"
	"banger/internal/system"
)

// defaultVsockHostDevice is the vhost-vsock device file every
// Firecracker guest relies on to talk to the host via vsock. Tests
// point at a tempfile by setting VMService.vsockHostDevice; production
// wiring defaults the field to this path in wireServices.
const defaultVsockHostDevice = "/dev/vhost-vsock"

func (s *VMService) validateStartPrereqs(ctx context.Context, vm model.VMRecord, image model.Image) error {
	checks := system.NewPreflight()
	s.addBaseStartPrereqs(checks, image)
	s.capHooks.addStartPrereqs(ctx, checks, vm, image)
	return checks.Err("vm start preflight failed")
}

func (s *VMService) validateWorkDiskResizePrereqs() error {
	checks := system.NewPreflight()
	checks.RequireCommand("truncate", toolHint("truncate"))
	checks.RequireCommand("e2fsck", `install e2fsprogs`)
	checks.RequireCommand("resize2fs", `install e2fsprogs`)
	return checks.Err("work disk resize preflight failed")
}

func (s *VMService) addBaseStartPrereqs(checks *system.Preflight, image model.Image) {
	s.addBaseStartCommandPrereqs(checks)
	checks.RequireExecutable(s.config.FirecrackerBin, "firecracker binary", `install firecracker or set "firecracker_bin"`)
	if helper, err := vsockAgentBinary(s.layout); err == nil {
		checks.RequireExecutable(helper, "vsock agent helper", `run 'make build' or reinstall banger`)
	} else {
		checks.Addf("%v", err)
	}
	checks.RequireFile(s.vsockHostDevice, "vsock host device", "load the vhost_vsock kernel module on the host")
	checks.RequireFile(image.RootfsPath, "rootfs image", "select a valid registered image")
	checks.RequireFile(image.KernelPath, "kernel image", `re-register or rebuild the image with a valid kernel`)
	if strings.TrimSpace(image.InitrdPath) != "" {
		checks.RequireFile(image.InitrdPath, "initrd image", `re-register or rebuild the image with a valid initrd`)
	}
}

func (s *VMService) addBaseStartCommandPrereqs(checks *system.Preflight) {
	for _, command := range []string{"ip", "dmsetup", "losetup", "blockdev", "truncate", "pgrep", "chown", "chmod", "kill", "e2cp", "e2rm", "debugfs"} {
		checks.RequireCommand(command, toolHint(command))
	}
}

func toolHint(command string) string {
	switch command {
	case "ip":
		return "install iproute2"
	case "iptables":
		return "install iptables"
	case "sysctl", "losetup", "blockdev", "mount", "umount":
		return "install util-linux"
	case "dmsetup":
		return "install device-mapper"
	case "pgrep", "kill":
		return "install procps"
	case "chown", "chmod", "cp", "truncate":
		return "install coreutils"
	case "e2fsck", "resize2fs", "debugfs", "mkfs.ext4":
		return "install e2fsprogs"
	case "e2cp", "e2rm":
		return "install e2tools"
	default:
		return ""
	}
}
