// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// deno-lint-ignore-file no-explicit-any

// Forked from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/cd61f5b4d3d143108569ec3f88adc0eb34b961c4/types/node/readline.d.ts

import { Abortable, EventEmitter } from "ext:deno_node/_events.d.ts";
import * as promises from "ext:deno_node/readline/promises.ts";
import { ReadableStream, WritableStream } from "ext:deno_node/_global.d.ts";
import { Buffer } from "node:buffer";
import type {
  AsyncCompleter,
  Completer,
  CompleterResult,
  ReadLineOptions,
} from "ext:deno_node/_readline_shared_types.d.ts";

/**
 * The `readline` module provides an interface for reading data from a `Readable` stream (such as `process.stdin`) one line at a time.
 *
 * To use the promise-based APIs:
 *
 * Once this code is invoked, the Node.js application will not terminate until the`readline.Interface` is closed because the interface waits for data to be
 * received on the `input` stream.
 * @see [source](https://github.com/nodejs/node/blob/v18.0.0/lib/readline.js)
 */
export { promises };
export interface Key {
  sequence?: string | undefined;
  name?: string | undefined;
  ctrl?: boolean | undefined;
  meta?: boolean | undefined;
  shift?: boolean | undefined;
}
/**
 * Instances of the `readline.Interface` class are constructed using the`readline.createInterface()` method. Every instance is associated with a
 * single `input` `Readable` stream and a single `output` `Writable` stream.
 * The `output` stream is used to print prompts for user input that arrives on,
 * and is read from, the `input` stream.
 * @since v0.1.104
 */
