// 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 (
	"bytes"
	"context"
	"fmt"
	"os/exec"
	"sort"
	"strconv"
	"strings"

	mcplib "github.com/mark3labs/mcp-go/mcp"
	"github.com/mark3labs/mcp-go/server"
	"printing-press-golden-pp-cli/internal/mcp/bound"
)

func shellOutToCLI(cliPath func() (string, error), commandPath []string, blockedStructuredArgs map[string]bool, allowedStructuredArgs map[string]bool, positionals []positionalArg, readOnly bool, positionalWriteSinks map[int]bool) server.ToolHandlerFunc {
	lookupPath, lookupErr := cliPath()
	prefixArgs := append([]string{}, commandPath...)
	return func(ctx context.Context, req mcplib.CallToolRequest) (*mcplib.CallToolResult, error) {
		if lookupErr != nil {
			return mcplib.NewToolResultError(fmt.Sprintf("companion CLI binary not found: %v\nTried sibling lookup, PRINTING_PRESS_GOLDEN_CLI_PATH env var, and PATH.", lookupErr)), nil
		}
		args := req.GetArguments()
		if err := validateMCPArgumentNames(args, allowedStructuredArgs); err != nil {
			return mcplib.NewToolResultError(err.Error()), nil
		}
		finalArgs := append([]string{}, prefixArgs...)
		finalArgs = append(finalArgs, cliArgsFromMCP(args, blockedStructuredArgs)...)
		positionalArgs, err := positionalArgsFromMCP(args, positionals, readOnly, positionalWriteSinks)
		if err != nil {
			return mcplib.NewToolResultError(err.Error()), nil
		}
		finalArgs = append(finalArgs, positionalArgs...)
		if raw, _ := args["args"].(string); strings.TrimSpace(raw) != "" {
			rawPositionals := positionalArgsFromRawArgsField(raw, positionals, len(positionalArgs))
			if err := validatePositionalArgsForMCPAtOffset(rawPositionals, readOnly, positionalWriteSinks, len(positionalArgs)); err != nil {
				return mcplib.NewToolResultError(err.Error()), nil
			}
			finalArgs = append(finalArgs, rawPositionals...)
		}
		out, err := RunCLICommand(ctx, lookupPath, finalArgs)
		if err != nil {
			return mcplib.NewToolResultError(err.Error()), nil
		}
		return mcplib.NewToolResultText(bound.Text(out)), nil
	}
}

func validateMCPArgumentNames(args map[string]any, allowed map[string]bool) error {
	if len(args) == 0 {
		return nil
	}
	var unknown []string
	for k := range args {
		if !allowed[k] {
			unknown = append(unknown, k)
		}
	}
	if len(unknown) == 0 {
		return nil
	}
	sort.Strings(unknown)
	if len(unknown) == 1 {
		return fmt.Errorf("unknown MCP parameter %q; use the tool schema's named parameters", unknown[0])
	}
	return fmt.Errorf("unknown MCP parameters %s; use the tool schema's named parameters", quoteMCPParameterNames(unknown))
}

func quoteMCPParameterNames(names []string) string {
	quoted := make([]string, len(names))
	for i, name := range names {
		quoted[i] = strconv.Quote(name)
	}
	return strings.Join(quoted, ", ")
}

func positionalArgsFromMCP(args map[string]any, positionals []positionalArg, readOnly bool, positionalWriteSinks map[int]bool) ([]string, error) {
	out := make([]string, 0, len(positionals))
	for positionalIndex, positional := range positionals {
		value, ok := args[positional.InputName]
		if !ok || value == nil {
			continue
		}
		var text string
		switch tv := value.(type) {
		case string:
			text = tv
		case float64:
			text = strconv.FormatFloat(tv, 'f', -1, 64)
		case bool:
			text = strconv.FormatBool(tv)
		default:
			text = fmt.Sprintf("%v", tv)
		}
		if strings.TrimSpace(text) == "" {
			continue
		}
		if positionalIndex != len(out) {
			return nil, fmt.Errorf("structured positional %q cannot be used while earlier positional arguments are omitted; provide the preceding positional arguments or use the args field", positional.InputName)
		}
		if text != "-" && strings.HasPrefix(text, "-") {
			return nil, fmt.Errorf("flag-like positional argument %q not allowed in structured positional %q; use structured flag parameters instead", text, positional.InputName)
		}
		if readOnly && positionalWriteSinks[positionalIndex] && text != "-" {
			return nil, fmt.Errorf("positional argument %d writes to %q; file output is not available for read-only MCP tools", positionalIndex+1, text)
		}
		out = append(out, text)
	}
	if err := validatePositionalArgsForMCP(out, readOnly, nil); err != nil {
		return nil, err
	}
	return out, nil
}

func positionalArgsFromRawArgsField(raw string, positionals []positionalArg, structuredCount int) []string {
	text := strings.TrimSpace(raw)
	if text == "" {
		return nil
	}
	if len(positionals) == 1 && structuredCount == 0 {
		return []string{text}
	}
	return SplitShellArgs(raw)
}

