import * as util from 'util';
import type { FileSystemLocation } from '../io';
import type { UsesAbilities } from './abilities';
import type { Answerable } from './Answerable';
import { Interaction } from './Interaction';
import type { Optional } from './Optional';
import type { AnswersQuestions } from './questions/AnswersQuestions';
import { Describable } from './questions/Describable';
import type { DescriptionFormattingOptions } from './questions/DescriptionFormattingOptions';
import type { MetaQuestion } from './questions/MetaQuestion';
import type { RecursivelyAnswered } from './RecursivelyAnswered';
import type { WithAnswerableProperties } from './WithAnswerableProperties';
/**
 * **Questions** describe how [actors](https://serenity-js.org/api/core/class/Actor/) should query the system under test or the test environment to retrieve some information.
 *
 * Questions are the core building block of the [Screenplay Pattern](https://serenity-js.org/handbook/design/screenplay-pattern),
 * along with [actors](https://serenity-js.org/api/core/class/Actor/), [abilities](https://serenity-js.org/api/core/class/Ability/),
 * [interactions](https://serenity-js.org/api/core/class/Interaction/),
 * and [tasks](https://serenity-js.org/api/core/class/Task/).
 *
 * ![Screenplay Pattern](https://serenity-js.org/images/design/serenity-js-screenplay-pattern.png)
 *
 * Learn more about:
 * - [`Actor`](https://serenity-js.org/api/core/class/Actor/)
 * - [`Ability`](https://serenity-js.org/api/core/class/Ability/)
 * - [`Interaction`](https://serenity-js.org/api/core/class/Interaction/)
 * - [`QuestionAdapter`](https://serenity-js.org/api/core/#QuestionAdapter)
 *
 * ## Implementing a basic custom Question
 *
 * ```ts
 *  import { actorCalled, AnswersQuestions, UsesAbilities, Question } from '@serenity-js/core'
 *  import { Ensure, equals } from '@serenity-js/assertions'
 *
 *  const LastItemOf = <T>(list: T[]): Question<T> =>
 *    Question.about('last item from the list', (actor: AnswersQuestions & UsesAbilities) => {
 *      return list[list.length - 1]
 *    });
 *
 *  await actorCalled('Quentin').attemptsTo(
 *    Ensure.that(LastItemFrom([1,2,3]), equals(3)),
 *  )
 * ```
 *
 * ## Implementing a Question that uses an Ability
 *
 * Just like the [interactions](https://serenity-js.org/api/core/class/Interaction/), a [`Question`](https://serenity-js.org/api/core/class/Question/)
 * also can use [actor's](https://serenity-js.org/api/core/class/Actor/) [abilities](https://serenity-js.org/api/core/class/Ability/).
 *
 * Here, we use the ability to [`CallAnApi`](https://serenity-js.org/api/rest/class/CallAnApi/) to retrieve a property of
 * an HTTP response.
 *
 * ```ts
 *  import { AnswersQuestions, UsesAbilities, Question } from '@serenity-js/core'
 *  import { CallAnApi } from '@serenity-js/rest'
 *
 *  const TextOfLastResponseStatus = () =>
 *    Question.about<number>(`the text of the last response status`, actor => {
 *      return CallAnApi.as(actor).mapLastResponse(response => response.statusText)
 *    })
 * ```
 *
 * #### Learn more
 * - [`CallAnApi`](https://serenity-js.org/api/rest/class/CallAnApi/)
 * - [`LastResponse`](https://serenity-js.org/api/rest/class/LastResponse/)
 *
 * ## Mapping answers to other questions
 *
 * Apart from retrieving information, [questions](https://serenity-js.org/api/core/class/Question/) can be used to transform information retrieved by other questions.
 *
 * Here, we use the factory method [`Question.about`](https://serenity-js.org/api/core/class/Question/#about) to produce a question that makes the received [actor](https://serenity-js.org/api/core/class/Actor/)
 * answer [`LastResponse.status`](https://serenity-js.org/api/rest/class/LastResponse/#status) and then compare it against some expected value.
 *
 * ```ts
 * import { actorCalled, AnswersQuestions, UsesAbilities, Question } from '@serenity-js/core'
 * import { CallAnApi, LastResponse } from '@serenity-js/rest'
 * import { Ensure, equals } from '@serenity-js/assertions'
 *
 * const RequestWasSuccessful = () =>
 *   Question.about<number>(`the text of the last response status`, async actor => {
 *     const status = await actor.answer(LastResponse.status());
 *
 *     return status === 200;
 *   })
 *
 * await actorCalled('Quentin')
 *   .whoCan(CallAnApi.at('https://api.example.org/'));
 *   .attemptsTo(
 *     Send.a(GetRequest.to('/books/0-688-00230-7')),
 *     Ensure.that(RequestWasSuccessful(), isTrue()),
 *   )
 * ```
 *
 * Note that the above example is for demonstration purposes only, Serenity/JS provides an easier way to
 * verify the response status of the [`LastResponse`](https://serenity-js.org/api/rest/class/LastResponse/):
 *
 * ```ts
 * import { actorCalled } from '@serenity-js/core'
 * import { CallAnApi, LastResponse } from '@serenity-js/rest'
 * import { Ensure, equals } from '@serenity-js/assertions'
 *
 * await actorCalled('Quentin')
 *   .whoCan(CallAnApi.at('https://api.example.org/'));
 *   .attemptsTo(
 *     Send.a(GetRequest.to('/books/0-688-00230-7')),
 *     Ensure.that(LastResponse.status(), equals(200)),
 *   )
 * ```
 *
 * @group Screenplay Pattern
 */
