import {
  _runLineRepl,
  _clearScreen,
  _termWidth,
  _clearHistory,
  _interruptChoice,
  _stickyInterruptAvailable,
 } from "agency-lang/stdlib-lib/cli.js"

/** @module
  @summary A line-mode REPL for CLI agents, driven by Node's readline instead of the alt-screen TUI engine in std::ui.
  A line-mode REPL for CLI agents, driven by Node's `readline` instead of
  the alt-screen TUI engine in `std::ui`. It gives up the pinned status
  line, live spinner, and in-app scrolling. In exchange, every prompt
  and reply is a plain line in the terminal's scrollback, so you get
  native search, copy/paste, link clicks, line editing, and history for
  free. It shares `std::ui`'s `repl` call signature, so switching modes is
  a one-line import change:

  ```ts
  // TUI mode (alt-screen)
  import { repl } from "std::ui"

  // Line mode (scrollback)
  import { repl } from "std::ui/cli"
  ```
*/

// `pushMessage` / `clearMessages` are re-exported from `std::ui`: the
// line-mode REPL never sets `_activeRepl`, so their `_activeRepl == null`
// fallbacks (straight print, silent no-op) are the line-mode behavior.
// `chooseOption` is intentionally NOT re-exported (see the note on the
// `export` below); consumers like `std::policy` import it from `std::ui`
// directly and rely on its own null-repl fallback.

// Re-export the line-mode-compatible parts of `std::ui`. The line
// mode REPL never sets `_activeRepl`, so each of these falls through
// to its line-friendly branch:
//   - pushMessage   → straight print()
//   - clearMessages → silent no-op
// `chooseOption` is *not* re-exported here because its compiled
// schema references the `ChoiceItem` type alias from `std::ui` and
// re-exporting through the value pathway doesn't bring the type
// along (the generated `cli.js` references `ChoiceItem` unqualified
// and crashes at load time). Consumers, notably `std::policy`,
// already import `chooseOption` directly from `std::ui`, and the
// fallback path inside `chooseOption` (active when `_activeRepl` is
// null) is exactly the line-mode behavior. So nothing actually
// needed to be re-exported here for line mode to work.
export { pushMessage, clearMessages } from "std::ui"

import { chooseOption } from "std::ui"

// The active `repl()` session's history-file path, held as an Agency module
// global so it lives in the execution model (serialized, module-isolated)
// rather than captured in a TS closure. `clearHistory()` reads it to know
// which file to clear. The runtime remembers the path, not a TS hook.
let _historyFile: string = ""

/**
 * Line-mode sibling of the std::ui TUI repl. Current limitations:
 * `status` is accepted for signature parity but not yet rendered;
 * `paletteCommands` has no tab completion yet; there is no busy
 * spinner and no Ctrl+C cancel of an in-flight turn.
 */
export def repl(
  status: any,
  onSubmit: any,
  prompt: string = "> ",
  historyFile: string = "",
  historyMax: number = 1000,
  paletteCommands: any = null,
) {
  """
  Line-mode REPL with the same call signature as the std::ui TUI repl,
  so switching modes is a one-line import change.

  Each iteration prints `prompt`, awaits one line of input (full line
  editing, history, bracketed paste), and calls `onSubmit(line)`.
  Returning false exits; returning a non-empty string prints it;
  anything else is ignored. Also exits on Ctrl+D (EOF) or Ctrl+C at an
  idle prompt. Type `/paste` (TTY only) to open a multi-line editor:
  Enter inserts a newline, Ctrl+D submits the whole buffer as one
  message, and Ctrl+C / Esc cancels.

  @param status - Callback returning {left, right, context}
  @param onSubmit - Called with the submitted line; return false to exit or a string to print
  @param prompt - String shown before the input buffer (default "> ")
  @param historyFile - Path to a JSON history file; loaded at start and saved on exit. Empty string disables persistence.
  @param historyMax - Trim history to this many most-recent entries
  @param paletteCommands - Map of /cmd -> description
  """
  // Publish the path to the execution-model global so `clearHistory()` can
  // resolve which file to clear from Agency state rather than a TS closure.
  _historyFile = historyFile
  _runLineRepl(
    status,
    onSubmit,
    prompt,
    historyFile,
    historyMax,
    paletteCommands,
  )
}

export def clearScreen() {
  """Clear the terminal screen."""
  _clearScreen()
}

export def clearHistory() {
  """Clear the input history of the currently running `repl()` session: both
  its in-session up-arrow recall and the `historyFile` that session was started
  with. A no-op when called outside an interactive `repl()`."""
  _clearHistory(_historyFile)
}

def termWidth(): number {
  return _termWidth()
}

export def hline(char: string = "─", width: number = null): string {
  """Return a horizontal rule: `char` repeated `width` times (terminal
  width when `width` is omitted)."""
  let _width = width || termWidth()
  return char.repeat(_width)
}

export def interruptChoice(
  title: string,
  body: string,
  items: any[],
  allowFreeText: boolean = false,
  allowCancel: boolean = false,
): string {
  """
  Approval prompt for line mode: renders a sticky footer pinned to the
  bottom of the terminal so concurrent tool-call output streams above it
  instead of burying the prompt. Type an option key or a rejection reason,
  then press Enter. Falls back to `chooseOption` when no line-mode REPL is
  active (the TUI, a non-TTY, or a headless run), so every non-line-mode
  path keeps its current behavior.

  @param title - Prompt heading (the interrupt message).
  @param body - Multi-line context shown under the title (or "").
  @param items - The {key, label} choices.
  @param allowFreeText - Accept a free-form rejection reason.
  @param allowCancel - When true, Escape cancels the whole request.
  """
  if (_stickyInterruptAvailable()) {
    return _interruptChoice(title, body, items, allowFreeText, allowCancel)
  }
  return chooseOption(title, body, items, allowFreeText: allowFreeText, allowCancel: allowCancel)
}
