package main

import (
	"bufio"
	"fmt"
	"os"
	"path/filepath"
	"strings"
)

// runGuided is what happens when somebody double-clicks the program instead of
// typing its name at a prompt.
//
// Without this, Explorer opens a console, main() finds no arguments, prints
// the usage text to stderr and exits — and Windows destroys the window in the
// same instant. From the other side of the screen that is indistinguishable
// from a crash. So when we know we were double-clicked, we ask the one
// question the program actually needs and stay on screen until the reader is
// done.
//
// This path is entered ONLY when there are no arguments and both ends of the
// program are a real console. Any scripted or piped use takes exactly the same
// code path it always did.
//
// Guided mode lists the library and checks it over. It never runs a workflow:
// a workflow's steps are real commands on this machine, and starting one is a
// decision somebody has to make on purpose, at a prompt.
func runGuided() {
	in := bufio.NewScanner(os.Stdin)

	fmt.Println()
	fmt.Println("  QuickFlow")
	fmt.Println("  Show what is in a shared library of saved jobs.")
	fmt.Println()
	fmt.Println("  A library is just a folder of small files, each describing a job your")
	fmt.Println("  team runs regularly: what it needs to be told, and the steps it takes.")
	fmt.Println("  I will list every job in the folder, what each one asks for, and")
	fmt.Println("  whether any of the files have mistakes in them.")
	fmt.Println()
	fmt.Println("  Nothing is run and nothing is changed. This is a reading of the folder.")
	fmt.Println()

	suggested := suggestedLibrary()
	for {
		fmt.Println("  Which library folder shall I read?")
		if suggested != "" {
			fmt.Printf("  (press Enter for %s)\n", suggested)
		}
		fmt.Print("  > ")

		if !in.Scan() {
			// stdin closed on us; there is nothing sensible left to ask.
			return
		}
		answer := strings.TrimSpace(in.Text())
		answer = strings.Trim(answer, `"`)
		if answer == "" {
			answer = suggested
		}
		if answer == "" {
			fmt.Println()
			fmt.Println("  I need a folder to read. Try again, or close this window.")
			fmt.Println("  Tip: you can drag a folder from Explorer onto this window to")
			fmt.Println("  paste its location, then press Enter.")
			fmt.Println()
			continue
		}

		info, err := os.Stat(answer)
		switch {
		case err != nil:
			fmt.Println()
			fmt.Printf("  I cannot find %q.\n", answer)
			fmt.Println("  Tip: you can drag a folder from Explorer onto this window to")
			fmt.Println("  paste its location, then press Enter.")
			fmt.Println()
			continue
		case !info.IsDir():
			fmt.Println()
			fmt.Printf("  %q is a file, not a folder. Give me the folder it sits in.\n", answer)
			fmt.Println()
			continue
		}

		// list refuses a library with nothing usable in it, and refusing means
		// exiting, which would take the window with it. So the same question
		// gets asked here, gently, before the command ever sees the folder.
		workflows, problems, err := loadLibrary(answer)
		if err != nil {
			fmt.Println()
			fmt.Printf("  I could not read %q as a library folder.\n", answer)
			fmt.Println()
			continue
		}
		if len(workflows) == 0 {
			fmt.Println()
			fmt.Printf("  There are no usable jobs in %q.\n", answer)
			if len(problems) == 0 {
				fmt.Println("  A library folder holds one .json file per job; this one has none.")
			} else {
				fmt.Printf("  It holds %d file(s) I could not make sense of:\n", len(problems))
				for i, p := range problems {
					if i == 5 {
						fmt.Printf("      ... and %d more\n", len(problems)-5)
						break
					}
					fmt.Printf("      %s: %s\n", filepath.Base(p.File), p.Message)
				}
			}
			fmt.Println("  Try another folder, or close this window.")
			fmt.Println()
			continue
		}

		fmt.Println()
		cmdList([]string{"--library", answer})
		break
	}

	fmt.Println()
	fmt.Println("  Done. Nothing was run — that was a reading of the folder.")
	fmt.Println("  There is a command-line version too, which can explain one job in")
	fmt.Println("  full and carry it out for you: quickflow help")
	pause(in)
}

// suggestedLibrary offers a library folder that really exists and really has
// jobs in it, so the reader can get a useful answer by pressing one key. It
// returns "" rather than guessing when no such folder is to be found.
func suggestedLibrary() string {
	var candidates []string
	if wd, err := os.Getwd(); err == nil {
		// The library sitting next to the program is the common case: somebody
		// unzipped the two together.
		candidates = append(candidates, filepath.Join(wd, "workflows"), filepath.Join(wd, "library"), wd)
	}
	if home, err := os.UserHomeDir(); err == nil {
		candidates = append(candidates,
			filepath.Join(home, "quickflow"),
			filepath.Join(home, "workflows"),
			filepath.Join(home, "Documents", "quickflow"))
	}
	for _, dir := range candidates {
		if hasWorkflowFiles(dir) {
			return dir
		}
	}
	return ""
}

// hasWorkflowFiles reports whether dir is a directory holding at least one
// .json file — the cheapest honest test for "this could be a library".
func hasWorkflowFiles(dir string) bool {
	info, err := os.Stat(dir)
	if err != nil || !info.IsDir() {
		return false
	}
	entries, err := os.ReadDir(dir)
	if err != nil {
		return false
	}
	for _, e := range entries {
		if !e.IsDir() && strings.HasSuffix(strings.ToLower(e.Name()), ".json") {
			return true
		}
	}
	return false
}

// pause keeps the console window open. Explorer closes it the moment the
// process exits, so without this the reader never sees the output.
func pause(in *bufio.Scanner) {
	fmt.Println()
	fmt.Print("  Press Enter to close this window. ")
	in.Scan()
}
