// Package cmd provides CLI commands for the gt tool.
package cmd

import (
	"fmt"
	"os"
	"os/exec"
	"strings"

	"github.com/spf13/cobra"
	"github.com/steveyegge/gastown/internal/style"
	"github.com/steveyegge/gastown/internal/version"
	"github.com/steveyegge/gastown/internal/workspace"
)

var rootCmd = &cobra.Command{
	Use:     "gt",
	Short:   "Gas Town - Multi-agent workspace manager",
	Version: Version,
	Long: `Gas Town (gt) manages multi-agent workspaces called rigs.

It coordinates agent spawning, work distribution, and communication
across distributed teams of AI agents working on shared codebases.`,
	PersistentPreRunE: persistentPreRun,
}

// Commands that don't require beads to be installed/checked.
// These are basic utility commands that should work without beads.
var beadsExemptCommands = map[string]bool{
	"version":    true,
	"help":       true,
	"completion": true,
}

// Commands exempt from the town root branch warning.
// These are commands that help fix the problem or are diagnostic.
var branchCheckExemptCommands = map[string]bool{
	"version":    true,
	"help":       true,
	"completion": true,
	"doctor":     true, // Used to fix the problem
	"install":    true, // Initial setup
	"git-init":   true, // Git setup
}

// persistentPreRun runs before every command.
func persistentPreRun(cmd *cobra.Command, args []string) error {
	// Get the root command name being run
	cmdName := cmd.Name()

	// Check town root branch (warning only, non-blocking)
	if !branchCheckExemptCommands[cmdName] {
		warnIfTownRootOffMain()
	}

	// Skip beads check for exempt commands
	if beadsExemptCommands[cmdName] {
		return nil
	}

	// Check beads version
	return CheckBeadsVersion()
}

// warnIfTownRootOffMain prints a warning if the town root is not on main branch.
// This is a non-blocking warning to help catch accidental branch switches.
func warnIfTownRootOffMain() {
	// Find town root (silently - don't error if not in workspace)
	townRoot, err := workspace.FindFromCwd()
	if err != nil || townRoot == "" {
		return
	}

	// Check if it's a git repo
	gitDir := townRoot + "/.git"
	if _, err := os.Stat(gitDir); os.IsNotExist(err) {
		return
	}

	// Get current branch
	gitCmd := exec.Command("git", "branch", "--show-current")
	gitCmd.Dir = townRoot
	out, err := gitCmd.Output()
	if err != nil {
		return
	}

	branch := strings.TrimSpace(string(out))
	if branch == "" || branch == "main" || branch == "master" {
		return
	}

	// Town root is on wrong branch - warn the user
	fmt.Fprintf(os.Stderr, "\n%s Town root is on branch '%s' (should be 'main')\n",
		style.Bold.Render("⚠️  WARNING:"), branch)
	fmt.Fprintf(os.Stderr, "   This can cause gt commands to fail. Run: %s\n\n",
		style.Dim.Render("gt doctor --fix"))
}

// checkBeadsDependency verifies beads meets minimum version requirements.
// Skips check for exempt commands (version, help, completion).
// Deprecated: Use persistentPreRun instead, which calls CheckBeadsVersion.
func checkBeadsDependency(cmd *cobra.Command, args []string) error {
	// Get the root command name being run
	cmdName := cmd.Name()

	// Skip check for exempt commands
	if beadsExemptCommands[cmdName] {
		return nil
	}

	// Check for stale binary (warning only, doesn't block)
	checkStaleBinaryWarning()

	// Check beads version
	return CheckBeadsVersion()
}

// staleBinaryWarned tracks if we've already warned about stale binary in this session.
// We use an environment variable since the binary restarts on each command.
var staleBinaryWarned = os.Getenv("GT_STALE_WARNED") == "1"