export class Interface extends EventEmitter {
  readonly terminal: boolean;
  /**
   * The current input data being processed by node.
   *
   * This can be used when collecting input from a TTY stream to retrieve the
   * current value that has been processed thus far, prior to the `line` event
   * being emitted. Once the `line` event has been emitted, this property will
   * be an empty string.
   *
   * Be aware that modifying the value during the instance runtime may have
   * unintended consequences if `rl.cursor` is not also controlled.
   *
   * **If not using a TTY stream for input, use the `'line'` event.**
   *
   * One possible use case would be as follows:
   *
   * ```js
   * const values = ['lorem ipsum', 'dolor sit amet'];
   * const rl = readline.createInterface(process.stdin);
   * const showResults = debounce(() => {
   *   console.log(
   *     '\n',
   *     values.filter((val) => val.startsWith(rl.line)).join(' ')
   *   );
   * }, 300);
   * process.stdin.on('keypress', (c, k) => {
   *   showResults();
   * });
   * ```
   * @since v0.1.98
   */
  readonly line: string;
  /**
   * The cursor position relative to `rl.line`.
   *
   * This will track where the current cursor lands in the input string, when
   * reading input from a TTY stream. The position of cursor determines the
   * portion of the input string that will be modified as input is processed,
   * as well as the column where the terminal caret will be rendered.
   * @since v0.1.98
   */
  readonly cursor: number;
  /**
   * NOTE: According to the documentation:
   *
   * > Instances of the `readline.Interface` class are constructed using the
   * > `readline.createInterface()` method.
   *
   * @see https://nodejs.org/dist/latest-v10.x/docs/api/readline.html#readline_class_interface
   */
  protected constructor(
    input: ReadableStream,
    output?: WritableStream,
    completer?: Completer | AsyncCompleter,
    terminal?: boolean,
  );
  /**
   * NOTE: According to the documentation:
   *
   * > Instances of the `readline.Interface` class are constructed using the
   * > `readline.createInterface()` method.
   *
   * @see https://nodejs.org/dist/latest-v10.x/docs/api/readline.html#readline_class_interface
   */
  protected constructor(options: ReadLineOptions);
  /**
   * The `rl.getPrompt()` method returns the current prompt used by `rl.prompt()`.
   * @since v15.3.0
   * @return the current prompt string
   */
  getPrompt(): string;
  /**
   * The `rl.setPrompt()` method sets the prompt that will be written to `output`whenever `rl.prompt()` is called.
   * @since v0.1.98
   */
  setPrompt(prompt: string): void;
  /**
   * The `rl.prompt()` method writes the `readline.Interface` instances configured`prompt` to a new line in `output` in order to provide a user with a new
   * location at which to provide input.
   *
   * When called, `rl.prompt()` will resume the `input` stream if it has been
   * paused.
   *
   * If the `readline.Interface` was created with `output` set to `null` or`undefined` the prompt is not written.
   * @since v0.1.98
   * @param preserveCursor If `true`, prevents the cursor placement from being reset to `0`.
   */
  prompt(preserveCursor?: boolean): void;
  /**
   * The `rl.question()` method displays the `query` by writing it to the `output`,
   * waits for user input to be provided on `input`, then invokes the `callback`function passing the provided input as the first argument.
   *
   * When called, `rl.question()` will resume the `input` stream if it has been
   * paused.
   *
   * If the `readline.Interface` was created with `output` set to `null` or`undefined` the `query` is not written.
   *
   * The `callback` function passed to `rl.question()` does not follow the typical
   * pattern of accepting an `Error` object or `null` as the first argument.
   * The `callback` is called with the provided answer as the only argument.
   *
   * Example usage:
   *
   * ```js
   * rl.question('What is your favorite food? ', (answer) => {
   *   console.log(`Oh, so your favorite food is ${answer}`);
   * });
   * ```
   *
   * Using an `AbortController` to cancel a question.
   *
   * ```js
   * const ac = new AbortController();
   * const signal = ac.signal;
   *
   * rl.question('What is your favorite food? ', { signal }, (answer) => {
   *   console.log(`Oh, so your favorite food is ${answer}`);
   * });
   *
   * signal.addEventListener('abort', () => {
   *   console.log('The food question timed out');
   * }, { once: true });
   *
   * setTimeout(() => ac.abort(), 10000);
   * ```
   *
   * If this method is invoked as it's util.promisify()ed version, it returns a
   * Promise that fulfills with the answer. If the question is canceled using
   * an `AbortController` it will reject with an `AbortError`.
   *
   * ```js
   * const util = require('util');
   * const question = util.promisify(rl.question).bind(rl);
   *
   * async function questionExample() {
   *   try {
   *     const answer = await question('What is you favorite food? ');
   *     console.log(`Oh, so your favorite food is ${answer}`);
   *   } catch (err) {
   *     console.error('Question rejected', err);
   *   }
   * }
   * questionExample();
   * ```
   * @since v0.3.3
   * @param query A statement or query to write to `output`, prepended to the prompt.
   * @param callback A callback function that is invoked with the user's input in response to the `query`.
   */
  question(query: string, callback: (answer: string) => void): void;
  question(
    query: string,
    options: Abortable,
    callback: (answer: string) => void,
  ): void;
  /**
   * The `rl.pause()` method pauses the `input` stream, allowing it to be resumed
   * later if necessary.
   *
   * Calling `rl.pause()` does not immediately pause other events (including`'line'`) from being emitted by the `readline.Interface` instance.
   * @since v0.3.4
   */
  pause(): this;
  /**
   * The `rl.resume()` method resumes the `input` stream if it has been paused.
   * @since v0.3.4
   */
  resume(): this;
  /**
   * The `rl.close()` method closes the `readline.Interface` instance and
   * relinquishes control over the `input` and `output` streams. When called,
   * the `'close'` event will be emitted.
   *
   * Calling `rl.close()` does not immediately stop other events (including `'line'`)
   * from being emitted by the `readline.Interface` instance.
   * @since v0.1.98
   */
  close(): void;
  /**
   * The `rl.write()` method will write either `data` or a key sequence identified
   * by `key` to the `output`. The `key` argument is supported only if `output` is
   * a `TTY` text terminal. See `TTY keybindings` for a list of key
   * combinations.
   *
   * If `key` is specified, `data` is ignored.
   *
   * When called, `rl.write()` will resume the `input` stream if it has been
   * paused.
   *
   * If the `readline.Interface` was created with `output` set to `null` or`undefined` the `data` and `key` are not written.
   *
   * ```js
   * rl.write('Delete this!');
   * // Simulate Ctrl+U to delete the line written previously
   * rl.write(null, { ctrl: true, name: 'u' });
   * ```
   *
   * The `rl.write()` method will write the data to the `readline` `Interface`'s`input`_as if it were provided by the user_.
   * @since v0.1.98
   */
  write(data: string | Buffer, key?: Key): void;
  write(data: undefined | null | string | Buffer, key: Key): void;
  /**
   * Returns the real position of the cursor in relation to the input
   * prompt + string. Long input (wrapping) strings, as well as multiple
   * line prompts are included in the calculations.
   * @since v13.5.0, v12.16.0
   */
  getCursorPos(): CursorPos;
  /**
   * events.EventEmitter
   * 1. close
   * 2. line
   * 3. pause
   * 4. resume
   * 5. SIGCONT
   * 6. SIGINT
   * 7. SIGTSTP
   * 8. history
   */
  addListener(event: string, listener: (...args: any[]) => void): this;
  addListener(event: "close", listener: () => void): this;
  addListener(event: "line", listener: (input: string) => void): this;
  addListener(event: "pause", listener: () => void): this;
  addListener(event: "resume", listener: () => void): this;
  addListener(event: "SIGCONT", listener: () => void): this;
  addListener(event: "SIGINT", listener: () => void): this;
  addListener(event: "SIGTSTP", listener: () => void): this;
  addListener(event: "history", listener: (history: string[]) => void): this;
  emit(event: string | symbol, ...args: any[]): boolean;
  emit(event: "close"): boolean;
  emit(event: "line", input: string): boolean;
  emit(event: "pause"): boolean;
  emit(event: "resume"): boolean;
  emit(event: "SIGCONT"): boolean;
  emit(event: "SIGINT"): boolean;
  emit(event: "SIGTSTP"): boolean;
  emit(event: "history", history: string[]): boolean;
  on(event: string, listener: (...args: any[]) => void): this;
  on(event: "close", listener: () => void): this;
  on(event: "line", listener: (input: string) => void): this;
  on(event: "pause", listener: () => void): this;
  on(event: "resume", listener: () => void): this;
  on(event: "SIGCONT", listener: () => void): this;
  on(event: "SIGINT", listener: () => void): this;
  on(event: "SIGTSTP", listener: () => void): this;
  on(event: "history", listener: (history: string[]) => void): this;
  once(event: string, listener: (...args: any[]) => void): this;
  once(event: "close", listener: () => void): this;
  once(event: "line", listener: (input: string) => void): this;
  once(event: "pause", listener: () => void): this;
  once(event: "resume", listener: () => void): this;
  once(event: "SIGCONT", listener: () => void): this;
  once(event: "SIGINT", listener: () => void): this;
  once(event: "SIGTSTP", listener: () => void): this;
  once(event: "history", listener: (history: string[]) => void): this;
  prependListener(event: string, listener: (...args: any[]) => void): this;
  prependListener(event: "close", listener: () => void): this;
  prependListener(event: "line", listener: (input: string) => void): this;
  prependListener(event: "pause", listener: () => void): this;
  prependListener(event: "resume", listener: () => void): this;
  prependListener(event: "SIGCONT", listener: () => void): this;
  prependListener(event: "SIGINT", listener: () => void): this;
  prependListener(event: "SIGTSTP", listener: () => void): this;
  prependListener(
    event: "history",
    listener: (history: string[]) => void,
  ): this;
  prependOnceListener(event: string, listener: (...args: any[]) => void): this;
  prependOnceListener(event: "close", listener: () => void): this;
  prependOnceListener(event: "line", listener: (input: string) => void): this;
  prependOnceListener(event: "pause", listener: () => void): this;
  prependOnceListener(event: "resume", listener: () => void): this;
  prependOnceListener(event: "SIGCONT", listener: () => void): this;
  prependOnceListener(event: "SIGINT", listener: () => void): this;
  prependOnceListener(event: "SIGTSTP", listener: () => void): this;
  prependOnceListener(
    event: "history",
    listener: (history: string[]) => void,
  ): this;
  [Symbol.asyncIterator](): AsyncIterableIterator<string>;
}
export type ReadLine = Interface; // type forwarded for backwards compatibility
export { AsyncCompleter, Completer, CompleterResult, ReadLineOptions };
/**
 * The `readline.createInterface()` method creates a new `readline.Interface`instance.
 *
 * ```js
 * const readline = require('readline');
 * const rl = readline.createInterface({
 *   input: process.stdin,
 *   output: process.stdout
 * });
 * ```
 *
 * Once the `readline.Interface` instance is created, the most common case is to
 * listen for the `'line'` event:
 *
 * ```js
 * rl.on('line', (line) => {
 *   console.log(`Received: ${line}`);
 * });
 * ```
 *
 * If `terminal` is `true` for this instance then the `output` stream will get
 * the best compatibility if it defines an `output.columns` property and emits
 * a `'resize'` event on the `output` if or when the columns ever change
 * (`process.stdout` does this automatically when it is a TTY).
 *
 * When creating a `readline.Interface` using `stdin` as input, the program
 * will not terminate until it receives `EOF` (Ctrl+D on
 * Linux/macOS, Ctrl+Z followed by Return on
 * Windows).
 * If you want your application to exit without waiting for user input, you can `unref()` the standard input stream:
 *
 * ```js
 * process.stdin.unref();
 * ```
 * @since v0.1.98
 */
