package main

import (
	"bufio"
	"fmt"
	"os"
	"path/filepath"
	"sort"
	"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 runs info, which opens the recording read only and writes
// nothing. Editing a recording produces a new file, and which file that is has
// to be a deliberate choice, so it stays at the command line.
func runGuided() {
	in := bufio.NewScanner(os.Stdin)

	fmt.Println()
	fmt.Println("  RecordDeck")
	fmt.Println("  Look inside a terminal recording before you edit it.")
	fmt.Println()
	fmt.Println("  Give it a recording and it tells you how long it runs, how much is in")
	fmt.Println("  it, and — most usefully — where the long dead pauses are, how much")
	fmt.Println("  time trimming them would save, and a ready-made edit list you can")
	fmt.Println("  start from.")
	fmt.Println()
	fmt.Println("  The recording is opened read only. It is never modified.")
	fmt.Println()

	suggested := suggestedCast()
	for {
		fmt.Println("  Which recording shall I look at?")
		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 recording to look at. Try again, or close this window.")
			fmt.Println("  Tip: you can drag a file or its folder from Explorer onto this")
			fmt.Println("  window to paste the 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 file or a folder from Explorer onto this")
			fmt.Println("  window to paste its location, then press Enter.")
			fmt.Println()
			continue
		case info.IsDir():
			// A dragged folder is a near miss worth rescuing: if there is
			// exactly one recording inside it, that is plainly the one meant.
			found := castsIn(answer)
			if len(found) == 1 {
				fmt.Println()
				fmt.Printf("  That is a folder. Using the one recording in it: %s\n", found[0])
				answer = found[0]
			} else {
				fmt.Println()
				fmt.Printf("  %q is a folder, not a recording.\n", answer)
				if len(found) > 1 {
					fmt.Println("  It holds several. Give me one of these:")
					for i, f := range found {
						if i == 8 {
							fmt.Printf("      ... and %d more\n", len(found)-8)
							break
						}
						fmt.Printf("      %s\n", f)
					}
				}
				fmt.Println()
				continue
			}
		}

		// info exits the program when a file turns out not to be a recording,
		// and exiting takes the window with it. So the file is opened here
		// first, and a bad answer only costs the reader another question.
		if _, err := loadCast(answer); err != nil {
			fmt.Println()
			fmt.Printf("  %q is not a recording I can read.\n", filepath.Base(answer))
			fmt.Printf("  (%v)\n", err)
			fmt.Println("  A recording is the .jsonl file SessionForge writes. Try another,")
			fmt.Println("  or close this window.")
			fmt.Println()
			continue
		}

		fmt.Println()
		cmdInfo([]string{answer})
		break
	}

	fmt.Println()
	fmt.Println("  Done. The recording is exactly as it was.")
	fmt.Println("  There is a command-line version too, which previews an edit and")
	fmt.Println("  writes the edited copy out as a new file: recorddeck help")
	waitForEnter(in)
}

// suggestedCast offers a recording that really exists, so the reader can get a
// useful answer by pressing one key. It returns "" rather than guessing when
// there is no recording anywhere obvious.
func suggestedCast() string {
	var dirs []string
	if wd, err := os.Getwd(); err == nil {
		// A recording is nearly always sitting in the folder the reader has
		// just been working in.
		dirs = append(dirs, wd)
	}
	if home, err := os.UserHomeDir(); err == nil {
		dirs = append(dirs, filepath.Join(home, "Videos"), filepath.Join(home, "Downloads"), home)
	}
	for _, dir := range dirs {
		if found := castsIn(dir); len(found) > 0 {
			return found[0]
		}
	}
	return ""
}

// castsIn lists the .jsonl files directly inside dir, in name order. It does
// not open them: this is the cheap "what is worth offering" pass, and loadCast
// is what actually decides whether a file is a recording.
func castsIn(dir string) []string {
	entries, err := os.ReadDir(dir)
	if err != nil {
		return nil
	}
	var found []string
	for _, e := range entries {
		if e.IsDir() || !strings.HasSuffix(strings.ToLower(e.Name()), ".jsonl") {
			continue
		}
		found = append(found, filepath.Join(dir, e.Name()))
	}
	sort.Strings(found)
	return found
}

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