package main

import (
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"strings"
)

// Various commands that we need for this process
const (
	CMD_BLKID   = "/usr/sbin/blkid"
	CMD_FINDMNT = "/usr/bin/findmnt"
	CMD_MOUNT   = "/usr/bin/mount"
	CMD_NMCLI   = "/usr/bin/nmcli"
	CMD_UMOUNT  = "/usr/bin/umount"
)

// Various file paths we need
const (
	MOUNTPOINT       = "/var/wifi"
	CONFIG_FILE      = "wifi.txt"
	CONFIG_PATH_FULL = MOUNTPOINT + "/" + CONFIG_FILE
)

func main() {
	// Check if mountpoint exists and create if it doesn't
	if _, err := os.Stat(MOUNTPOINT); os.IsNotExist(err) {
		if err := os.Mkdir(MOUNTPOINT, 0755); err != nil {
			log.Fatalf("Could not create mountpoint %s : %s ", MOUNTPOINT, err)
		}
	}

	// Find all blkids that we need to evaluate for wifi config
	blkidOut, err := exec.Command(CMD_BLKID, "-o", "device").Output()
	if err != nil {
		log.Fatalf("Error running %s : %s", CMD_BLKID, err)
	}
	blkids := string(blkidOut)
	// Check blkids to see if they are already mounted
	//     skip if mounted, skip zram (never mounted) as well
	for _, blkid := range strings.Split(strings.TrimSuffix(blkids, "\n"), "\n") {
		if blkid == "" || strings.Contains(blkid, "zram") {
			continue
		}
		// SKIP err check here, findmnt will exit w/ status 1 if it's not mounted
		//     We obviously don't care if it can't find a mountpoint, that's the intent here
		findmntOut, _ := exec.Command(CMD_FINDMNT, "-n", "-o", "TARGET", blkid).Output()
		mnt := strings.Trim(string(findmntOut), "\n")
		if mnt == "" {
			// Mount the unmounted volume
			err := exec.Command(CMD_MOUNT, blkid, MOUNTPOINT).Run()
			if err != nil {
				log.Fatalf("Could not mount %s : %s", blkid, err)
			}
			// Check if the wifi config exists at the new mountpoint, we don't care if it's not present so just keep going
			if _, err := os.Stat(CONFIG_PATH_FULL); os.IsNotExist(err) {
				if err != nil {
					log.Println("WiFi config not found")
					err := exec.Command(CMD_UMOUNT, MOUNTPOINT).Run()
					if err != nil {
						log.Fatalf("Could not unmount %s : %s ", MOUNTPOINT, err)
					}
					continue
				}
			}
			// Sanity check config file
			stat, err := os.Stat(CONFIG_PATH_FULL)
			if err != nil {
				log.Fatalf("Error getting config file information : %s", err)
			}
			if stat.Size() > 100000 {
				log.Fatal("Config is >100kb, cannot process")
			}
			if !stat.Mode().IsRegular() {
				log.Fatal("Config is NOT a regular (TEXT) file, cannot process")
			}
			// Open and read wifi config file
			configFileContents, err := ioutil.ReadFile(CONFIG_PATH_FULL)
			if err != nil {
				log.Fatalf("Error reading file %s : %s", CONFIG_PATH_FULL, err)
			}
			config := strings.Split(strings.Trim(string(configFileContents), "\n"), "\n")
			// Final sanity checks
			if len(config) != 2 {
				log.Fatal("Config has incorrect number of lines, it should only be 2 lines")
			}
			// Doing stuff with the file contents goes here
			essid := config[0]
			password := config[1]
			log.Printf("Using config: %s / %s for WiFi\n", essid, password)
			// Cleanup existing WiFi connections
			nmcliOut, err := exec.Command(CMD_NMCLI, "-t", "connection", "show").Output()
			if err != nil {
				log.Fatalf("Error running %s : %s", CMD_NMCLI, err)
			}
			connections := strings.Split(strings.Trim(string(nmcliOut), "\n"), "\n")
			for _, connection := range connections {
				details := strings.Split(connection, ":")
				if details[2] != "802-11-wireless" {
					continue
				}
				log.Printf("Cleaning up WiFi connection %s", details[0])
				err := exec.Command(CMD_NMCLI, "connection", "del", details[1]).Run()
				if err != nil {
					log.Fatalf("Error running %s : %s", CMD_NMCLI, err)
				}
			}
			// Create new WiFi connection with network manager
			log.Printf("Connecting to %s with password %s\n", essid, password)
			err = exec.Command(CMD_NMCLI, "d", "wifi", "connect", essid, "password", password).Run()
			if err != nil {
				log.Fatalf("Error running %s : %s", CMD_NMCLI, err)
			}
			// Unmount the filesystem and continue
			err = exec.Command(CMD_UMOUNT, MOUNTPOINT).Run()
			if err != nil {
				log.Fatalf("Could not unmount %s : %s ", MOUNTPOINT, err)
			}
		}
	}
}
