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

// Package mcp — code-orchestration thin surface.
//
// Two tools cover the entire API: <api>_search to discover endpoints, and
// <api>_execute to invoke one. This collapses a large API (50+ endpoints)
// to ~1K tokens of tool definitions while preserving full coverage — the
// agent writes the composition logic in its own sandbox.
//
// Pattern source: Anthropic 2026-04-22 "Building agents that reach
// production systems with MCP" — Cloudflare's MCP server covers ~2,500
// endpoints in roughly 1K tokens via the same search+execute shape.

package mcp

import (
	"context"
	"encoding/json"
	"fmt"
	"sort"
	"strings"

	mcplib "github.com/mark3labs/mcp-go/mcp"
	"github.com/mark3labs/mcp-go/server"
)

// RegisterCodeOrchestrationTools registers the two agent-facing tools that
// cover the whole API surface. Called from RegisterTools in place of the
// per-endpoint registrations when MCP.Orchestration is "code".
func RegisterCodeOrchestrationTools(s *server.MCPServer) {
	s.AddTool(
		mcplib.NewTool("mcp-cloudflare_search",
			mcplib.WithDescription("Search the mcp-cloudflare API for endpoints matching a natural-language query. Returns a ranked list of {endpoint_id, method, path, summary} entries. Call this first to find the endpoint to execute."),
			mcplib.WithString("query", mcplib.Required(), mcplib.Description("Natural-language description of what you want to do.")),
			mcplib.WithNumber("limit", mcplib.Description("Max endpoints to return (default 10).")),
		),
		handleCodeOrchSearch,
	)

	s.AddTool(
		mcplib.NewTool("mcp-cloudflare_execute",
			mcplib.WithDescription("Execute one mcp-cloudflare API endpoint by its endpoint_id (from mcp-cloudflare_search). Params are passed as a JSON object; path placeholders and query strings are resolved automatically."),
			mcplib.WithString("endpoint_id", mcplib.Required(), mcplib.Description("Endpoint identifier returned by mcp-cloudflare_search (e.g., \"users.list\").")),
			mcplib.WithObject("params", mcplib.Description("Parameters for the endpoint. Path placeholders match by name; remaining entries become query string on GET/DELETE or JSON body on POST/PUT/PATCH.")),
		),
		handleCodeOrchExecute,
	)
}

// codeOrchEndpoint captures the small slice of endpoint metadata the
// search+execute pair needs at runtime. `keywords` is a precomputed
// lowercase stream of description + path tokens used for naive ranking;
// anything more sophisticated belongs on the agent side.
type codeOrchEndpoint struct {
	ID         string
	Method     string
	Path       string
	Tier       string
	Summary    string
	Positional []string
	// HeaderOverrides carries per-endpoint request headers (e.g. an
	// Accept override for binary-only response endpoints). Without
	// threading these through, the code-orchestration execute path
	// sends the client's default Accept and binary endpoints 406.
	HeaderOverrides map[string]string
	keywords        []string
}

// codeOrchEndpoints is the generator-populated registry covering every
// endpoint declared in the spec. Kept flat on purpose — the agent queries
// via <api>_search, so hierarchy shows up as dotted IDs, not nested maps.
var codeOrchEndpoints = []codeOrchEndpoint{
	{
		ID:         "items.list",
		Method:     "GET",
		Path:       "/items",
		Summary:    "List items",
		Positional: []string{},
		keywords:   codeOrchKeywords("items", "list", "List items", "/items"),
	},
}

// codeOrchStopwords filters two-letter and short common-word substrings
// that pollute ranking via the substring-contains rule. Without them, a
// search for "list links" matches every endpoint whose description
// contains "is enrolled" because "is" is two chars and the matcher
// accepts kw.contains(t) || t.contains(kw).
var codeOrchStopwords = map[string]bool{
	"a": true, "an": true, "and": true, "are": true, "as": true,
	"at": true, "be": true, "by": true, "for": true, "from": true,
	"has": true, "in": true, "is": true, "it": true, "its": true,
	"of": true, "on": true, "or": true, "that": true, "the": true,
	"this": true, "to": true, "was": true, "will": true, "with": true,
	"your": true, "you": true, "any": true, "all": true,
}

// codeOrchKeywords produces the lowercase token stream used for search
// ranking. Defined at package level so the registry initializer can call it
// inline above without pulling in a separate precompute step.
func codeOrchKeywords(resource, endpoint, summary, path string) []string {
	raw := strings.ToLower(resource + " " + endpoint + " " + summary + " " + path)
	raw = strings.Map(func(r rune) rune {
		switch r {
		case '_', '-', '/', '{', '}', '.', ',', ':', ';':
			return ' '
		}
		return r
	}, raw)
	out := make([]string, 0, 16)
	seen := map[string]bool{}
	for _, tok := range strings.Fields(raw) {
		if len(tok) < 3 || codeOrchStopwords[tok] || seen[tok] {
			continue
		}
		seen[tok] = true
		out = append(out, tok)
	}
	return out
}