// checkStaleBinaryWarning checks if the installed binary is stale and prints a warning.
// This is a non-blocking check - errors are silently ignored.
func checkStaleBinaryWarning() {
	// Only warn once per shell session
	if staleBinaryWarned {
		return
	}

	repoRoot, err := version.GetRepoRoot()
	if err != nil {
		// Can't find repo - silently skip (might be running from non-dev environment)
		return
	}

	info := version.CheckStaleBinary(repoRoot)
	if info.Error != nil {
		// Check failed - silently skip
		return
	}

	if info.IsStale {
		staleBinaryWarned = true
		os.Setenv("GT_STALE_WARNED", "1")

		msg := fmt.Sprintf("gt binary is stale (built from %s, repo at %s)",
			version.ShortCommit(info.BinaryCommit), version.ShortCommit(info.RepoCommit))
		if info.CommitsBehind > 0 {
			msg = fmt.Sprintf("gt binary is %d commits behind (built from %s, repo at %s)",
				info.CommitsBehind, version.ShortCommit(info.BinaryCommit), version.ShortCommit(info.RepoCommit))
		}
		fmt.Fprintf(os.Stderr, "%s %s\n", style.WarningPrefix, msg)
		fmt.Fprintf(os.Stderr, "    %s Run 'gt install' to update\n", style.ArrowPrefix)
	}
}

// Execute runs the root command and returns an exit code.
// The caller (main) should call os.Exit with this code.
func Execute() int {
	if err := rootCmd.Execute(); err != nil {
		// Check for silent exit (scripting commands that signal status via exit code)
		if code, ok := IsSilentExit(err); ok {
			return code
		}
		// Other errors already printed by cobra
		return 1
	}
	return 0
}

// Command group IDs - used by subcommands to organize help output
const (
	GroupWork      = "work"
	GroupAgents    = "agents"
	GroupComm      = "comm"
	GroupServices  = "services"
	GroupWorkspace = "workspace"
	GroupConfig    = "config"
	GroupDiag      = "diag"
)

func init() {
	// Enable prefix matching for subcommands (e.g., "gt ref at" -> "gt refinery attach")
	cobra.EnablePrefixMatching = true

	// Define command groups (order determines help output order)
	rootCmd.AddGroup(
		&cobra.Group{ID: GroupWork, Title: "Work Management:"},
		&cobra.Group{ID: GroupAgents, Title: "Agent Management:"},
		&cobra.Group{ID: GroupComm, Title: "Communication:"},
		&cobra.Group{ID: GroupServices, Title: "Services:"},
		&cobra.Group{ID: GroupWorkspace, Title: "Workspace:"},
		&cobra.Group{ID: GroupConfig, Title: "Configuration:"},
		&cobra.Group{ID: GroupDiag, Title: "Diagnostics:"},
	)

	// Put help and completion in a sensible group
	rootCmd.SetHelpCommandGroupID(GroupDiag)
	rootCmd.SetCompletionCommandGroupID(GroupConfig)

	// Global flags can be added here
	// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file")
}

// buildCommandPath walks the command hierarchy to build the full command path.
// For example: "gt mail send", "gt status", etc.
func buildCommandPath(cmd *cobra.Command) string {
	var parts []string
	for c := cmd; c != nil; c = c.Parent() {
		parts = append([]string{c.Name()}, parts...)
	}
	return strings.Join(parts, " ")
}

// requireSubcommand returns a RunE function for parent commands that require
// a subcommand. Without this, Cobra silently shows help and exits 0 for
// unknown subcommands like "gt mol foobar", masking errors.
func requireSubcommand(cmd *cobra.Command, args []string) error {
	if len(args) == 0 {
		return fmt.Errorf("requires a subcommand\n\nRun '%s --help' for usage", buildCommandPath(cmd))
	}
	return fmt.Errorf("unknown command %q for %q\n\nRun '%s --help' for available commands",
		args[0], buildCommandPath(cmd), buildCommandPath(cmd))
}
