#!/usr/bin/env bb
;; vi: ft=clojure
;;
;; Create virtual machine with one command cuz sometimes you need more than a container
;;
(require '[babashka.process :refer [shell]]
         '[babashka.cli :as cli]
         '[babashka.http-client :as http]
         '[babashka.fs :as fs]
         '[clojure.java.io :as io]
         '[clojure.string :as string])

(def spec {:name {:require true :alias :n :desc "The name of your new VM"}
           :base {:require true :alias :b :desc "The base image that you want to create your VM from"}})

(def images {:ubuntu-20 {:url "https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64.img"
                         :image-name "focal-server-cloudimg-amd64.img"}
             :ubuntu-22 {:url "https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img"
                         :image-name "jammy-server-cloudimg-amd64.img"}})

(defn parse-args
  "Parse command line arguments"
  [args]
  (cli/parse-opts args {:spec spec
                        :error-fn
                        (fn [{:keys [spec type cause msg option] :as data}]
                          (if (= :org.babashka/cli type)
                            (case cause
                              :require
                              (println
                                (format "Missing required argument:\n%s"
                                        (cli/format-opts {:spec (select-keys spec [option])})))
                              (println msg))
                            (throw (ex-info msg data)))
                          (System/exit 1))}))

(def args (parse-args *command-line-args*))
(def image (get images (keyword (:base args))))

(when (nil? image)
  ;; TODO(AdeAttwood): Add a list of images to choose from
  (println "Image not found")
  (System/exit 1))

(def base-image-path (format "/var/lib/libvirt/images/%s", (:image-name image)))

(when (not (fs/exists? base-image-path))
  (println "Base image not found downloading it now")
  (io/copy (:body (http/get (:url image) {:as :stream}))
           (io/file base-image-path)))


(if (fs/exists? (format "/var/lib/libvirt/images/%s.img", (:name args)))
  (println "VM already exists")
  (shell (format "sudo qemu-img create -b %s -f qcow2 -F qcow2 /var/lib/libvirt/images/%s.img 10G" base-image-path (:name args))))

(spit "/tmp/cloud-metadata", (format "instance-id: %s\nlocal-hostname: %s\n", (:name args) (:name args)))
(spit "/tmp/cloud-userdata", (format
                               (clojure.string/join
                                 "\n" ["#cloud-config"
                                       ""
                                       "users:"
                                       "  - name: vm"
                                       "    password: vm"
                                       "    ssh_authorized_keys:"
                                       "      - %s"
                                       "    sudo: \"ALL=(ALL) NOPASSWD:ALL\""
                                       "    groups: sudo"
                                       "    shell: /bin/bash"])
                               (slurp (format "%s/.ssh/id_ed25519.pub" (System/getenv "HOME")) )))

(shell (format
         "sudo virt-install --name=%s --ram=2048 --vcpus=4 --import --disk path=%s,format=qcow2 --cloud-init meta-data=/tmp/cloud-metadata,user-data=/tmp/cloud-userdata --os-variant=ubuntu20.04 --network bridge=virbr0,model=virtio --graphics vnc,listen=0.0.0.0 --noautoconsole"
        (:name args)
        (format "/var/lib/libvirt/images/%s.img" (:name args))))

