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 thing
// the program needs and stay on screen until the reader is done.
//
// ScreenFlow writes image files, so the guided session does the reading half
// only: it opens the recording and reports exactly what an animation made from
// it would be — how many frames, how big, how long it would run. No image file
// is produced here. Writing one stays a deliberate act at 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("  ScreenFlow")
	fmt.Println("  Turn a recorded terminal session into a small animation you can share.")
	fmt.Println()
	fmt.Println("  It does not watch your screen. It reads a recording that SessionForge")
	fmt.Println("  made earlier — the text a command printed, with the timing of every")
	fmt.Println("  line — and paints those characters back into pictures.")
	fmt.Println()
	fmt.Println("  This window measures a recording and tells you what the animation would")
	fmt.Println("  come out as. No picture file is written while you are in here.")
	fmt.Println()

	path, ok := askRecording(in, suggestedRecording())
	if !ok {
		return
	}

	fmt.Println()
	cmdInfo([]string{path})

	fmt.Println()
	fmt.Println("  Nothing was written. That was a measurement of the recording.")
	fmt.Println("  To produce the animation itself, and to choose its size, speed and")
	fmt.Println("  colours, run ScreenFlow from a command prompt: screenflow --help")
	pause(in)
}

// askRecording asks for the recording file and keeps asking until it gets one
// that exists and can actually be read as a recording.
func askRecording(in *bufio.Scanner, suggested string) (string, bool) {
	for {
		fmt.Println("  Which recording shall I look at?")
		fmt.Println("  It is the file SessionForge wrote when it recorded the session. You")
		fmt.Println("  can drag it, or the folder holding it, from Explorer onto this")
		fmt.Println("  window to paste the location.")
		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 "", false
		}
		answer := strings.Trim(strings.TrimSpace(in.Text()), `"`)
		if answer == "" {
			answer = suggested
		}
		if answer == "" {
			fmt.Println()
			fmt.Println("  I need a recording to look at. If you have not made one yet,")
			fmt.Println("  SessionForge is the tool that records a session; come back here")
			fmt.Println("  with the file it writes. 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("  Check the location and try again, or close this window.")
			fmt.Println()
			continue
		case info.IsDir():
			// A dragged folder is a near miss worth rescuing rather than
			// scolding: look inside it for the recording they meant.
			if found := firstRecordingIn(answer); found != "" {
				fmt.Println()
				fmt.Printf("  That is a folder, so I will use the recording inside it:\n  %s\n", found)
				return found, true
			}
			fmt.Println()
			fmt.Printf("  %q is a folder and I found no recording in it. Give me the\n", answer)
			fmt.Println("  recording file itself.")
			fmt.Println()
			continue
		}

		if _, err := loadCast(answer); err != nil {
			fmt.Println()
			fmt.Printf("  %q is not a recording I can read.\n", answer)
			fmt.Println("  ScreenFlow reads the files SessionForge writes. An ordinary text")
			fmt.Println("  file or a log will not work. Try again, or close this window.")
			fmt.Println()
			continue
		}
		return answer, true
	}
}

// suggestedRecording offers a recording the reader is likely to have just
// made, so the common case is one keypress. It returns "" when there is
// nothing to offer, and the prompt says where recordings come from.
func suggestedRecording() string {
	var dirs []string
	if wd, err := os.Getwd(); err == nil {
		dirs = append(dirs, wd)
	}
	if exe, err := os.Executable(); err == nil {
		dirs = append(dirs, filepath.Dir(exe))
	}
	if home, err := os.UserHomeDir(); err == nil {
		dirs = append(dirs, filepath.Join(home, "Downloads"),
			filepath.Join(home, "Documents"), home)
	}
	for _, dir := range dirs {
		if found := firstRecordingIn(dir); found != "" {
			return found
		}
	}
	return ""
}

// firstRecordingIn returns the first file in dir that ScreenFlow can actually
// read as a recording, or "" if there is none. Candidates are checked by
// opening them, not by trusting the name, so a file that merely ends in
// .jsonl is never offered as a default that then fails.
func firstRecordingIn(dir string) string {
	entries, err := os.ReadDir(dir)
	if err != nil {
		return ""
	}
	var names []string
	for _, e := range entries {
		if e.IsDir() {
			continue
		}
		if ext := strings.ToLower(filepath.Ext(e.Name())); ext == ".jsonl" || ext == ".cast" {
			names = append(names, e.Name())
		}
	}
	sort.Strings(names)
	for _, name := range names {
		candidate := filepath.Join(dir, name)
		if _, err := loadCast(candidate); err == nil {
			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()
}