export declare abstract class Question<T> extends Describable {
    /**
     * Factory method that simplifies the process of defining custom questions.
     *
     * #### Defining a custom question
     *
     * ```ts
     * import { Question } from '@serenity-js/core'
     *
     * const EnvVariable = (name: string) =>
     *   Question.about(`the ${ name } env variable`, actor => process.env[name])
     * ```
     *
     * @param description
     * @param body
     * @param [metaQuestionBody]
     */
    static about<Answer_Type, Supported_Context_Type>(description: Answerable<string>, body: (actor: AnswersQuestions & UsesAbilities) => Promise<Answer_Type> | Answer_Type, metaQuestionBody: (answerable: Answerable<Supported_Context_Type>) => Question<Promise<Answer_Type>> | Question<Answer_Type>): MetaQuestionAdapter<Supported_Context_Type, Awaited<Answer_Type>>;
    static about<Answer_Type>(description: Answerable<string>, body: (actor: AnswersQuestions & UsesAbilities) => Promise<Answer_Type> | Answer_Type): QuestionAdapter<Awaited<Answer_Type>>;
    /**
     * Generates a [`QuestionAdapter`](https://serenity-js.org/api/core/#QuestionAdapter) that recursively resolves
     * any [`Answerable`](https://serenity-js.org/api/core/#Answerable) fields of the provided object,
     * including [`Answerable`](https://serenity-js.org/api/core/#Answerable) fields
     * of [nested objects](https://serenity-js.org/api/core/#WithAnswerableProperties).
     *
     * Optionally, the method accepts `overrides` to be shallow-merged with the fields of the original `source`,
     * producing a new merged object.
     *
     * Overrides are applied from left to right, with subsequent objects overwriting property assignments of the previous ones.
     *
     * #### Resolving an object recursively using `Question.fromObject`
     *
     * ```ts
     * import { actorCalled, Question } from '@serenity-js/core'
     * import { Send, PostRequest } from '@serenity-js/rest'
     * import { By, Text, PageElement } from '@serenity-js/web'
     *
     * await actorCalled('Daisy')
     *   .whoCan(CallAnApi.at('https://api.example.org'))
     *   .attemptsTo(
     *     Send.a(
     *       PostRequest.to('/products/2')
     *         .with(
     *           Question.fromObject({
     *             name: Text.of(PageElement.located(By.css('.name'))),
     *           })
     *         )
     *       )
     *   );
     * ```
     *
     * #### Merging objects using `Question.fromObject`
     *
     * ```ts
     *  import { actorCalled, Question } from '@serenity-js/core'
     *  import { Send, PostRequest } from '@serenity-js/rest'
     *  import { By, Text, PageElement } from '@serenity-js/web'
     *
     *  await actorCalled('Daisy')
     *    .whoCan(CallAnApi.at('https://api.example.org'))
     *    .attemptsTo(
     *      Send.a(
     *        PostRequest.to('/products/2')
     *          .with(
     *            Question.fromObject({
     *              name: Text.of(PageElement.located(By.css('.name'))),
     *              quantity: undefined,
     *            }, {
     *              quantity: 2,
     *            })
     *          )
     *        )
     *    );
     * ```
     *
     * #### Learn more
     * - [`WithAnswerableProperties`](https://serenity-js.org/api/core/#WithAnswerableProperties)
     * - [`RecursivelyAnswered`](https://serenity-js.org/api/core/#RecursivelyAnswered)
     * - [`Answerable`](https://serenity-js.org/api/core/#Answerable)
     *
     * @param source
     * @param overrides
     */
    static fromObject<Source_Type extends object>(source: Answerable<WithAnswerableProperties<Source_Type>>, ...overrides: Array<Answerable<Partial<WithAnswerableProperties<Source_Type>>>>): QuestionAdapter<RecursivelyAnswered<Source_Type>>;
    /**
     * Generates a [`QuestionAdapter`](https://serenity-js.org/api/core/#QuestionAdapter) that resolves
     * any [`Answerable`](https://serenity-js.org/api/core/#Answerable) elements of the provided array.
     */
    static fromArray<Source_Type>(source: Array<Answerable<Source_Type>>, options?: DescriptionFormattingOptions): QuestionAdapter<Source_Type[]>;
    /**
     * Checks if the value is a [`Question`](https://serenity-js.org/api/core/class/Question/).
     *
     * @param maybeQuestion
     *  The value to check
     */
    static isAQuestion<T>(maybeQuestion: unknown): maybeQuestion is Question<T>;
    /**
     * Checks if the value is a [`MetaQuestion`](https://serenity-js.org/api/core/interface/MetaQuestion/).
     *
     * @param maybeMetaQuestion
     *  The value to check
     */
    static isAMetaQuestion<CT, RQT extends Question<unknown>>(maybeMetaQuestion: unknown): maybeMetaQuestion is MetaQuestion<CT, RQT>;
    /**
     * Creates a [`MetaQuestion`](https://serenity-js.org/api/core/interface/MetaQuestion/) that can be composed with any [`Answerable`](https://serenity-js.org/api/core/#Answerable)
     * to produce a single-line description of its value.
     *
     * ```ts
     * import { actorCalled, Question } from '@serenity-js/core'
     * import { Ensure, equals } from '@serenity-js/assertions'
     *
     * const accountDetails = () =>
     *   Question.about('account details', actor => ({ name: 'Alice', age: 28 }))
     *
     * await actorCalled('Alice').attemptsTo(
     *   Ensure.that(
     *     Question.formattedValue().of(accountDetails()),
     *     equals('{ name: "Alice", age: 28 }'),
     *   ),
     * )
     * ```
     *
     * @param options
     */
    static formattedValue(options?: DescriptionFormattingOptions): MetaQuestion<any, Question<Promise<string>>>;
    /**
     * Creates a [`MetaQuestion`](https://serenity-js.org/api/core/interface/MetaQuestion/) that can be composed with any [`Answerable`](https://serenity-js.org/api/core/#Answerable)
     * to return its value when the answerable is a [`Question`](https://serenity-js.org/api/core/class/Question/),
     * or the answerable itself otherwise.
     *
     * The description of the resulting question is produced by calling [`Question.describedBy`](https://serenity-js.org/api/core/class/Question/#describedBy) on the
     * provided answerable.
     *
     * ```ts
     * import { actorCalled, Question } from '@serenity-js/core'
     * import { Ensure, equals } from '@serenity-js/assertions'
     *
     * const accountDetails = () =>
     *   Question.about('account details', actor => ({ name: 'Alice', age: 28 }))
     *
     * await actorCalled('Alice').attemptsTo(
     *   Ensure.that(
     *     Question.description().of(accountDetails()),
     *     equals('account details'),
     *   ),
     *   Ensure.that(
     *     Question.value().of(accountDetails()),
     *     equals({ name: 'Alice', age: 28 }),
     *   ),
     * )
     * ```
     */
    static value<Answer_Type>(): MetaQuestion<Answer_Type, Question<Promise<Answer_Type>>>;
    protected static createAdapter<AT>(statement: Question<AT>): QuestionAdapter<Awaited<AT>>;
    private static staticFieldDescription;
    private static methodDescription;
    /**
     * Instructs the provided [`Actor`](https://serenity-js.org/api/core/class/Actor/) to use their [abilities](https://serenity-js.org/api/core/class/Ability/)
     * to answer this question.
     */
    abstract answeredBy(actor: AnswersQuestions & UsesAbilities): T;
    /**
     * Changes the description of this object, as returned by [`Describable.describedBy`](https://serenity-js.org/api/core/class/Describable/#describedBy)
     * and [`Describable.toString`](https://serenity-js.org/api/core/class/Describable/#toString).
     *
     * @param description
     *  Replaces the current description according to the following rules:
     *  - If `description` is an [`Answerable`](https://serenity-js.org/api/core/#Answerable), it replaces the current description
     *  - If `description` is a [`MetaQuestion`](https://serenity-js.org/api/core/interface/MetaQuestion/), the current description is passed as `context` to `description.of(context)`, and the result replaces the current description
     */
    describedAs(description: Answerable<string> | MetaQuestion<Awaited<T>, Question<Promise<string>>>): this;
    /**
     * Maps this question to one of a different type.
     *
     * ```ts
     * Question.about('number returned as string', actor => '42')   // returns: QuestionAdapter<string>
     *   .as(Number)                                                // returns: QuestionAdapter<number>
     * ```
     *
     * @param mapping
     */
    as<O>(mapping: (answer: Awaited<T>) => Promise<O> | O): QuestionAdapter<O>;
}
declare global {
    interface ProxyConstructor {
        new <Source_Type extends object, Target_Type extends object>(target: Source_Type, handler: ProxyHandler<Source_Type>): Target_Type;
    }
}
/**
 * Describes an object recursively wrapped in [`QuestionAdapter`](https://serenity-js.org/api/core/#QuestionAdapter) proxies, so that:
 * - both methods and fields of the wrapped object can be used as [questions](https://serenity-js.org/api/core/class/Question/) or [interactions](https://serenity-js.org/api/core/class/Interaction/)
 * - method parameters of the wrapped object will accept [`Answerable<T>`](https://serenity-js.org/api/core/#Answerable)
 *
 * @group Questions
 */
