// Copyright 2026 printing-press-golden and contributors. Licensed under Apache-2.0. See LICENSE.
// Generated by CLI Printing Press (https://github.com/mvanhorn/cli-printing-press). DO NOT EDIT.

package cli

import (
	"encoding/json"
	"os"
	"sort"

	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
	"printing-press-rich-pp-cli/internal/cliutil"
)

// agentContextSchemaVersion is bumped on any breaking change to the JSON
// shape emitted by `agent-context`. Agents should check this before
// parsing. Shape at v4 adds resolved config/data/state/cache directories.
const agentContextSchemaVersion = "4"

// agentContext is the structured description of this CLI consumed by AI
// agents. Inspired by Cloudflare's /cdn-cgi/explorer/api runtime endpoint
// (2026-04-13 Wrangler post): agents can introspect the live CLI without
// parsing --help or reading source.
type agentContext struct {
	SchemaVersion              string                 `json:"schema_version"`
	CLI                        agentContextCLI        `json:"cli"`
	Auth                       agentContextAuth       `json:"auth"`
	Paths                      agentContextPaths      `json:"paths"`
	Discovery                  *agentContextDiscovery `json:"discovery,omitempty"`
	Commands                   []agentContextCommand  `json:"commands"`
	AvailableProfiles          []string               `json:"available_profiles"`
	FeedbackEndpointConfigured bool                   `json:"feedback_endpoint_configured"`
}

type agentContextCLI struct {
	Name        string `json:"name"`
	Description string `json:"description"`
	Version     string `json:"version"`
}

type agentContextAuth struct {
	Mode    string                   `json:"mode"`
	EnvVars []agentContextAuthEnvVar `json:"env_vars"`
}

type agentContextAuthEnvVar struct {
	Name        string `json:"name"`
	Kind        string `json:"kind"`
	Required    bool   `json:"required"`
	Sensitive   bool   `json:"sensitive"`
	Description string `json:"description,omitempty"`
}

type agentContextPaths struct {
	ConfigDir string `json:"config_dir"`
	DataDir   string `json:"data_dir"`
	StateDir  string `json:"state_dir"`
	CacheDir  string `json:"cache_dir"`
}

type agentContextDiscovery struct {
	Source            string   `json:"source"`
	TargetURL         string   `json:"target_url,omitempty"`
	EntryCount        int      `json:"entry_count,omitempty"`
	APIEntryCount     int      `json:"api_entry_count,omitempty"`
	Reachability      string   `json:"reachability,omitempty"`
	Protocols         []string `json:"protocols,omitempty"`
	AuthCandidates    []string `json:"auth_candidates,omitempty"`
	Protections       []string `json:"protections,omitempty"`
	GenerationHints   []string `json:"generation_hints,omitempty"`
	Warnings          []string `json:"warnings,omitempty"`
	CandidateCommands []string `json:"candidate_commands,omitempty"`
}

type agentContextCommand struct {
	Name        string                `json:"name"`
	Use         string                `json:"use,omitempty"`
	Short       string                `json:"short,omitempty"`
	Annotations map[string]string     `json:"annotations,omitempty"`
	Flags       []agentContextFlag    `json:"flags,omitempty"`
	Subcommands []agentContextCommand `json:"subcommands,omitempty"`
}

type agentContextFlag struct {
	Name    string `json:"name"`
	Type    string `json:"type"`
	Usage   string `json:"usage,omitempty"`
	Default string `json:"default,omitempty"`
}

func newAgentContextCmd(rootCmd *cobra.Command) *cobra.Command {
	var pretty bool
	cmd := &cobra.Command{
		Use:         "agent-context",
		Short:       "Emit structured JSON describing this CLI for agents",
		Annotations: map[string]string{"mcp:read-only": "true"},
		Long: `Outputs a machine-readable description of commands, flags, and auth so
agents can introspect this CLI at runtime without parsing --help or
reading source. Schema is versioned via schema_version.`,
		RunE: func(cmd *cobra.Command, args []string) error {
			ctx := buildAgentContext(rootCmd)
			enc := json.NewEncoder(os.Stdout)
			if pretty {
				enc.SetIndent("", "  ")
			}
			return enc.Encode(ctx)
		},
	}
	cmd.Flags().BoolVar(&pretty, "pretty", false, "indent JSON output for human reading")
	return cmd
}