export function createInterface(
  input: ReadableStream,
  output?: WritableStream,
  completer?: Completer | AsyncCompleter,
  terminal?: boolean,
): Interface;
export function createInterface(options: ReadLineOptions): Interface;
/**
 * The `readline.emitKeypressEvents()` method causes the given `Readable` stream to begin emitting `'keypress'` events corresponding to received input.
 *
 * Optionally, `interface` specifies a `readline.Interface` instance for which
 * autocompletion is disabled when copy-pasted input is detected.
 *
 * If the `stream` is a `TTY`, then it must be in raw mode.
 *
 * This is automatically called by any readline instance on its `input` if the`input` is a terminal. Closing the `readline` instance does not stop
 * the `input` from emitting `'keypress'` events.
 *
 * ```js
 * readline.emitKeypressEvents(process.stdin);
 * if (process.stdin.isTTY)
 *   process.stdin.setRawMode(true);
 * ```
 *
 * ## Example: Tiny CLI
 *
 * The following example illustrates the use of `readline.Interface` class to
 * implement a small command-line interface:
 *
 * ```js
 * const readline = require('readline');
 * const rl = readline.createInterface({
 *   input: process.stdin,
 *   output: process.stdout,
 *   prompt: 'OHAI> '
 * });
 *
 * rl.prompt();
 *
 * rl.on('line', (line) => {
 *   switch (line.trim()) {
 *     case 'hello':
 *       console.log('world!');
 *       break;
 *     default:
 *       console.log(`Say what? I might have heard '${line.trim()}'`);
 *       break;
 *   }
 *   rl.prompt();
 * }).on('close', () => {
 *   console.log('Have a great day!');
 *   process.exit(0);
 * });
 * ```
 *
 * ## Example: Read file stream line-by-Line
 *
 * A common use case for `readline` is to consume an input file one line at a
 * time. The easiest way to do so is leveraging the `fs.ReadStream` API as
 * well as a `for await...of` loop:
 *
 * ```js
 * const fs = require('fs');
 * const readline = require('readline');
 *
 * async function processLineByLine() {
 *   const fileStream = fs.createReadStream('input.txt');
 *
 *   const rl = readline.createInterface({
 *     input: fileStream,
 *     crlfDelay: Infinity
 *   });
 *   // Note: we use the crlfDelay option to recognize all instances of CR LF
 *   // ('\r\n') in input.txt as a single line break.
 *
 *   for await (const line of rl) {
 *     // Each line in input.txt will be successively available here as `line`.
 *     console.log(`Line from file: ${line}`);
 *   }
 * }
 *
 * processLineByLine();
 * ```
 *
 * Alternatively, one could use the `'line'` event:
 *
 * ```js
 * const fs = require('fs');
 * const readline = require('readline');
 *
 * const rl = readline.createInterface({
 *   input: fs.createReadStream('sample.txt'),
 *   crlfDelay: Infinity
 * });
 *
 * rl.on('line', (line) => {
 *   console.log(`Line from file: ${line}`);
 * });
 * ```
 *
 * Currently, `for await...of` loop can be a bit slower. If `async` / `await`flow and speed are both essential, a mixed approach can be applied:
 *
 * ```js
 * const { once } = require('events');
 * const { createReadStream } = require('fs');
 * const { createInterface } = require('readline');
 *
 * (async function processLineByLine() {
 *   try {
 *     const rl = createInterface({
 *       input: createReadStream('big-file.txt'),
 *       crlfDelay: Infinity
 *     });
 *
 *     rl.on('line', (line) => {
 *       // Process the line.
 *     });
 *
 *     await once(rl, 'close');
 *
 *     console.log('File processed.');
 *   } catch (err) {
 *     console.error(err);
 *   }
 * })();
 * ```
 * @since v0.7.7
 */
