import type { Agent } from 'node:https';
import { RequireApproval } from '@aws-cdk/cloud-assembly-schema';
import type { IIoHost, IoMessage, IoMessageCode, IoMessageLevel, IoRequest, ToolkitAction } from '@aws-cdk/toolkit-lib';
import type { IoHelper, IoMessageMaker, IoRequestMaker, IoDefaultMessages } from '../../../lib/api-private';
import type { Context } from '../../api/context';
import { StackActivityProgress } from '../../commands/deploy';
import { TelemetrySession } from '../telemetry/session';
export type { IIoHost, IoMessage, IoMessageCode, IoMessageLevel, IoRequest };
/**
 * The current action being performed by the CLI. 'none' represents the absence of an action.
 */
type CliAction = ToolkitAction | 'context' | 'docs' | 'flags' | 'notices' | 'version' | 'cli-telemetry' | 'none';
export interface CliIoHostProps {
    /**
     * The initial Toolkit action the hosts starts with.
     *
     * @default 'none'
     */
    readonly currentAction?: CliAction;
    /**
     * Determines the verbosity of the output.
     *
     * The CliIoHost will still receive all messages and requests,
     * but only the messages included in this level will be printed.
     *
     * @default 'info'
     */
    readonly logLevel?: IoMessageLevel;
    /**
     * Overrides the automatic TTY detection.
     *
     * When TTY is disabled, the CLI will have no interactions or color.
     *
     * @default - determined from the current process
     */
    readonly isTTY?: boolean;
    /**
     * Whether the CliIoHost is running in CI mode.
     *
     * In CI mode, all non-error output goes to stdout instead of stderr.
     * Set to false in the CliIoHost constructor it will be overwritten if the CLI CI argument is passed
     *
     * @default - determined from the environment, specifically based on `process.env.CI`
     */
    readonly isCI?: boolean;
    /**
     * In what scenarios should the CliIoHost ask for approval
     *
     * @default RequireApproval.BROADENING
     */
    readonly requireDeployApproval?: RequireApproval;
    /**
     * The initial Toolkit action the hosts starts with.
     *
     * @default StackActivityProgress.BAR
     */
    readonly stackProgress?: StackActivityProgress;
    /**
     * Whether the CLI should attempt to automatically respond to prompts.
     *
     * When true, operation will usually proceed without interactive confirmation.
     * Confirmations are responded to with yes. Other prompts will respond with the default value.
     *
     * @default false
     */
    readonly autoRespond?: boolean;
}
/**
 * A type for configuring a target stream
 */
export type TargetStream = 'stdout' | 'stderr' | 'drop';
/**
 * The result a message listener may return to influence how a message is handled.
 *
 * A listener may update the message _text_ and/or its _level_; it cannot change
 * any other field of the message (such as its `code`), which keeps the
 * code-keyed listener registry valid.
 */
export interface MessageListenerResult {
    /**
     * Replace the text that is printed for this message.
     *
     * @default - the message text is left unchanged
     */
    readonly message?: string;
    /**
     * Override the level of this message.
     *
     * The new level is used for both verbosity filtering and stream selection, so
     * this can move a message between stdout/stderr (e.g. downgrade a `result` to
     * `info`). The `code` is intentionally left unchanged.
     *
     * @default - the message level is left unchanged
     */
    readonly level?: IoMessageLevel;
    /**
     * Skip the default handling of the message.
     *
     * For a notification this means it is not written to a stream. For a request
     * it stops processing entirely: the user is not prompted, nothing is written,
     * and the request resolves with its (possibly `respond`-overridden) default
     * response.
     *
     * @default false
     */
    readonly preventDefault?: boolean;
    /**
     * For requests only: the value to resolve the request with. It is folded into
     * the request's default response and skips the prompt (the request is treated
     * as not promptable). The question is still written unless `preventDefault` is
     * also set. Ignored for plain notifications.
     *
     * The presence of the key is what matters, so `false`/`0`/`''` are valid
     * answers. Use the `respond`/`respondOnce` helpers for the common case.
     *
     * @default - this listener does not supply a response
     */
    readonly respond?: unknown;
}
/**
 * What a message listener may return: nothing, a `MessageListenerResult`, or a
 * `Promise` of either.
 *
 * Listeners may be async. The host awaits each listener before running the
 * next, so registration order — and the cumulative effect on the message — is
 * preserved regardless of whether listeners are sync or async.
 */