func buildAgentContext(rootCmd *cobra.Command) agentContext {
	envVars := []agentContextAuthEnvVar{
		{
			Name:        "RICH_AUTH_API_KEY",
			Kind:        "per_call",
			Required:    true,
			Sensitive:   true,
			Description: "Set to your API credential.",
		},
		{
			Name:        "RICH_AUTH_CLIENT_ID",
			Kind:        "auth_flow_input",
			Required:    false,
			Sensitive:   false,
			Description: "OAuth application client identifier.",
		},
		{
			Name:        "RICH_AUTH_CLIENT_SECRET",
			Kind:        "auth_flow_input",
			Required:    false,
			Sensitive:   true,
			Description: "Set during initial auth setup.",
		},
		{
			Name:        "RICH_AUTH_OPTIONAL_TOKEN",
			Kind:        "per_call",
			Required:    false,
			Sensitive:   true,
			Description: "Set to your API credential.",
		},
		{
			Name:        "RICH_AUTH_BOT_TOKEN",
			Kind:        "per_call",
			Required:    false,
			Sensitive:   true,
			Description: "Set to your API credential.",
		},
		{
			Name:        "RICH_AUTH_USER_TOKEN",
			Kind:        "per_call",
			Required:    false,
			Sensitive:   true,
			Description: "Set to your API credential.",
		},
	}
	authMode := "api_key"
	if authMode == "" {
		authMode = "none"
	}
	profiles := ListProfileNames()
	if profiles == nil {
		profiles = []string{}
	}
	return agentContext{
		SchemaVersion: agentContextSchemaVersion,
		CLI: agentContextCLI{
			Name:        "printing-press-rich-pp-cli",
			Description: "Purpose-built fixture for rich auth env-var model coverage.",
			Version:     rootCmd.Version,
		},
		Auth: agentContextAuth{
			Mode:    authMode,
			EnvVars: envVars,
		},
		Paths:                      buildAgentContextPaths(),
		Discovery:                  buildAgentDiscoveryContext(),
		Commands:                   collectAgentCommands(rootCmd),
		AvailableProfiles:          profiles,
		FeedbackEndpointConfigured: FeedbackEndpointConfigured(),
	}
}

func buildAgentContextPaths() agentContextPaths {
	configDir, _ := cliutil.ConfigDir()
	dataDir, _ := cliutil.DataDir()
	stateDir, _ := cliutil.StateDir()
	cacheDir, _ := cliutil.CacheDir()
	return agentContextPaths{
		ConfigDir: configDir,
		DataDir:   dataDir,
		StateDir:  stateDir,
		CacheDir:  cacheDir,
	}
}

func buildAgentDiscoveryContext() *agentContextDiscovery {
	return nil
}

// collectAgentCommands walks the cobra tree from the given command and
// returns its direct children (skipping the agent-context command itself
// to avoid self-reference). Each child is recursed into if it has
// subcommands. Flags are captured via VisitAll. Output is sorted by
// command name for stable diffs across regenerations.
//
// Cobra's Hidden flag suppresses listing in --help but does not gate
// agent discovery. Raw resource parents are Hidden so --help stays
// curated and the `api` browser populates; the agent-context surface
// must still enumerate them and their endpoints so agents can call any
// action a CLI user could.
func collectAgentCommands(c *cobra.Command) []agentContextCommand {
	children := c.Commands()
	sort.Slice(children, func(i, j int) bool { return children[i].Name() < children[j].Name() })

	out := make([]agentContextCommand, 0, len(children))
	for _, sub := range children {
		if sub.Name() == "agent-context" {
			continue
		}
		entry := agentContextCommand{
			Name:  sub.Name(),
			Use:   sub.Use,
			Short: sub.Short,
		}
		// Surface Cobra annotations (e.g., pp:endpoint, mcp:read-only) so
		// agents and the live-dogfood classifier can detect destructive-at-auth
		// endpoints without parsing source. Empty maps are stripped via
		// omitempty in the struct tag.
		if len(sub.Annotations) > 0 {
			entry.Annotations = make(map[string]string, len(sub.Annotations))
			for k, v := range sub.Annotations {
				entry.Annotations[k] = v
			}
		}
		sub.Flags().VisitAll(func(f *pflag.Flag) {
			entry.Flags = append(entry.Flags, agentContextFlag{
				Name:    f.Name,
				Type:    f.Value.Type(),
				Usage:   f.Usage,
				Default: f.DefValue,
			})
		})
		sort.Slice(entry.Flags, func(i, j int) bool {
			return entry.Flags[i].Name < entry.Flags[j].Name
		})
		if len(sub.Commands()) > 0 {
			entry.Subcommands = collectAgentCommands(sub)
		}
		out = append(out, entry)
	}
	return out
}