export type QuestionAdapterFieldDecorator<Original_Type> = {
    [Field in keyof Omit<Original_Type, keyof QuestionStatement<Original_Type>>]: Original_Type[Field] extends (...args: infer OriginalParameters) => infer OriginalMethodResult ? Field extends 'replace' | 'replaceAll' ? (searchValue: Answerable<string | RegExp>, replaceValue: Answerable<string>) => QuestionAdapter<string> : (...args: {
        [P in keyof OriginalParameters]: Answerable<Awaited<OriginalParameters[P]>>;
    }) => QuestionAdapter<Awaited<OriginalMethodResult>> : Original_Type[Field] extends number | bigint | boolean | string | symbol | object ? QuestionAdapter<Awaited<Original_Type[Field]>> : any;
};
/**
 * A union type representing a proxy object returned by [`Question.about`](https://serenity-js.org/api/core/class/Question/#about).
 *
 * [`QuestionAdapter`](https://serenity-js.org/api/core/#QuestionAdapter) proxies the methods and fields of the wrapped object recursively,
 * allowing them to be used as either a [`Question`](https://serenity-js.org/api/core/class/Question/) or an [`Interaction`](https://serenity-js.org/api/core/class/Interaction/).
 *
 * @group Questions
 */
export type QuestionAdapter<Answer_Type> = Question<Promise<Answer_Type>> & Interaction & {
    isPresent(): Question<Promise<boolean>>;
} & QuestionAdapterFieldDecorator<Answer_Type>;
/**
 * An extension of [`QuestionAdapter`](https://serenity-js.org/api/core/#QuestionAdapter), that in addition to proxying methods and fields
 * of the wrapped object can also act as a [`MetaQuestion`](https://serenity-js.org/api/core/interface/MetaQuestion/).
 *
 * @group Questions
 */
