// Package cmd implements the Cobra-based CLI commands for the agentteam application.
// It defines the root command and all subcommands for runtime management,
// daemon control, service lifecycle, and diagnostics.
package cmd

import (
	"fmt"
	"os"
	"strings"
	"text/tabwriter"

	"github.com/spf13/cobra"

	"github.com/tidbcloud/AgentTeam/desktop/internal/version"
)

// versionInfo holds version data for serialization.
type versionInfo struct {
	Version   string `json:"version"   yaml:"version"   toml:"Version"`
	Commit    string `json:"commit"    yaml:"commit"    toml:"Commit"`
	BuildAt   string `json:"built"     yaml:"built"     toml:"BuildAt"`
	GoVersion string `json:"go"        yaml:"go"        toml:"GoVersion"`
}

var (
	// showVersion is set by the --version/-v flag.
	showVersion bool
	// versionOutputFormat controls the serialization format for --version output.
	versionOutputFormat string
)

// rootCmd is the base Cobra command for the agentteam CLI.
// All subcommands are registered as children of this command.
var rootCmd = &cobra.Command{
	Use:   "agentteam",
	Short: "AgentTeam daemon and command-line client",
	Long: `agentteam is the AgentTeam daemon and command-line client for this machine.

It connects this machine to AgentTeam, runs local agent runtimes, exposes an
authenticated local API, and manages local workspaces, apps, channels, and
the background daemon service. Electron owns the desktop application shell
on macOS and Windows.

Get started by running the interactive setup wizard, or start the daemon
directly if you already have a configuration file.`,
	Example: `  # First-time setup (interactive wizard)
  agentteam onboard

  # Start the daemon in the foreground
  agentteam daemon

  # Check service status
  agentteam status

  # Install and manage as a system service
  agentteam service install
  agentteam service start

  # View service logs
  agentteam journal --tail 50
  agentteam j -f
  agentteam j --tail 20 -f

  # Manage skills
  agentteam skill list
  agentteam skill import ./code-review.md
  agentteam skill import ./my-skill.zip --id my-skill --name "My Skill"

  # Manage AgentTeam server auth and inspect synced agents
  agentteam auth status
  agentteam agent list

  # Check installation health
  agentteam doctor

  # Print version information
  agentteam --version
  agentteam -v
  agentteam --version -f json

  # Upgrade to the latest version
  agentteam upgrade`,
	SilenceUsage:  true,
	SilenceErrors: false,
}

func init() {
	cobra.EnableCommandSorting = false // keep insertion order

	rootCmd.Flags().BoolVarP(&showVersion, "version", "v", false, "Print version and build information")
	rootCmd.Flags().StringVarP(&versionOutputFormat, "format", "f", "table",
		"output format for --version (table|json|jsonc|yaml|toml)")

	// Register subcommands in desired help order (matching web sidebar).
	rootCmd.AddCommand(onboardCmd)
	rootCmd.AddCommand(authCmd)
	rootCmd.AddCommand(daemonCmd)
	rootCmd.AddCommand(skillCmd)
	rootCmd.AddCommand(channelCmd)
	rootCmd.AddCommand(agentCmd)
	rootCmd.AddCommand(doctorCmd)
	rootCmd.AddCommand(tokenCmd)
	rootCmd.AddCommand(statusCmd)
	rootCmd.AddCommand(serviceCmd)
	rootCmd.AddCommand(journalCmd)
	rootCmd.AddCommand(upgradeCmd)
	rootCmd.AddCommand(docCmd)
	rootCmd.AddCommand(desktopDeepLinkCmd)
	rootCmd.AddCommand(desktopProcessIdentityCmd)
	rootCmd.AddCommand(desktopReceiptVerifyCmd)
	registerPrivateRuntimeCommands(rootCmd, os.Getenv, execCmd)

	rootCmd.InitDefaultCompletionCmd()
}

func registerPrivateRuntimeCommands(
	root *cobra.Command,
	getenv func(string) string,
	execCommand *cobra.Command,
) {
	if execCommand != nil && execRuntimeEnvAvailable(getenv) {
		root.AddCommand(execCommand)
	}
}

// Execute runs the root Cobra command and returns any error encountered.
// It writes errors to stderr and returns a non-nil error for the caller to handle.
// It intercepts --version/-v before Cobra so we can use multi-format output.
func Execute() error {
	// Parse flags early to detect --version and --format before Cobra's
	// built-in version handler (which only prints a simple string).
	if err := rootCmd.ParseFlags(os.Args[1:]); err == nil {
		if showVersion {
			if err := printVersion(rootCmd); err != nil {
				fmt.Fprintln(os.Stderr, err)
				return err
			}
			return nil
		}
	}

	if err := rootCmd.Execute(); err != nil {
		// Exec failures are already rendered as the single structured stderr
		// JSON envelope by renderExecFailure; printing the plain-text error
		// would leak a second, non-stable line onto stderr.
		if _, ok := ExecExitCode(err); !ok {
			fmt.Fprintln(os.Stderr, err)
		}
		return err
	}
	return nil
}

// printVersion prints version information according to the selected format.
func printVersion(cmd *cobra.Command) error {
	info := versionInfo{
		Version:   version.BuildVersion(),
		Commit:    version.Commit,
		BuildAt:   version.BuildAt,
		GoVersion: version.GoToolchainVersion(),
	}

	switch strings.ToLower(versionOutputFormat) {
	case "table":
		w := tabwriter.NewWriter(cmd.OutOrStdout(), 0, 0, 2, ' ', 0)
		fmt.Fprintf(w, "Version\t%s\n", info.Version)
		fmt.Fprintf(w, "Commit\t%s\n", info.Commit)
		fmt.Fprintf(w, "Built\t%s\n", info.BuildAt)
		fmt.Fprintf(w, "Golang\t%s\n", info.GoVersion)
		return w.Flush()
	case "json":
		return printJSON(info, false)
	case "jsonc":
		return printJSON(info, true)
	case "yaml":
		return printYAML(info)
	case "toml":
		return printTOML(info)
	default:
		return fmt.Errorf("unsupported format: %s (valid: table, json, jsonc, yaml, toml)", versionOutputFormat)
	}
}
