import { _hostedSearchTools } from "agency-lang/stdlib-lib/llm.js"
import { env } from "std::system"
import { search as braveSearch, tavilySearch } from "std::web/search"

/** @module
@summary Select client-side and provider-hosted search tools for agents.

`searchTools` selects Brave when `BRAVE_API_KEY` is available, then Tavily
when `TAVILY_API_KEY` is available, or no client-side tool without a key.
`hostedSearchTools` selects hosted search for the model and provider route
used by an LLM call. Both can be offered together.
*/

type SearchProvider = {
  name: string;
  envKey: string;
  toolName: string;
  tool: any
}

static const providers: SearchProvider[] = [
  {
    name: "Brave",
    envKey: "BRAVE_API_KEY",
    toolName: "web_search_brave",
    tool: braveSearch
  },
  {
    name: "Tavily",
    envKey: "TAVILY_API_KEY",
    toolName: "web_search_tavily",
    tool: tavilySearch
  },
]

def hasKey(provider: SearchProvider): boolean {
  const key = env(provider.envKey) with approve
  return key != null && key != ""
}

def selectedProviders(): SearchProvider[] {
  const provider = find(providers, hasKey)
  if (provider == null) {
    return []
  }
  return [provider]
}

def boundTool(provider: SearchProvider): any {
  // Bind the key so it is absent from the tool schema and the model cannot replace it.
  const key = env(provider.envKey) with approve
  return provider.tool.partial(apiKey: key).rename(provider.toolName)
}

export def searchTools(): any[] {
  """
  Return one web-search tool, preferring BRAVE_API_KEY over TAVILY_API_KEY.
  Return an empty array when neither key is set.
  """
  return map(selectedProviders(), boundTool)
}

export def searchProviderName(toolName: string): string | null {
  """
  Return the catalog provider name for a search tool, or null when its name is unknown.
  """
  const provider = find(providers, \provider -> provider.toolName == toolName)
  if (provider == null) {
    return null
  }
  return provider.name
}

export def hostedSearchTools(model: string = "", provider: string = ""): string[] {
  """
  Select provider-hosted search for an LLM call's model and provider route.

  @param model - Use the same model as the call, or "" for the ambient model.
  @param provider - Use the same provider as the call, or "" for automatic resolution.
  """
  return _hostedSearchTools(model, provider)
}

export def searchProviderNames(): string[] {
  """
  Return the selected web-search provider's name, preferring Brave over
  Tavily, or an empty array when neither API key is set.
  """
  return map(selectedProviders(), \provider -> provider.name)
}