export type MessageListenerResultOrPromise = void | MessageListenerResult | Promise<void | MessageListenerResult>;
/**
 * Selects which messages a listener applies to.
 *
 * Either a message/request *maker* — the listener fires for messages with that
 * maker's `code` (the original behavior) — or a custom *predicate* over the
 * message. A maker's `.is` type guard (e.g. `IO.CDK_TOOLKIT_I7010.is`) is a
 * convenient predicate, but any `(msg) => boolean` works (e.g. to match a family
 * of codes, or on the message level).
 */
export type MessageSelector<T> = IoMessageMaker<T> | IoRequestMaker<T, any> | ((msg: IoMessage<any>) => boolean);
/**
 * How an IoHost processed a single message or request.
 *
 * This describes the message *as the host handled it*, which can differ from
 * what was emitted: listeners may rewrite the text or level, prevent it from
 * being written at all, or (for requests) answer it on the user's behalf.
 *
 * Both notifications (`notify`) and requests (`requestResponse`) are reported,
 * so an observer sees the complete, ordered stream the host handled. Use
 * `type` to tell them apart.
 */
export interface IoMessageObservation {
    /**
     * Whether this observation describes a plain notification (`notify`) or a
     * request that asked for a response (`requestResponse`).
     */
    readonly type: 'notify' | 'request';
    /**
     * The message exactly as it was emitted to the host (before any listeners).
     */
    readonly emitted: IoMessage<unknown>;
    /**
     * The message after the host's listeners ran (text and/or level may differ).
     */
    readonly effective: IoMessage<unknown>;
    /**
     * Whether a listener prevented this message from being written, i.e. the user
     * would not see it. Always `false` for requests (a request is reported once
     * it has been resolved, regardless of how it was answered).
     */
    readonly dropped: boolean;
}
/**
 * An IoHost whose message handling can be observed.
 *
 * This is a CLI-internal contract used by tests to record the *effective*,
 * user-facing message stream (after listeners) without reaching into host
 * internals. It is intentionally separate from `IIoHost` so that the recorder
 * can work with any `IIoHost` and only enrich its output when the host also
 * implements this interface.
 */
export interface ObservableIoHost {
    /**
     * Register an observer that is invoked for every message the host handles —
     * both notifications and requests — with the disposition the host computed
     * for it (its effective form after listeners and whether it was dropped). For
     * a request, the resolved answer is the effective message's `defaultResponse`.
     * Returns a function that removes the observer again.
     */
    observeMessages(observer: (observation: IoMessageObservation) => void): () => void;
}
/**
 * A simple IO host for the CLI that writes messages to the console.
 */
