// DrivePulse — instant space map, largest-file finder, and duplicate-size
// candidate hints for a directory tree.
//
// Usage:
//
//	drivepulse scan <dir> [--depth N] [--top N] [--json]
//
// Scope note: real disk/SMART health telemetry needs OS- and hardware-
// specific access DrivePulse's cross-platform CLI build doesn't attempt —
// see the "Roadmap" section of its build plan. This binary covers the
// space-map, largest-file, and duplicate-candidate parts of v1.
package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"sort"
	"strings"
)

type dirSize struct {
	Path string
	Size int64
}

type fileSize struct {
	Path string
	Size int64
}

func main() {
	if len(os.Args) < 2 {
		// Double-clicked in Explorer rather than run from a prompt: ask the
		// one question the program needs and stay on screen. Printing usage
		// and exiting here is what made the window vanish instantly.
		if interactiveConsole() {
			runGuided()
			return
		}
		usage()
		os.Exit(1)
	}
	switch os.Args[1] {
	case "scan":
		cmdScan(os.Args[2:])
	case "-h", "--help", "help":
		usage()
	default:
		fmt.Fprintf(os.Stderr, "unknown command %q\n\n", os.Args[1])
		usage()
		os.Exit(1)
	}
}

func usage() {
	fmt.Fprint(os.Stderr, `DrivePulse — instant space map for a directory tree

Usage:
  drivepulse scan <dir> [--depth N] [--top N] [--json]

  --depth N   how many levels deep the space map groups folders (default 2)
  --top N     how many rows to show per section (default 10)
`)
}

func reorderFlags(args []string, valueFlags map[string]bool) []string {
	var flags, positional []string
	for i := 0; i < len(args); i++ {
		a := args[i]
		name := strings.TrimLeft(a, "-")
		if strings.HasPrefix(a, "-") && valueFlags[name] {
			flags = append(flags, a)
			if i+1 < len(args) {
				i++
				flags = append(flags, args[i])
			}
			continue
		}
		if strings.HasPrefix(a, "-") {
			flags = append(flags, a)
			continue
		}
		positional = append(positional, a)
	}
	return append(flags, positional...)
}

func cmdScan(args []string) {
	fs := flag.NewFlagSet("scan", flag.ExitOnError)
	depth := fs.Int("depth", 2, "folder grouping depth for the space map")
	top := fs.Int("top", 10, "rows to show per section")
	jsonOut := fs.Bool("json", false, "print machine-readable JSON")
	fs.Parse(reorderFlags(args, map[string]bool{"depth": true, "top": true}))
	dirs := fs.Args()
	if len(dirs) != 1 {
		fmt.Fprintln(os.Stderr, "usage: drivepulse scan <dir> [--depth N] [--top N] [--json]")
		os.Exit(1)
	}
	root := dirs[0]

	dirTotals := map[string]int64{}
	var files []fileSize
	bySize := map[int64][]string{}
	var totalSize int64
	var totalFiles int64

	err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return nil
		}
		if info.IsDir() {
			return nil
		}
		if !info.Mode().IsRegular() {
			return nil
		}
		size := info.Size()
		totalSize += size
		totalFiles++
		files = append(files, fileSize{Path: path, Size: size})
		bySize[size] = append(bySize[size], path)

		rel, err := filepath.Rel(root, path)
		if err != nil {
			return nil
		}
		group := groupPath(root, rel, *depth)
		dirTotals[group] += size
		return nil
	})
	if err != nil {
		fmt.Fprintln(os.Stderr, "error:", err)
		os.Exit(1)
	}

	var dirs2 []dirSize
	for p, s := range dirTotals {
		dirs2 = append(dirs2, dirSize{Path: p, Size: s})
	}
	sort.Slice(dirs2, func(i, j int) bool { return dirs2[i].Size > dirs2[j].Size })
	if len(dirs2) > *top {
		dirs2 = dirs2[:*top]
	}

	sort.Slice(files, func(i, j int) bool { return files[i].Size > files[j].Size })
	largest := files
	if len(largest) > *top {
		largest = largest[:*top]
	}

	type dupCandidate struct {
		Size  int64    `json:"size"`
		Count int      `json:"count"`
		Files []string `json:"files"`
	}
	var dupes []dupCandidate
	for size, paths := range bySize {
		if len(paths) < 2 {
			continue
		}
		dupes = append(dupes, dupCandidate{Size: size, Count: len(paths), Files: paths})
	}
	sort.Slice(dupes, func(i, j int) bool { return dupes[i].Size*int64(dupes[i].Count) > dupes[j].Size*int64(dupes[j].Count) })
	if len(dupes) > *top {
		dupes = dupes[:*top]
	}

	if *jsonOut {
		enc := json.NewEncoder(os.Stdout)
		enc.SetIndent("", "  ")
		enc.Encode(map[string]any{
			"total_size_bytes":          totalSize,
			"total_files":               totalFiles,
			"space_map":                 dirs2,
			"largest_files":             largest,
			"duplicate_size_candidates": dupes,
		})
		return
	}

	fmt.Printf("DrivePulse space map — %s across %d files\n\n", humanBytes(totalSize), totalFiles)

	fmt.Printf("Top folders (depth %d):\n", *depth)
	for _, d := range dirs2 {
		pct := 0.0
		if totalSize > 0 {
			pct = float64(d.Size) / float64(totalSize) * 100
		}
		fmt.Printf("  %8s  %5.1f%%  %s\n", humanBytes(d.Size), pct, d.Path)
	}

	fmt.Printf("\nLargest files:\n")
	for _, f := range largest {
		fmt.Printf("  %8s  %s\n", humanBytes(f.Size), f.Path)
	}

	fmt.Printf("\nDuplicate-size candidates (same size, not yet content-verified):\n")
	if len(dupes) == 0 {
		fmt.Println("  none found")
	}
	for _, d := range dupes {
		fmt.Printf("  %8s x%d  e.g. %s\n", humanBytes(d.Size), d.Count, d.Files[0])
	}
	if len(dupes) > 0 {
		fmt.Println("\n  These share a size, which is a cheap first signal — run DupePilot for exact-content verification before deleting anything.")
	}

	fmt.Println("\nNote: SMART/disk-health telemetry is not implemented in this cross-platform CLI build — see the DrivePulse build plan's roadmap.")
}

func groupPath(root, rel string, depth int) string {
	if rel == "." {
		return root
	}
	parts := strings.Split(filepath.ToSlash(rel), "/")
	if len(parts) <= depth {
		parts = parts[:len(parts)-1] // file's own directory only, no deeper
	} else {
		parts = parts[:depth]
	}
	if len(parts) == 0 {
		return root
	}
	return filepath.Join(root, filepath.Join(parts...))
}

func humanBytes(n int64) string {
	const unit = 1024
	if n < unit {
		return fmt.Sprintf("%d B", n)
	}
	div, exp := int64(unit), 0
	for x := n / unit; x >= unit; x /= unit {
		div *= unit
		exp++
	}
	return fmt.Sprintf("%.1f %ciB", float64(n)/float64(div), "KMGTPE"[exp])
}
