import { hostedSearchTools, searchTools } from "std::agents/lib/search"
import { AGENT_SPILL_OVER, llmOptions, withContext } from "std::agents/lib/shared"
import { webTools, spillTools, communicationTools, SAVE_DRAFT_HINT, ASK_USER_HINT } from "std::agents/lib/toolkits"
import { dbnomicsSeries } from "std::data/finance/dbnomics"
import { edgarFilings, edgarFilingsByCik } from "std::data/finance/edgar"
import { fredSeries, fredSeriesInfo } from "std::data/finance/fred"
import { gdeltNews } from "std::data/news/gdelt"
import {
  littlesisSearch,
  littlesisEntity,
  littlesisRelationships,
  littlesisConnections,
 } from "std::data/people/littlesis"
import { hnStories, hnItem, hnUser, hnSearch } from "std::data/tech/hackernews"
import { ycBatch, ycIndustry, ycTag, ycList, ycMeta } from "std::data/tech/yc"
import { usaspendingAwards, usaspendingAward } from "std::data/usaspending"
import {
  wikidataSearch,
  wikidataEntity,
  wikidataQuery,
 } from "std::data/wikidata"
import { guarded } from "std::decorators"
import { agentSkill } from "std::skills"
import { systemMessage, threadIsNew } from "std::thread"

/** @module
@summary Answer questions using structured public-data APIs.

Use `dataAgent` for economic series, securities filings, public spending,
news, and entity records. It can search for identifiers before querying an
API. Use `researcherAgent` for questions answered mainly by web pages.

```ts
import { dataAgent } from "std::agents/data"

node main() {
  print(dataAgent("Find the latest reported US unemployment rate and cite its series"))
}
```
*/

static const systemPrompt = """You answer questions using structured public-data APIs.

- Find the exact identifier before you query. A FRED series ID, an EDGAR CIK, a Wikidata Q-number: look it up rather than guessing, because a wrong identifier returns a real answer to the wrong question, which is far worse than an error.
- Name the source and the identifier behind every figure you report, so a reader can check it.
- State the units, the period, and any revision or seasonal adjustment the source applies. A number without its units is not an answer.
- When a source has no data for what was asked, say so plainly. NEVER estimate a number and present it as retrieved.

## Communicating with the user
- Make sure the user is following what you're doing. Use the `whatIAmDoing` tool frequently to tell the user what you're doing.${ASK_USER_HINT}${SAVE_DRAFT_HINT}
"""

static const skills = agentSkill("data")

export def buildTools(): any[] {
  """
  Return the data agent's connectors and source lookup tools.
  """
  return [
    fredSeries,
    fredSeriesInfo,
    dbnomicsSeries,
    edgarFilings,
    edgarFilingsByCik,
    gdeltNews,
    usaspendingAwards,
    usaspendingAward,
    wikidataSearch,
    wikidataEntity,
    wikidataQuery,
    littlesisSearch,
    littlesisEntity,
    littlesisRelationships,
    littlesisConnections,
    hnStories,
    hnItem,
    hnUser,
    hnSearch,
    ycBatch,
    ycIndustry,
    ycTag,
    ycList,
    ycMeta,
    ...webTools(),
    ...spillTools(),
    ...searchTools(),
    ...communicationTools(),
    skills,
  ]
}

def answer(
  task: string,
  context: string,
  model: string,
  provider: string,
  extraTools: any[],
): string {
  if (threadIsNew()) {
    systemMessage(systemPrompt)
  }

  return llm(
    withContext(task: task, context: context),
    llmOptions(
    model: model,
    provider: provider,
    tools: [...buildTools(), ...extraTools, saveDraft],
    spillToolResultsOver: AGENT_SPILL_OVER,
    hostedTools: hostedSearchTools(model, provider),
  ),
  )
}

@guarded(cost: maxCost, time: maxTime)
export def dataAgent(
  task: string,
  context: string = "",
  maxCost: number = $20.00,
  maxTime: number = 15m,
  model: string = "",
  provider: string = "",
  session: string = "",
  extraTools: any[] = [],
): Result<string> {
  """
  Answer a question from structured public-data APIs, naming the source and
  identifier behind every figure.

  @param task - The question to answer
  @param context - Extra material folded into the prompt, or ""
  @param maxCost - Cost budget in USD.
  @param maxTime - Time budget in milliseconds.
  @param model - Model override, or "" for the ambient model
  @param provider - Provider for the model override, ignored when model is empty.
  @param session - Session name to share a thread across calls, or "" for isolated
  @param extraTools - Extra tools to offer the LLM, appended to the built-in set
  """

  thread(label: "dataAgent", session: session) {
    return answer(
      task: task,
      context: context,
      model: model,
      provider: provider,
      extraTools: extraTools,
    )
  }
}