export function emitKeypressEvents(
  stream: ReadableStream,
  readlineInterface?: Interface,
): void;
export type Direction = -1 | 0 | 1;
export interface CursorPos {
  rows: number;
  cols: number;
}
/**
 * The `readline.clearLine()` method clears current line of given `TTY` stream
 * in a specified direction identified by `dir`.
 * @since v0.7.7
 * @param callback Invoked once the operation completes.
 * @return `false` if `stream` wishes for the calling code to wait for the `'drain'` event to be emitted before continuing to write additional data; otherwise `true`.
 */
export function clearLine(
  stream: WritableStream,
  dir: Direction,
  callback?: () => void,
): boolean;
/**
 * The `readline.clearScreenDown()` method clears the given `TTY` stream from
 * the current position of the cursor down.
 * @since v0.7.7
 * @param callback Invoked once the operation completes.
 * @return `false` if `stream` wishes for the calling code to wait for the `'drain'` event to be emitted before continuing to write additional data; otherwise `true`.
 */
export function clearScreenDown(
  stream: WritableStream,
  callback?: () => void,
): boolean;
/**
 * The `readline.cursorTo()` method moves cursor to the specified position in a
 * given `TTY` `stream`.
 * @since v0.7.7
 * @param callback Invoked once the operation completes.
 * @return `false` if `stream` wishes for the calling code to wait for the `'drain'` event to be emitted before continuing to write additional data; otherwise `true`.
 */
