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 two
// questions the program actually needs and stay on screen until the reader is
// done.
//
// The guided session checks the plan and stops there. Carrying a plan out
// moves, copies and retires real files, and FileDeck's own safety model says
// the plan is read before it is run. Running it stays on the command line.
//
// 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.
func runGuided() {
	in := bufio.NewScanner(os.Stdin)

	fmt.Println()
	fmt.Println("  FileDeck")
	fmt.Println("  Check your spreadsheet of file changes before anyone runs it.")
	fmt.Println()
	fmt.Println("  You export a sheet where each row names one file and says what")
	fmt.Println("  should happen to it. This reads that sheet, finds every problem")
	fmt.Println("  in one go — a missing file, two rows fighting over the same")
	fmt.Println("  name, a typo in an instruction — and tells you the row number of")
	fmt.Println("  each one.")
	fmt.Println()
	fmt.Println("  It only reads. No file is moved, copied, renamed or retired.")
	fmt.Println()

	plan := askPlan(in)
	if plan == "" {
		return
	}
	root := askRoot(in, filepath.Dir(plan))
	if root == "" {
		return
	}

	fmt.Println()
	cmdValidate([]string{"--plan", plan, "--root", root})

	fmt.Println()
	fmt.Println("  Done. Nothing was moved — that was a check, not a run.")
	fmt.Println("  The command-line version is the one that carries the plan out,")
	fmt.Println("  and it writes a journal that puts everything back: filedeck --help")
	fmt.Println("  For a sheet to start from, run: filedeck template")
	pause(in)
}

// askPlan asks for the sheet and keeps asking until the answer is a real file.
// It returns "" only when stdin closes, which means there is nobody left to
// ask.
func askPlan(in *bufio.Scanner) string {
	suggested := suggestedPlan()
	for {
		fmt.Println("  Where is your plan?")
		if suggested != "" {
			fmt.Printf("  (press Enter for %s)\n", suggested)
		}
		fmt.Print("  > ")

		if !in.Scan() {
			return ""
		}
		answer := strings.TrimSpace(in.Text())
		answer = strings.Trim(answer, `"`)
		if answer == "" {
			answer = suggested
		}
		if answer == "" {
			fmt.Println()
			fmt.Println("  I need the sheet your team exported, saved as CSV.")
			fmt.Println("  Try again, or close this window.")
			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 the file, or the folder holding it, from")
			fmt.Println("  Explorer onto this window to paste its location, then press")
			fmt.Println("  Enter.")
			fmt.Println()
			continue
		case info.IsDir():
			fmt.Println()
			fmt.Printf("  %q is a folder. I need the sheet inside it.\n", answer)
			fmt.Println()
			continue
		}
		return answer
	}
}

// askRoot asks which folder the plan's paths are written relative to. The
// sheet's own folder is nearly always the answer, so it is offered as the
// default, but a sheet kept somewhere else entirely is common enough that
// guessing silently would quietly check the wrong tree.
func askRoot(in *bufio.Scanner, suggested string) string {
	for {
		fmt.Println()
		fmt.Println("  Which folder do the paths in that plan start from?")
		if suggested != "" {
			fmt.Printf("  (press Enter for %s)\n", suggested)
		}
		fmt.Print("  > ")

		if !in.Scan() {
			return ""
		}
		answer := strings.TrimSpace(in.Text())
		answer = strings.Trim(answer, `"`)
		if answer == "" {
			answer = suggested
		}
		if answer == "" {
			fmt.Println()
			fmt.Println("  I need the folder the plan's files live under.")
			fmt.Println("  Try again, or close this window.")
			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
		}
		return answer
	}
}

// suggestedPlan offers a sheet the reader can accept with one keypress, but
// only when one is really there: offering a name that does not exist would
// send the very first keypress straight into an error.
func suggestedPlan() string {
	var dirs []string
	if wd, err := os.Getwd(); err == nil {
		dirs = append(dirs, wd)
	}
	if home, err := os.UserHomeDir(); err == nil {
		dirs = append(dirs, filepath.Join(home, "Documents"), filepath.Join(home, "Downloads"), home)
	}
	for _, dir := range dirs {
		for _, name := range []string{"plan.csv", "filedeck.csv", "filedeck-plan.csv"} {
			candidate := filepath.Join(dir, name)
			if info, err := os.Stat(candidate); err == nil && !info.IsDir() {
				return candidate
			}
		}
	}
	return ""
}

// 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()
}
