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 questions
// 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 answers one question — "has anything in this folder changed
// since I last trusted it?" — using "list" and "check". Both only read. It
// never takes a snapshot (that copies files into the store) and it never rolls
// anything back (that overwrites a file with an older copy). Restoring a file
// is exactly the sort of thing nobody should be able to do by accident from a
// window they opened by double-clicking, so it stays on the command line.
func runGuided() {
	in := bufio.NewScanner(os.Stdin)

	fmt.Println()
	fmt.Println("  DriverGuard")
	fmt.Println("  Tell me if anything in an important folder has changed behind my back.")
	fmt.Println()
	fmt.Println("  DriverGuard keeps a store of snapshots: a copy of every file in a")
	fmt.Println("  folder as it was on the day you were happy with it. Show me that")
	fmt.Println("  store and I will list what is in it and compare the folder as it")
	fmt.Println("  stands today against the newest one.")
	fmt.Println()
	fmt.Println("  This only compares. Nothing is snapshotted, restored, overwritten")
	fmt.Println("  or deleted.")
	fmt.Println()

	store, ok := askStore(in)
	if !ok {
		return // stdin closed on us
	}

	fmt.Println()
	cmdList([]string{"--store", store})

	labels, err := listSnapshotLabels(store)
	if err != nil || len(labels) == 0 {
		fmt.Println()
		fmt.Printf("  There are no snapshots in %q yet, so there is nothing to\n", store)
		fmt.Println("  compare against.")
		fmt.Println()
		fmt.Println("  Taking the first snapshot copies every file in the folder you")
		fmt.Println("  want protected into the store, so it is a deliberate step and it")
		fmt.Println("  lives on the command line:")
		fmt.Println()
		fmt.Println("    driverguard snapshot <folder> --store <this store folder>")
		pause(in)
		return
	}

	latest := labels[len(labels)-1]
	dir, ok := askCheckedDir(in, store, latest)
	if !ok {
		return // stdin closed on us
	}

	fmt.Println()
	fmt.Println("  Comparing, file by file. On a large folder this takes a minute.")
	fmt.Println()
	runCheck([]string{dir, "--store", store, "--against", latest})

	fmt.Println()
	fmt.Println("  Done. That was a comparison — every file is exactly as it was.")
	fmt.Println("  The command-line version can put a changed file back the way the")
	fmt.Println("  snapshot has it: driverguard --help")
	pause(in)
}

// askStore asks where the snapshot store is. It returns false only when stdin
// closes, which is the caller's signal to stop.
func askStore(in *bufio.Scanner) (string, bool) {
	suggested := suggestedFolder()
	for {
		fmt.Println("  Where is your snapshot store?")
		fmt.Println("  (the folder DriverGuard keeps its saved copies in)")
		if suggested != "" {
			fmt.Printf("  (press Enter for %s)\n", suggested)
		}
		fmt.Print("  > ")

		if !in.Scan() {
			return "", false
		}
		answer := strings.TrimSpace(in.Text())
		answer = strings.Trim(answer, `"`)
		if answer == "" {
			answer = suggested
		}
		if answer == "" {
			fmt.Println()
			fmt.Println("  I need the store folder to look inside. Try again, or close")
			fmt.Println("  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. The store is a folder.\n", answer)
			fmt.Println()
			continue
		}
		return answer, true
	}
}

// askCheckedDir asks which folder to compare against the newest snapshot. The
// snapshot records where it was taken from, which is almost always the answer,
// so that is offered as the one-keypress default.
func askCheckedDir(in *bufio.Scanner, store, label string) (string, bool) {
	suggested := ""
	if m, err := loadManifest(store, label); err == nil {
		if info, err := os.Stat(m.SourceDir); err == nil && info.IsDir() {
			suggested = m.SourceDir
		}
	}

	for {
		fmt.Println()
		fmt.Printf("  Which folder shall I compare against snapshot %q?\n", label)
		if suggested != "" {
			fmt.Printf("  (press Enter for %s, where that snapshot was taken)\n", suggested)
		}
		fmt.Print("  > ")

		if !in.Scan() {
			return "", false
		}
		answer := strings.TrimSpace(in.Text())
		answer = strings.Trim(answer, `"`)
		if answer == "" {
			answer = suggested
		}
		if answer == "" {
			fmt.Println()
			fmt.Println("  I need a folder to compare. Try again, or close this window.")
			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.")
			continue
		case !info.IsDir():
			fmt.Println()
			fmt.Printf("  %q is a file, not a folder. Give me the folder it sits in.\n", answer)
			continue
		}
		return answer, true
	}
}

// suggestedFolder offers a store the reader already has, so they can get an
// answer by pressing one key. There is no store every machine is guaranteed to
// own, so this returns nothing rather than something wrong; the prompt copes
// with an empty suggestion by asking for a path instead.
func suggestedFolder() string {
	home, err := os.UserHomeDir()
	if err != nil {
		return ""
	}
	for _, rel := range []string{
		"DriverGuard",
		filepath.Join("Documents", "DriverGuard"),
		filepath.Join("Documents", "Snapshots"),
	} {
		candidate := filepath.Join(home, rel)
		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()
}
