package main

import (
	"bufio"
	"encoding/csv"
	"fmt"
	"os"
	"os/user"
	"path/filepath"
	"strings"
)

var swPath, fPath string

///////////////////////////////
// Startup & helpers
//////////////////////////////

func ifErrExit(e error, msg string) {
	if e != nil {
		fmt.Fprintf(os.Stderr, "%s: %s\n", msg, e.Error())
		os.Exit(1)
	}
}

func earlyExit(msg string) {
	fmt.Fprintf(os.Stderr, "Error: %s\n", msg)
	os.Exit(1)
}

// validateDataPaths makes sure that the necessary data folders exist
// for the user running spacewalk. It builds them if they are not
// present and does an error exit if there is an error.
func validateDataPaths() {
	xdgDataHome := os.Getenv("XDG_DATA_HOME")
	if xdgDataHome == "" {
		usr, _ := user.Current()
		xdgDataHome = filepath.Join(usr.HomeDir, "/.local/share")
	}
	neededFolders := filepath.Join(xdgDataHome, "spacewalk")
	neededFolders, _ = filepath.Abs(neededFolders)
	swPath = neededFolders
	neededFolders = filepath.Join(neededFolders, "flights")
	fPath = neededFolders
	err := os.MkdirAll(neededFolders, 0755)
	ifErrExit(err, "Error validation data paths")
	p := filepath.Join(swPath, "flight-manifest")
	f, err := os.OpenFile(p, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	ifErrExit(err, "Could not create flight manifest")
	f.Close()
}

func displayUsage() {
	fmt.Println("spacewalk [stuff] [things]")
	os.Exit(0)
}

// parseArgs is the main entry point into scbm and will run the appropriate functions
// based on the arguments passed in at run time.
func parseArgs() {
	a := os.Args
	if len(a) == 1 {
		displayUsage()
	}

	switch a[1] {
	case "create":
		if len(a) == 3 {
			create(a[2])
		} else {
			earlyExit("Missing flight name.\n-   spacewalk create \033[3mflight\033[0m")
		}
	case "update":
		if len(a) == 3 {
			update(a[2])
		} else {
			earlyExit("Missing flight name.\n-   spacewalk update \033[3mflight\033[0m")
		}
	case "remove":
		if len(a) == 3 {
			remove(a[2])
		} else {
			earlyExit("Missing flight name.\n-   spacewalk remove \033[3mflight\033[0m")
		}
	case "add":
		if len(a) == 5 {
			add(a[2], a[3], a[4])
		} else {
			earlyExit("Incorrect syntax.\n-   spacewalk add \033[3mflight url title\033[0m")
		}
	case "delete":
		if len(a) == 4 {
			del(a[2], a[3])
		} else {
			earlyExit("Incorrect syntax.\n-   spacewalk delete \033[3mflight url|title\033[0m")
		}
	case "launch":
		if len(a) == 2 {
			launchFlights()
		} else if len(a) == 3 {
			launchFlight(a[2])
		} else {
			earlyExit("Incorrect syntax.\n-   spacewalk launch [\033[3mflight\033[0m]")
		}
	case "show":
		if len(a) == 2 {
			showFlights("")
		} else if len(a) == 3 {
			showFlights(a[2])
		} else {
			earlyExit("Incorrect syntax.\n-   spacewalk show [\033[3mflight\033[0m]")
		}
	default:
		earlyExit(fmt.Sprintf("Unknown command %q", a[1]))
	}
}

func getLine(prefix string) (string, error) {
	reader := bufio.NewReader(os.Stdin)
	fmt.Print(prefix)
	text, err := reader.ReadString('\n')
	if err != nil {
		return "", err
	}

	return text[:len(text)-1], nil
}

func readManifest() [][]string {
	p := filepath.Join(swPath, "flight-manifest")
	f, err := os.Open(p)
	ifErrExit(err, "Could not open flight manifest")
	defer f.Close()
	r := csv.NewReader(f)
	records, err := r.ReadAll()
	ifErrExit(err, "Could not read from flight manifest")
	return records
}

///////////////////////////////
// Command Execution
//////////////////////////////

func create(flight string) {
	var lp, hp, fp string
	var err error
	records := readManifest()
	for _, r := range records {
		if r[0] == flight {
			earlyExit(fmt.Sprintf("There is already a flight with the name %q", flight))
		}
	}

	fmt.Printf("Creating flight: %q\n", flight)

	happy := false
	for !happy {
		lp, err = getLine("Enter the launch path (output path including file name): ")
		ifErrExit(err, "Error reading from stdin")
		if lp == "" {
			fmt.Println("Launch path cannot be empty")
			continue
		}
		hp, err = getLine("Enter the header path, or leave blank for none: ")
		ifErrExit(err, "Error reading from stdin")
		if hp == "" {
			hp = "none"
		}
		fp, err = getLine("Enter the footer path, or leave blank for none: ")
		ifErrExit(err, "Error reading from stdin")
		if fp == "" {
			fp = "none"
		}
		fmt.Println("Are you happy with the following:")
		fmt.Printf("Launch path: %s\nHeader path: %s\nFooter path: %s\n", lp, hp, fp)
		yesNo, err := getLine("Type 'yes' to accept, anything else to redo: ")
		ifErrExit(err, "Error reading from stdin")

		if strings.ToLower(yesNo) == "yes" {
			happy = true
		}
	}

	p := filepath.Join(swPath, "flight-manifest")
	f, err := os.OpenFile(p, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	ifErrExit(err, "Could not open flight manifest")
	defer f.Close()
	ln := fmt.Sprintf("%s, %s, %s, %s\n", flight, lp, hp, fp)
	_, err = f.WriteString(ln)
	ifErrExit(err, "Unable to save new flight to data file")
	fpath := filepath.Join(fPath, flight)
	ff, err := os.OpenFile(fpath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	ifErrExit(err, fmt.Sprintf("Could not create flight log for %s", flight))
	ff.Close()
}

func update(flight string) {
	fmt.Println(flight)
}

func remove(flight string) {
	fmt.Println(flight)
}

func add(flight, url, title string) {
	p := filepath.Join(fPath, "flights", flight)
	f, err := os.OpenFile(p, os.O_APPEND|os.O_WRONLY, 0644)
	ifErrExit(err, fmt.Sprintf("Could not open flight log for %s", flight))
	defer f.Close()
	ln := fmt.Sprintf("%s, %s, %s, %s\n", title, url, "nil", "0")
	_, err = f.WriteString(ln)
	ifErrExit(err, "Unable to save new capsule to data file")
	fmt.Printf("Capsule %q added to %s's flight log", title, flight)
}

func del(flight, item string) {
	fmt.Println(flight, item)
}

func launchFlights() {
	fmt.Println("Hello")
}

func launchFlight(flight string) {
	fmt.Println(flight)
}

func showFlights(flight string) {
	records := readManifest()
	for _, row := range records {
		if flight != "" && row[0] != flight {
			continue
		}
		fmt.Printf("\033[1mFlight Name:\033[0m %s\n", row[0])
		fmt.Printf("\033[1mLaunch Path:\033[0m %s\n", row[1])
		fmt.Printf("\033[1mHeader Path:\033[0m %s\n", row[2])
		fmt.Printf("\033[1mFooter Path:\033[0m %s\n--\n", row[3])
	}
}

func main() {
	validateDataPaths()
	parseArgs()
}
