import {
  _evalValue,
  _evalOutput,
  _evalRecord,
  _evalValues,
  _evalOutputs,
  _finalEvalOutput,
} from "agency-lang/stdlib-lib/statelog.js"

/** @module
  @summary Record and read back eval data from the statelog, marking an agent's input and response for later analysis.
  Record and read back eval data from the statelog. Call `evalValue` and
  `evalOutput` inside an agent to mark its user-facing input and response,
  then read them from a saved trace with `evalValues`, `evalOutputs`,
  `finalEvalOutput`, or the full `evalRecord`. `emit` sends a custom event
  straight to the host.

  ```ts
  import { evalValue, evalOutput } from "std::statelog"

  node main(question: string) {
    evalValue(question)
    let answer: string = llm(question)
    evalOutput(answer)
  }
  ```
*/

export type StatelogEvalValue = {
  value: any;
  threadId: string | null;
  tMs: number;
  truncated?: boolean
}

export type StatelogEvalRecord = {
  traceId: string;
  recordVersion: number;
  formatVersion: number;
  durationMs: number;
  source: string;
  evalValues: StatelogEvalValue[];
  evalOutputs: StatelogEvalValue[];
  threads: Record<string, any>[];
  events: Record<string, any>[];
  interrupts: Record<string, any>[];
  errors: Record<string, any>[];
  incomplete: Record<string, any>[];
  metrics: Record<string, any>;
  warnings: string[]
}

/** Delivered to the host via the `onEmit` callback. */
export def emit(data: any) {
  """
  Emit a custom event to the calling TypeScript code.

  @param data - The event payload to emit.
  """
  _emit(data)
}

/**
 * Records the value in the statelog as an `evalValueRecorded` event, which
 * `agency eval extract` surfaces on the `evalValues[]` field. When no eval
 * annotation exists in a trace, `eval extract` falls back to a heuristic
 * (first user-role message of the first LLM call) and emits a warning.
 * Annotating explicitly is preferred. The consuming eval / judge / input
 * definition decides what to do with multiple firings.
 *
 * Serialization: the value is stored as `unknown`. Top-level `undefined`
 * records as `null`. Top-level functions and symbols throw a TypeError.
 * Nested functions, `undefined`, and symbols follow JSON rules. Circular
 * references and `bigint` throw at the call site.
 */
export def evalValue(value: any) {
  """
  Record a value as part of the user-facing input to this agent. May be called multiple times per trace; all firings are collected in order.

  @param value - The value to record. Any JSON-serializable type is accepted.
  """
  _evalValue(value)
}

/**
 * Records the value in the statelog as an `evalOutputRecorded` event, which
 * `agency eval extract` surfaces on the `evalOutputs[]` field. When no eval
 * annotation exists in a trace, `eval extract` falls back to a heuristic
 * (last LLM completion on the top-level thread) and emits a warning.
 * Annotating explicitly is preferred, since the heuristic does not account
 * for post-LLM processing the agent applies before showing a response. The
 * consuming eval / judge / task definition decides what to do with multiple
 * firings (e.g. a pairwise judge can use the last firing). Same
 * serialization rules as `evalValue`.
 */
export def evalOutput(value: any) {
  """
  Record a value as the agent's user-facing response. May be called multiple times per trace; all firings are collected in order.

  @param value - The value to record. Any JSON-serializable type is accepted.
  """
  _evalOutput(value)
}

export def evalRecord(statelogPath: string, allowedPaths: string[] = []): StatelogEvalRecord {
  """
  Parse a statelog JSONL file and return the same structured EvalRecord
  produced by `agency eval extract`. Use this when an agent needs to inspect
  a previous run without shelling out to the CLI.

  @param statelogPath - Path to the statelog JSONL file to parse
  @param allowedPaths - Optional allow-list of path prefixes. When provided,
    statelogPath must resolve under one of these prefixes.
  """
  return _evalRecord(statelogPath, allowedPaths)
}

/** Mirrors `new StatelogParser(path).evalValues()` in TypeScript. */
export def evalValues(statelogPath: string, allowedPaths: string[] = []): StatelogEvalValue[] {
  """
  Parse a statelog JSONL file and return the values recorded as eval values.

  @param statelogPath - Path to the statelog JSONL file to parse.
  @param allowedPaths - Optional allow-list of path prefixes; statelogPath must resolve under one.
  """
  return _evalValues(statelogPath, allowedPaths)
}

/** Mirrors `new StatelogParser(path).evalOutputs()` in TypeScript. */
export def evalOutputs(statelogPath: string, allowedPaths: string[] = []): StatelogEvalValue[] {
  """
  Parse a statelog JSONL file and return the values recorded as eval outputs.

  @param statelogPath - Path to the statelog JSONL file to parse.
  @param allowedPaths - Optional allow-list of path prefixes; statelogPath must resolve under one.
  """
  return _evalOutputs(statelogPath, allowedPaths)
}

export def finalEvalOutput(statelogPath: string, allowedPaths: string[] = []): StatelogEvalValue | null {
  """
  Parse a statelog JSONL file and return the final eval output, or null when
  the trace has no output. This is the canonical judge-ready final-output
  selection rule.

  @param statelogPath - Path to the statelog JSONL file to parse
  @param allowedPaths - Optional allow-list of path prefixes
  """
  return _finalEvalOutput(statelogPath, allowedPaths)
}
