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.
//
// FolderForge MOVES FILES. That makes the guided session read-only by
// construction: it runs the same planning and collision-checking code the
// "preview" command runs, prints the plan, and stops there. There is no way to
// reach a move from this session at all — the guided path never calls
// applyMoves, and the only way to move a file with FolderForge remains typing
// the organize command yourself at a prompt.
func runGuided() {
	in := bufio.NewScanner(os.Stdin)

	fmt.Println()
	fmt.Println("  FolderForge")
	fmt.Println("  Work out how to tidy a cluttered folder into sensible sub-folders.")
	fmt.Println()
	fmt.Println("  Point it at a folder full of odds and ends — a Downloads folder is")
	fmt.Println("  the usual one — and it shows you where each file would go: pictures")
	fmt.Println("  with pictures, documents with documents, and so on.")
	fmt.Println()
	fmt.Println("  This window shows you the plan and nothing else. No file is moved,")
	fmt.Println("  renamed or deleted here, and no folder is created.")
	fmt.Println()

	suggested := suggestedFolder()
	for {
		fmt.Println("  Which folder 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 folder to look at. 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
		}

		fmt.Println()
		fmt.Println("  Working out the plan. Nothing is being moved.")
		fmt.Println()
		previewFolder(answer)
		break
	}

	fmt.Println()
	fmt.Println("  That was a plan only. Every file is still exactly where it was.")
	fmt.Println("  The command-line version can carry a plan out, and can use your own")
	fmt.Println("  rules instead of the starter set above: folderforge help")
	pause(in)
}

// previewFolder produces exactly the read-only view the "preview" command
// produces: the same buildPlan, the same detectCollisions, the same
// printReport, with the apply flag hard-wired false. Nothing else is called,
// so there is no code path from here to a file move.
func previewFolder(dir string) {
	cfg := starterRules()

	// Destinations are planned inside the folder itself, which is what
	// "preview" does when no destination root is named.
	matched, unmatched, err := buildPlan(dir, dir, cfg)
	if err != nil {
		fmt.Printf("  I could not read the files in %s.\n", dir)
		fmt.Println("  It may be a folder Windows will not let this program open.")
		return
	}

	collisions := detectCollisions(matched)
	printReport(dir, dir, matched, unmatched, collisions, false, len(cfg.Rules))

	if len(collisions) > 0 {
		fmt.Println()
		fmt.Println("Two files above would land on the same name. FolderForge treats that")
		fmt.Println("as a reason to stop rather than overwrite anything.")
	}
}

// starterRules is the set of rules the guided session plans against, so a
// reader who has never written a rules file still sees a real, useful plan.
// It mirrors the example in the README: a first-match-wins list of filename
// patterns mapped to destination folder names.
func starterRules() *RulesConfig {
	return &RulesConfig{Rules: []Rule{
		{Name: "images", Match: "*.png,*.jpg,*.jpeg,*.gif,*.bmp,*.webp,*.heic,*.tif,*.tiff", Dest: "Images"},
		{Name: "documents", Match: "*.pdf,*.doc,*.docx,*.odt,*.rtf,*.txt,*.md", Dest: "Documents"},
		{Name: "spreadsheets", Match: "*.xls,*.xlsx,*.ods,*.csv", Dest: "Spreadsheets"},
		{Name: "presentations", Match: "*.ppt,*.pptx,*.odp", Dest: "Presentations"},
		{Name: "audio", Match: "*.mp3,*.wav,*.flac,*.m4a,*.aac,*.ogg", Dest: "Audio"},
		{Name: "video", Match: "*.mp4,*.mkv,*.mov,*.avi,*.wmv,*.webm", Dest: "Video"},
		{Name: "archives", Match: "*.zip,*.rar,*.7z,*.tar,*.gz,*.bz2,*.xz", Dest: "Archives"},
		{Name: "installers", Match: "*.exe,*.msi,*.dmg,*.pkg,*.deb,*.rpm,*.appx", Dest: "Installers"},
		{Name: "disc images", Match: "*.iso,*.img", Dest: "DiscImages"},
	}}
}

// suggestedFolder offers somewhere worth looking that is certain to exist, so
// the reader can get a useful answer by pressing one key.
func suggestedFolder() string {
	home, err := os.UserHomeDir()
	if err != nil {
		return ""
	}
	// Downloads is the folder this tool exists for: a flat pile of files of
	// every kind, which is exactly what the rules sort out.
	for _, name := range []string{"Downloads", "Desktop"} {
		candidate := filepath.Join(home, name)
		if info, err := os.Stat(candidate); err == nil && info.IsDir() {
			return candidate
		}
	}
	return home
}

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