export type MetaQuestionAdapter<Context_Type, Answer_Type> = QuestionAdapter<Answer_Type> & MetaQuestion<Context_Type, QuestionAdapter<Answer_Type>>;
/**
 * @package
 */
declare class QuestionStatement<Answer_Type> extends Interaction implements Question<Promise<Answer_Type>>, Optional {
    private readonly body;
    private answer;
    constructor(subject: Answerable<string>, body: (actor: AnswersQuestions & UsesAbilities, ...Parameters: any[]) => Promise<Answer_Type> | Answer_Type, location?: FileSystemLocation);
    /**
     * Returns a Question that resolves to `true` if resolving the `QuestionStatement`
     * returns a value other than `null` or `undefined`, and doesn't throw errors.
     */
    isPresent(): Question<Promise<boolean>>;
    answeredBy(actor: AnswersQuestions & UsesAbilities): Promise<Answer_Type>;
    performAs(actor: UsesAbilities & AnswersQuestions): Promise<void>;
    [util.inspect.custom](depth: number, options: util.InspectOptionsStylized, inspect: typeof util.inspect): string;
    describedAs(description: Answerable<string> | MetaQuestion<Answer_Type, Question<Promise<string>>>): this;
    as<O>(mapping: (answer: Awaited<Answer_Type>) => (Promise<O> | O)): QuestionAdapter<O>;
}
export {};
//# sourceMappingURL=Question.d.ts.map