// 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 cobratree

import (
	"strconv"
	"strings"

	"github.com/spf13/cobra"
)

const (
	EndpointAnnotation = "pp:endpoint"
	HiddenAnnotation   = "mcp:hidden"
	// ReadOnlyAnnotation, when set on a Cobra command to "true"/"1"/"yes",
	// causes the runtime walker to register the resulting MCP tool with
	// readOnlyHint=true. Use for novel CLI commands that don't mutate
	// external state — read-only API queries, local cache reads, etc.
	// Without it, hosts like Claude Desktop default to "could write or
	// delete" and demand permission per call.
	ReadOnlyAnnotation = "mcp:read-only"
	// PositionalWriteSinksAnnotation lists zero-based positional argument
	// indexes that write to user-visible files when populated. It is enforced
	// only on commands that also carry ReadOnlyAnnotation.
	PositionalWriteSinksAnnotation = "mcp:write-positionals"
)

type commandKind int

const (
	commandNovel commandKind = iota
	commandEndpoint
	commandFramework
	commandHidden
)

// frameworkCommands are top-level CLI commands the walker should skip when
// mirroring the Cobra tree. Two cases qualify:
//
//  1. A typed MCP tool already covers the same capability (the typed tool's
//     schema is strictly better than a shell-out). Examples:
//     `context`/`about`/`agent-context`, `api` (endpoint mirror tools cover it).
//  2. The command is non-functional via MCP (interactive setup, shell-only
//     ergonomics, trivial introspection, local-only feedback). Examples:
//     `auth`, `completion`, `doctor`, `version`, `feedback`, `profile`,
//     `which`, `help`.
//
// Commands that DO have agent value — `sync` (populates the store that `sql`
// and `search` query), `stale`/`orphans`/`reconcile`/`load` (store
// diagnostics), `export`/`import` (data movement), `workflow`
// (compound operations), `analytics` (aggregations) — must NOT be in this
// list. Excluding `sync` while exposing `sql` is a broken contract because
// the typed `sql` tool returns empty results until something populates the
// store. See AGENTS.md "Agent-Native Surface" for the principle.
//
// Adding a new generator-emitted command means deciding which of the two
// cases above applies. When in doubt, leave it out — the walker registers
// any user-facing command as a shell-out tool, and the cost of a slightly
// underused tool is much smaller than the cost of a broken contract like
// `sql` without `sync`.
var frameworkCommands = map[string]bool{
	"about":         true,
	"agent-context": true,
	"api":           true,
	"auth":          true,
	"completion":    true,
	"doctor":        true,
	"feedback":      true,
	"help":          true,
	"profile":       true,
	"version":       true,
	"which":         true,
}

func classify(cmd *cobra.Command) commandKind {
	if cmd == nil || cmd.Hidden || isMCPHidden(cmd) {
		return commandHidden
	}
	if endpointID(cmd) != "" {
		return commandEndpoint
	}
	if isTopLevelFrameworkCommand(cmd) {
		return commandFramework
	}
	return commandNovel
}

func isTopLevelFrameworkCommand(cmd *cobra.Command) bool {
	if cmd == nil || !frameworkCommands[cmd.Name()] {
		return false
	}
	parent := cmd.Parent()
	return parent != nil && parent.Parent() == nil
}

func endpointID(cmd *cobra.Command) string {
	if cmd == nil || cmd.Annotations == nil {
		return ""
	}
	return strings.TrimSpace(cmd.Annotations[EndpointAnnotation])
}

func isMCPHidden(cmd *cobra.Command) bool {
	return annotationIsTrue(cmd, HiddenAnnotation)
}

func isMCPReadOnly(cmd *cobra.Command) bool {
	return annotationIsTrue(cmd, ReadOnlyAnnotation)
}

func positionalWriteSinkIndexes(cmd *cobra.Command) map[int]bool {
	if cmd == nil || cmd.Annotations == nil {
		return nil
	}
	raw := strings.TrimSpace(cmd.Annotations[PositionalWriteSinksAnnotation])
	if raw == "" {
		return nil
	}
	out := map[int]bool{}
	for _, part := range strings.FieldsFunc(raw, func(r rune) bool {
		return r == ',' || r == ';' || r == ' ' || r == '\t' || r == '\n'
	}) {
		idx, err := strconv.Atoi(strings.TrimSpace(part))
		if err != nil || idx < 0 {
			continue
		}
		out[idx] = true
	}
	if len(out) == 0 {
		return nil
	}
	return out
}

func annotationIsTrue(cmd *cobra.Command, key string) bool {
	if cmd == nil || cmd.Annotations == nil {
		return false
	}
	v := strings.ToLower(strings.TrimSpace(cmd.Annotations[key]))
	return v == "true" || v == "1" || v == "yes"
}