export function cursorTo(
  stream: WritableStream,
  x: number,
  y?: number,
  callback?: () => void,
): boolean;
/**
 * The `readline.moveCursor()` method moves the cursor _relative_ to its current
 * position in a given `TTY` `stream`.
 *
 * ## Example: Tiny CLI
 *
 * The following example illustrates the use of `readline.Interface` class to
 * implement a small command-line interface:
 *
 * ```js
 * const readline = require('readline');
 * const rl = readline.createInterface({
 *   input: process.stdin,
 *   output: process.stdout,
 *   prompt: 'OHAI> '
 * });
 *
 * rl.prompt();
 *
 * rl.on('line', (line) => {
 *   switch (line.trim()) {
 *     case 'hello':
 *       console.log('world!');
 *       break;
 *     default:
 *       console.log(`Say what? I might have heard '${line.trim()}'`);
 *       break;
 *   }
 *   rl.prompt();
 * }).on('close', () => {
 *   console.log('Have a great day!');
 *   process.exit(0);
 * });
 * ```
 *
 * ## Example: Read file stream line-by-Line
 *
 * A common use case for `readline` is to consume an input file one line at a
 * time. The easiest way to do so is leveraging the `fs.ReadStream` API as
 * well as a `for await...of` loop:
 *
 * ```js
 * const fs = require('fs');
 * const readline = require('readline');
 *
 * async function processLineByLine() {
 *   const fileStream = fs.createReadStream('input.txt');
 *
 *   const rl = readline.createInterface({
 *     input: fileStream,
 *     crlfDelay: Infinity
 *   });
 *   // Note: we use the crlfDelay option to recognize all instances of CR LF
 *   // ('\r\n') in input.txt as a single line break.
 *
 *   for await (const line of rl) {
 *     // Each line in input.txt will be successively available here as `line`.
 *     console.log(`Line from file: ${line}`);
 *   }
 * }
 *
 * processLineByLine();
 * ```
 *
 * Alternatively, one could use the `'line'` event:
 *
 * ```js
 * const fs = require('fs');
 * const readline = require('readline');
 *
 * const rl = readline.createInterface({
 *   input: fs.createReadStream('sample.txt'),
 *   crlfDelay: Infinity
 * });
 *
 * rl.on('line', (line) => {
 *   console.log(`Line from file: ${line}`);
 * });
 * ```
 *
 * Currently, `for await...of` loop can be a bit slower. If `async` / `await`flow and speed are both essential, a mixed approach can be applied:
 *
 * ```js
 * const { once } = require('events');
 * const { createReadStream } = require('fs');
 * const { createInterface } = require('readline');
 *
 * (async function processLineByLine() {
 *   try {
 *     const rl = createInterface({
 *       input: createReadStream('big-file.txt'),
 *       crlfDelay: Infinity
 *     });
 *
 *     rl.on('line', (line) => {
 *       // Process the line.
 *     });
 *
 *     await once(rl, 'close');
 *
 *     console.log('File processed.');
 *   } catch (err) {
 *     console.error(err);
 *   }
 * })();
 * ```
 * @since v0.7.7
 * @param callback Invoked once the operation completes.
 * @return `false` if `stream` wishes for the calling code to wait for the `'drain'` event to be emitted before continuing to write additional data; otherwise `true`.
 */
export function moveCursor(
  stream: WritableStream,
  dx: number,
  dy: number,
  callback?: () => void,
): boolean;