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.
//
// The only command that writes anything is "record", which appends a line to
// the history file. Guided mode does not use it: it reads the history and
// reports, and leaves the file exactly as it found it.
func runGuided() {
	in := bufio.NewScanner(os.Stdin)

	fmt.Println()
	fmt.Println("  BackupMedic")
	fmt.Println("  Tells you which backups are actually running, and which have quietly")
	fmt.Println("  stopped.")
	fmt.Println()
	fmt.Println("  Your backup jobs write one line each time they run, into a shared")
	fmt.Println("  history file. Point me at that file and I will show you, per machine")
	fmt.Println("  and per job, when it last succeeded, how far behind it has fallen, and")
	fmt.Println("  whether its output has suddenly shrunk — the classic sign of a backup")
	fmt.Println("  that is failing without saying so.")
	fmt.Println()
	fmt.Println("  I only read the history file. Nothing is written to it here.")
	fmt.Println()

	suggested := suggestedHistory()
	for {
		fmt.Println("  Which history file shall I read?")
		if suggested != "" {
			fmt.Printf("  (press Enter for %s)\n", suggested)
		} else {
			fmt.Println("  Tip: you can drag the file, or the folder holding it, from")
			fmt.Println("  Explorer onto this window to paste its location.")
		}
		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 history file to read. Try again, or close this window.")
			fmt.Println()
			fmt.Println("  If you have not got one yet, it is the file your backup scripts")
			fmt.Println("  append to when they finish — on a file share, a NAS path or a")
			fmt.Println("  synced folder that every machine can reach.")
			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 Enter.")
			fmt.Println()
			continue
		case info.IsDir():
			// A dragged folder is a fair guess at where the history lives.
			found := historyInDir(answer)
			if found == "" {
				fmt.Println()
				fmt.Printf("  %q is a folder, and I could not spot a history file in it.\n", answer)
				fmt.Println("  Drag the history file itself onto this window instead.")
				fmt.Println()
				continue
			}
			fmt.Println()
			fmt.Printf("  That is a folder. Using the history file inside it: %s\n", found)
			answer = found
		}

		fmt.Println()
		fmt.Println("  Reading.")
		fmt.Println()
		guidedHealth(answer)
		break
	}

	fmt.Println()
	fmt.Println("  Done. The command-line version can judge against a different promise")
	fmt.Println("  (--rpo), show the raw run records, and return an exit code your")
	fmt.Println("  monitoring can act on: backupmedic --help")
	pause(in)
}

// guidedHealth runs the same read-only fleet report the health command runs,
// against the default 24-hour recovery point objective.
//
// runHealth returns its exit code rather than calling os.Exit, so the guided
// session survives an unhealthy fleet — which is, after all, the case somebody
// double-clicking this program is most likely to be in.
func guidedHealth(path string) {
	code := runHealth([]string{"--history", path})
	switch code {
	case exitOK:
		fmt.Println()
		fmt.Println("  Every job is healthy.")
	case exitUnhealthy:
		fmt.Println()
		fmt.Println("  At least one job above is not healthy. The reason is printed")
		fmt.Println("  under each one.")
	default:
		fmt.Println()
		fmt.Println("  I could not read that as a history file — see the message above.")
		fmt.Println("  It should hold one JSON record per line, the kind \"backupmedic")
		fmt.Println("  record\" appends at the end of a backup run.")
	}
}

// historyNames are what people actually call a BackupMedic history file, in
// the order we would rather find them.
var historyNames = []string{"history.jsonl", "backupmedic.jsonl", "backups.jsonl", "history.json"}

// historyInDir returns the first plausible history file directly inside dir.
func historyInDir(dir string) string {
	for _, name := range historyNames {
		candidate := filepath.Join(dir, name)
		if info, err := os.Stat(candidate); err == nil && info.Mode().IsRegular() {
			return candidate
		}
	}
	return ""
}

// suggestedHistory offers a history file that is certain to exist, so the
// reader can get the fleet report by pressing one key. It returns "" when
// there is nothing nearby, and the prompt asks for a path instead.
func suggestedHistory() string {
	var dirs []string
	if wd, err := os.Getwd(); err == nil {
		dirs = append(dirs, wd)
	}
	// The folder the program was double-clicked in is often the share the
	// history file itself sits on.
	if exe, err := os.Executable(); err == nil {
		dirs = append(dirs, filepath.Dir(exe))
	}
	if home, err := os.UserHomeDir(); err == nil {
		dirs = append(dirs, home, filepath.Join(home, "Documents"))
	}
	for _, dir := range dirs {
		if found := historyInDir(dir); found != "" {
			return found
		}
	}
	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()
}