func validatePositionalArgsForMCP(tokens []string, readOnly bool, positionalWriteSinks map[int]bool) error {
	return validatePositionalArgsForMCPAtOffset(tokens, readOnly, positionalWriteSinks, 0)
}

func validatePositionalArgsForMCPAtOffset(tokens []string, readOnly bool, positionalWriteSinks map[int]bool, offset int) error {
	for _, t := range tokens {
		if t != "-" && strings.HasPrefix(t, "-") {
			return fmt.Errorf("flag-like argument %q not allowed in positional args field; use structured tool parameters instead", t)
		}
	}
	if !readOnly || len(positionalWriteSinks) == 0 {
		return nil
	}
	for i, t := range tokens {
		positionalIndex := offset + i
		if !positionalWriteSinks[positionalIndex] {
			continue
		}
		if strings.TrimSpace(t) == "" || t == "-" {
			continue
		}
		return fmt.Errorf("positional argument %d writes to %q; file output is not available for read-only MCP tools", positionalIndex+1, t)
	}
	return nil
}

// reservedStructuredArgs are MCP-only argument names that must never be
// forwarded as CLI flags.
var reservedStructuredArgs = map[string]bool{
	"args": true,
}

// blockedRootFlags are root-level CLI flags that an MCP client must not be
// able to override via structured tool parameters. Allowing them lets a
// caller swap auth credentials, redirect the API base URL, select a different
// per-client filesystem, relocate the config/data/state/cache roots, load a
// malicious config file, or change the delivery target, all of which sit
// outside the per-command surface the agent is supposed to be calling.
var blockedRootFlags = map[string]bool{
	"base-url": true,
	"client":   true,
	"config":   true,
	"deliver":  true,
	"home":     true,
	"profile":  true,
	"token":    true,
}

func cliArgsFromMCP(args map[string]any, blocked map[string]bool) []string {
	keys := make([]string, 0, len(args))
	for k := range args {
		if strings.Contains(k, "=") {
			continue
		}
		if blocked[k] {
			continue
		}
		keys = append(keys, k)
	}
	sort.Strings(keys)

	var out []string
	for _, k := range keys {
		v := args[k]
		switch tv := v.(type) {
		case bool:
			if tv {
				out = append(out, "--"+k)
			}
		case float64:
			out = append(out, "--"+k, strconv.FormatFloat(tv, 'f', -1, 64))
		case string:
			if tv != "" {
				out = append(out, "--"+k, tv)
			}
		case []any:
			if len(tv) > 0 {
				parts := make([]string, 0, len(tv))
				for _, item := range tv {
					parts = append(parts, fmt.Sprintf("%v", item))
				}
				out = append(out, "--"+k, strings.Join(parts, ","))
			}
		default:
			if v != nil {
				out = append(out, "--"+k, fmt.Sprintf("%v", v))
			}
		}
	}
	return out
}

// SplitShellArgs whitespace-splits with shell-style quote preservation.
func SplitShellArgs(s string) []string {
	var tokens []string
	var cur []rune
	inSingleQuote := false
	inDoubleQuote := false
	escaped := false
	for _, r := range s {
		switch {
		case escaped:
			cur = append(cur, r)
			escaped = false
		case r == '\\' && !inSingleQuote:
			escaped = true
		case r == '\'' && !inDoubleQuote:
			inSingleQuote = !inSingleQuote
		case r == '"' && !inSingleQuote:
			inDoubleQuote = !inDoubleQuote
		case (r == ' ' || r == '\t') && !inSingleQuote && !inDoubleQuote:
			if len(cur) > 0 {
				tokens = append(tokens, string(cur))
				cur = cur[:0]
			}
		default:
			cur = append(cur, r)
		}
	}
	if escaped {
		cur = append(cur, '\\')
	}
	if len(cur) > 0 {
		tokens = append(tokens, string(cur))
	}
	return tokens
}

// RunCLICommand executes the companion CLI while preserving stdout as the
// machine-readable channel. Stderr is included only in error text so post-run
// telemetry or quota output cannot corrupt JSON results.
func RunCLICommand(ctx context.Context, binPath string, args []string) (string, error) {
	cmd := exec.CommandContext(ctx, binPath, args...)
	var stdout, stderr bytes.Buffer
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	if err := cmd.Run(); err != nil {
		msg := strings.TrimSpace(stderr.String())
		if msg == "" {
			msg = strings.TrimSpace(stdout.String())
		}
		if msg != "" {
			label := "stderr"
			if strings.TrimSpace(stderr.String()) == "" {
				label = "output"
			}
			return stdout.String(), fmt.Errorf("cli %s: %w (%s: %s)", binPath, err, label, msg)
		}
		return stdout.String(), fmt.Errorf("cli %s: %w", binPath, err)
	}
	return stdout.String(), nil
}