func handleCodeOrchSearch(ctx context.Context, req mcplib.CallToolRequest) (*mcplib.CallToolResult, error) {
	args := req.GetArguments()
	query, ok := args["query"].(string)
	if !ok || strings.TrimSpace(query) == "" {
		return mcplib.NewToolResultError("query is required"), nil
	}
	limit := 10
	if v, ok := args["limit"].(float64); ok && v > 0 {
		limit = int(v)
	}

	terms := codeOrchKeywords("", "", query, "")
	type scored struct {
		ep    *codeOrchEndpoint
		score int
	}
	results := make([]scored, 0, len(codeOrchEndpoints))
	for i := range codeOrchEndpoints {
		ep := &codeOrchEndpoints[i]
		score := 0
		for _, t := range terms {
			for _, kw := range ep.keywords {
				if kw == t {
					score += 2
				} else if strings.Contains(kw, t) || strings.Contains(t, kw) {
					score++
				}
			}
		}
		if score > 0 {
			results = append(results, scored{ep: ep, score: score})
		}
	}
	sort.SliceStable(results, func(i, j int) bool { return results[i].score > results[j].score })
	if len(results) > limit {
		results = results[:limit]
	}

	out := make([]map[string]any, 0, len(results))
	for _, r := range results {
		out = append(out, map[string]any{
			"endpoint_id": r.ep.ID,
			"method":      r.ep.Method,
			"path":        r.ep.Path,
			"summary":     r.ep.Summary,
			"score":       r.score,
		})
	}
	data, _ := json.Marshal(map[string]any{"count": len(out), "results": out})
	return mcplib.NewToolResultText(string(data)), nil
}

func handleCodeOrchExecute(ctx context.Context, req mcplib.CallToolRequest) (*mcplib.CallToolResult, error) {
	args := req.GetArguments()
	id, ok := args["endpoint_id"].(string)
	if !ok || id == "" {
		return mcplib.NewToolResultError("endpoint_id is required (call mcp-cloudflare_search first)"), nil
	}

	var ep *codeOrchEndpoint
	for i := range codeOrchEndpoints {
		if codeOrchEndpoints[i].ID == id {
			ep = &codeOrchEndpoints[i]
			break
		}
	}
	if ep == nil {
		return mcplib.NewToolResultError(fmt.Sprintf("unknown endpoint_id %q — call mcp-cloudflare_search to discover valid ids", id)), nil
	}

	params, _ := args["params"].(map[string]any)
	if params == nil {
		params = map[string]any{}
	}

	c, err := newMCPClient()
	if err != nil {
		return mcplib.NewToolResultError(err.Error()), nil
	}

	path := ep.Path
	for _, p := range ep.Positional {
		if v, ok := params[p]; ok {
			path = strings.ReplaceAll(path, "{"+p+"}", fmt.Sprintf("%v", v))
			delete(params, p)
		}
	}

	// Code-orch dispatch has no schema metadata at runtime to bucket params
	// into body vs query, so POST/PUT/PATCH keep the everything-to-body
	// design. DELETE is the one verb where query routing is unambiguous; the
	// previous code built `query` but never passed it.
	query := map[string]string{}
	if ep.Method == "GET" || ep.Method == "DELETE" {
		for k, v := range params {
			query[k] = fmt.Sprintf("%v", v)
		}
	}

	hdrs := ep.HeaderOverrides
	var data json.RawMessage
	switch ep.Method {
	case "GET":
		if len(hdrs) > 0 {
			data, err = c.GetWithHeaders(path, query, hdrs)
		} else {
			data, err = c.Get(path, query)
		}
	case "DELETE":
		if len(hdrs) > 0 {
			data, _, err = c.DeleteWithParamsAndHeaders(path, query, hdrs)
		} else {
			data, _, err = c.DeleteWithParams(path, query)
		}
	case "POST":
		if len(hdrs) > 0 {
			data, _, err = c.PostWithHeaders(path, params, hdrs)
		} else {
			data, _, err = c.Post(path, params)
		}
	case "PUT":
		if len(hdrs) > 0 {
			data, _, err = c.PutWithHeaders(path, params, hdrs)
		} else {
			data, _, err = c.Put(path, params)
		}
	case "PATCH":
		if len(hdrs) > 0 {
			data, _, err = c.PatchWithHeaders(path, params, hdrs)
		} else {
			data, _, err = c.Patch(path, params)
		}
	default:
		return mcplib.NewToolResultError(fmt.Sprintf("unsupported method %q", ep.Method)), nil
	}
	if err != nil {
		return mcplib.NewToolResultError(err.Error()), nil
	}
	return mcplib.NewToolResultText(string(data)), nil
}