export declare class CliIoHost implements IIoHost, ObservableIoHost {
    /**
     * Returns the singleton instance
     */
    static instance(props?: CliIoHostProps, forceNew?: boolean): CliIoHost;
    /**
     * Returns the singleton instance if it exists
     */
    static get(): CliIoHost | undefined;
    /**
     * Singleton instance of the CliIoHost
     */
    private static _instance;
    /**
     * The current action being performed by the CLI.
     */
    currentAction: CliAction;
    /**
     * Whether the CliIoHost is running in CI mode.
     *
     * In CI mode, all non-error output goes to stdout instead of stderr.
     */
    isCI: boolean;
    /**
     * Whether the host can use interactions and message styling.
     */
    isTTY: boolean;
    /**
     * The current threshold.
     *
     * Messages with a lower priority level will be ignored.
     */
    logLevel: IoMessageLevel;
    /**
     * The conditions for requiring approval in this CliIoHost.
     */
    requireDeployApproval: RequireApproval;
    /**
     * Configure the target stream for notices
     *
     * (Not a setter because there's no need for additional logic when this value
     * is changed yet)
     */
    noticesDestination: TargetStream;
    private _progress;
    private activityPrinter?;
    private corkedCounter;
    private readonly corkedLoggingBuffer;
    private readonly messageListeners;
    private readonly messageObservers;
    private corkReplaying;
    private readonly autoRespond;
    /**
     * The telemetry session object
     *
     * Will remain `undefined` if the user has disabled telemetry.
     */
    telemetry?: TelemetrySession;
    private constructor();
    startTelemetry(args: any, context: Context, proxyAgent?: Agent): Promise<void>;
    /**
     * Update the stackProgress preference.
     */
    set stackProgress(type: StackActivityProgress);
    /**
     * Gets the stackProgress value.
     *
     * This takes into account other state of the ioHost,
     * like if isTTY and isCI.
     */
    get stackProgress(): StackActivityProgress;
    get defaults(): IoDefaultMessages;
    asIoHelper(): IoHelper;
    /**
     * Executes a block of code with corked logging. All log messages during execution
     * are buffered and only written when all nested cork blocks complete (when CORK_COUNTER reaches 0).
     * The corking is bound to the specific instance of the CliIoHost.
     *
     * @param block - Async function to execute with corked logging
     * @returns Promise that resolves with the block's return value
     */
    withCorkedLogging<T>(block: () => Promise<T>): Promise<T>;
    /**
     * Register a listener that is invoked for every message with the given code.
     *
     * The listener may return a `MessageListenerResult` to update the message
     * text and/or prevent the default processing (writing it to a stream);
     * returning nothing leaves the message untouched. The listener may be async
     * (return a `Promise`); the host awaits it before processing the message
     * further. Returns a function that removes the listener again.
     *
     * @example
     * const dispose = ioHost.on(IO.CDK_TOOLKIT_I2901, async (msg) => {
     *   myCount += msg.data.stacks.length;
     *   await persist(myCount);
     * });
     *
     * @example
     * // Match with a custom predicate instead of a code, e.g. a maker's `.is`:
     * const dispose = ioHost.on(IO.CDK_TOOLKIT_I7010.is, (msg) => ({ respond: true }));
     */
    on<T>(selector: IoMessageMaker<T> | IoRequestMaker<T, any> | ((msg: IoMessage<any>) => msg is IoMessage<T>), listener: (msg: IoMessage<T>) => MessageListenerResultOrPromise): () => void;
    on(predicate: (msg: IoMessage<any>) => boolean, listener: (msg: IoMessage<unknown>) => MessageListenerResultOrPromise): () => void;
    /**
     * Register an observer that is invoked for every message the host handles —
     * both notifications and requests — with the disposition the host computed
     * for it (its effective form after listeners and whether it was dropped). For
     * a request, the resolved answer is the effective message's `defaultResponse`.
     * Returns a function that removes the observer.
     *
     * @see ObservableIoHost
     */
    observeMessages(observer: (observation: IoMessageObservation) => void): () => void;
    /**
     * Like `on`, but the listener is automatically removed after it has been
     * invoked once.
     */
    once<T>(selector: IoMessageMaker<T> | IoRequestMaker<T, any> | ((msg: IoMessage<any>) => msg is IoMessage<T>), listener: (msg: IoMessage<T>) => MessageListenerResultOrPromise): () => void;
    once(predicate: (msg: IoMessage<any>) => boolean, listener: (msg: IoMessage<unknown>) => MessageListenerResultOrPromise): () => void;
    /**
     * Remove every message listener registered via `on`/`once`/`rewrite`/`respond`.
     *
     * The host's own internal listeners (such as stack-activity routing) are kept,
     * so the host keeps working afterwards. Message observers registered via
     * `observeMessages` are a separate mechanism and are not affected.
     *
     * This is mainly useful for tests that share the singleton host and need to
     * reset listener state between cases (a leftover listener would otherwise
     * leak into the next test).
     */
    removeAllListeners(): void;
    /**
     * Answer a request (by its code) on the user's behalf with a fixed value, so
     * the host does not prompt. Syntactic sugar for an `on` listener returning
     * `{ respond: value, preventDefault: suppressQuestion }`; for conditional
     * answers or to also reword the question, use `on`/`once` directly. Returns a
     * function that removes the responder again.
     *
     * @param suppressQuestion - whether to also suppress writing the question text.
     *   Defaults to `true` (answer silently). Pass `false` to still surface the
     *   question while answering it.
     *
     * @example
     * // Under --force, auto-confirm the destroy prompt without prompting.
     * const dispose = ioHost.respond(IO.CDK_TOOLKIT_I7010, true);
     */
    respond<T, U>(code: IoRequestMaker<T, U>, value: U, suppressQuestion?: boolean): () => void;
    /**
     * Like `respond`, but the answer is given only once and then removed.
     */
    respondOnce<T, U>(code: IoRequestMaker<T, U>, value: U, suppressQuestion?: boolean): () => void;
    /**
     * Register a formatter that replaces the printed text of messages with the
     * given code. This lets a caller define _how_ a toolkit message is presented
     * without the IoHost needing to know about it.
     *
     * Optionally pass a `level` to also override the message's level (which moves
     * it between stdout/stderr and changes verbosity filtering). For the rarer
     * case of overriding only the level, use `on`/`once` returning `{ level }`.
     *
     * Syntactic sugar for an `on` listener that returns `{ message, level? }`.
     * Returns a function that removes the formatter again.
     *
     * @example
     * const dispose = ioHost.rewrite(IO.CDK_TOOLKIT_I2901, (msg) =>
     *   serializeStructure(msg.data.stacks, true));
     */
    rewrite<T>(code: IoMessageMaker<T> | IoRequestMaker<T, any>, formatter: (msg: IoMessage<T>) => string, level?: IoMessageLevel): () => void;
    /**
     * Like `rewrite`, but the formatter is automatically removed after it has
     * been applied once.
     */
    rewriteOnce<T>(code: IoMessageMaker<T> | IoRequestMaker<T, any>, formatter: (msg: IoMessage<T>) => string, level?: IoMessageLevel): () => void;
    /**
     * Add a listener to the registry and return a function that removes it.
     */
    private addMessageListener;
    /**
     * Run every registered listener that matches the message, in registration
     * order. A listener matches by its code (maker) or its custom predicate.
     *
     * A listener may update the message text/level (passed on to subsequent
     * listeners and the rest of the pipeline), prevent the default processing, or
     * (for requests) answer it. `once` listeners are removed after they have run.
     * Matching is decided against the message as emitted, so a rewrite by an
     * earlier listener does not change which later listeners apply.
     *
     * Returns the (possibly updated) message, whether the default processing was
     * prevented, and whether a listener answered the request (and with what).
     */
    private applyMessageListeners;
    /**
     * Notifies the host of a message.
     * The caller waits until the notification completes.
     */
    notify(msg: IoMessage<unknown>): Promise<void>;
    /**
     * Notify every registered message observer of how a message or request was
     * handled. A no-op when nothing is observing (i.e. outside of tests), so the
     * surrounding hot paths pay nothing in production.
     */
    private notifyObservers;
    /**
     * Write a (already listener-processed) message to its target stream, honoring
     * the log level and corked-logging buffer. Shared by `notify` and the
     * non-prompting `requestResponse` path.
     */
    private writeMessage;
    private maybeEmitTelemetry;
    /**
     * Route stack-activity messages to the activity printer (progress bar or
     * event list) rather than writing them to a stream.
     *
     * Implemented as listeners that handle the message via the printer and
     * prevent the default processing, so the rest of the pipeline does not also
     * emit them. The printer is created lazily on the first stack-activity message.
     */
    private routeStackActivityToPrinter;
    /**
     * Detect special messages encode information about whether or not
     * they require approval
     */
    private skipApprovalStep;
    /**
     * Determines the output stream, based on message and configuration.
     */
    private selectStream;
    /**
     * Determines the output stream, based on message level and configuration.
     */
    private selectStreamFromLevel;
    /**
     * Notifies the host of a message that requires a response.
     *
     * Registered listeners run first: a listener may reword the question (its text
     * or level) or answer it outright via `respond` (e.g. `--force` auto-confirms
     * a destroy). If no listener answers and the host cannot prompt, the suggested
     * default response is used.
     */
    requestResponse<DataType, ResponseType>(msg: IoRequest<DataType, ResponseType>): Promise<ResponseType>;
    /**
     * Resolve a request to its response: a listener's answer if one was given,
     * otherwise the answer prompted from the user, otherwise the suggested
     * default when the host cannot prompt.
     *
     * Kept separate from `requestResponse` so the response can be observed in a
     * single place regardless of which of these paths produced it.
     */
    private resolveRequest;
    /**
     * Formats a message for console output with optional color support
     */
    private formatMessage;
    /**
     * Formats date to HH:MM:SS
     */
    private formatTime;
    /**
     * Get an instance of the ActivityPrinter
     */
    private makeActivityPrinter;
}
/**
 * Combine several selectors into a single predicate that matches a message when
 * *any* of them matches. Each selector may be a maker (matched by its `code`)
 * or a predicate.
 *
 * Useful for one listener that spans multiple codes, e.g.
 * `ioHost.on(matchAny(IO.CDK_TOOLKIT_I5501, IO.CDK_TOOLKIT_I5502), listener)`.
 */
export declare function matchAny(...selectors: MessageSelector<any>[]): (msg: IoMessage<unknown>) => boolean;
