import { AsyncGenerator, Generator } from "@fluid-private/stochastic-test-utils";
import { Client, DDSFuzzTestState } from "@fluid-private/test-dds-utils";
import { FlexTreeView, SharedTreeFactory, TreeContent, ITreeViewFork, SharedTree, ISharedTree } from "../../../shared-tree/index.js";
import { FieldKey, FieldUpPath } from "../../../core/index.js";
import { Operation, TransactionBoundary, TreeEdit, UndoRedo } from "./operationTypes.js";
import { FuzzNode, FuzzNodeSchema, fuzzSchema } from "./fuzzUtils.js";
export type FuzzView = FlexTreeView<typeof fuzzSchema.rootFieldSchema> & {
    /**
     * This client's current stored schema, which dictates allowable edits that the client may perform.
     * @remarks - The type of this field isn't totally correct, since the supported schema for fuzz nodes changes
     * at runtime to support different primitives (this allows fuzz testing of schema changes).
     * However, fuzz schemas always have the same field names, so schema-dependent
     * APIs such as the tree reading API will work correctly anyway.
     *
     * TODO: The schema for each client should be properly updated if "afterSchemaChange" (or equivalent event) occurs
     * once schema ops are supported.
     */
    currentSchema: FuzzNodeSchema;
};
export type FuzzTransactionView = ITreeViewFork<typeof fuzzSchema.rootFieldSchema> & {
    /**
     * This client's current stored schema, which dictates allowable edits that the client may perform.
     * @remarks - The type of this field isn't totally correct, since the supported schema for fuzz nodes changes
     * at runtime to support different primitives (this allows fuzz testing of schema changes).
     * However, fuzz schemas always have the same field names, so schema-dependent
     * APIs such as the tree reading API will work correctly anyway.
     *
     * TODO: The schema for each client should be properly updated if "afterSchemaChange" (or equivalent event) occurs
     * once schema ops are supported.
     */
    currentSchema: FuzzNodeSchema;
};
export interface FuzzTestState extends DDSFuzzTestState<SharedTreeFactory> {
    /**
     * Schematized view of clients and their nodeSchemas. Created lazily by viewFromState.
     *
     * SharedTrees undergoing a transaction will have a forked view in {@link transactionViews} instead,
     * which should be used in place of this view until the transaction is complete.
     */
    view?: Map<SharedTree, FuzzView>;
    /**
     * Schematized view of clients undergoing transactions with their nodeSchemas.
     * Edits to this view are not visible to other clients until the transaction is closed.
     *
     * Maintaining a separate view here is necessary since async transactions are not supported on the root checkout,
     * and the fuzz testing model only simulates async transactions.
     */
    transactionViews?: Map<ISharedTree, FuzzTransactionView>;
}
export declare function viewFromState(state: FuzzTestState, client?: Client<SharedTreeFactory>, initialTree?: TreeContent<typeof fuzzSchema.rootFieldSchema>["initialTree"]): FuzzView;
/**
 * When performing an operation, a random field must be selected. Rather than enumerate all fields of the tree, this is
 * performed recursively starting at the root field.
 *
 * When a field needs to be selected, the fuzz test generator walks down the tree from the root field, randomly selecting
 * one of the below options. If the selected option is not valid (e.g. the field is empty and the generator is trying to
 * select a node to remove), field selection will automatically re-sample excluding this option.
 *
 * Each weight is used throughout the field selection process.
 *
 * @remarks
 * Allowing more than just numbers here could be interesting. E.g. `(depth: number) => number` to allow biasing away from
 * changing the root field for more interesting merge outcomes. The `filter` parameter can already accomplish this, but is
 * a bit less efficient in doing so (it'd need to walk to the root repeatedly to compute depth)
 */
export interface FieldSelectionWeights {
    /**
     * Select the current Fuzz node's "optionalChild" field
     */
    optional: number;
    /**
     * Select the current Fuzz node's "requiredChild" field
     */
    required: number;
    /**
     * Select the current Fuzz node's "sequenceChild" field
     */
    sequence: number;
    /**
     * Select a direct child of the current Fuzz node, uniformly at random
     */
    recurse: number;
    /**
     * Whether the selected field is acceptable for use.
     *
     * @remarks - This can be helpful for restricting tests to only use certain types of fields
     */
    filter?: FieldFilter;
}
export interface EditGeneratorOpWeights {
    insert: number;
    remove: number;
    start: number;
    commit: number;
    abort: number;
    undo: number;
    redo: number;
    move: number;
    fieldSelection: FieldSelectionWeights;
    synchronizeTrees: number;
}
export interface EditGeneratorOptions {
    weights: Partial<EditGeneratorOpWeights>;
    maxRemoveCount: number;
}
export declare const makeEditGenerator: (opWeightsArg: Partial<EditGeneratorOpWeights>) => Generator<TreeEdit, FuzzTestState>;
export declare const makeTransactionEditGenerator: (opWeights: Partial<EditGeneratorOpWeights>) => Generator<TransactionBoundary, FuzzTestState>;
export declare const makeUndoRedoEditGenerator: (opWeights: Partial<EditGeneratorOpWeights>) => Generator<UndoRedo, FuzzTestState>;
export declare function makeOpGenerator(weightsArg?: Partial<EditGeneratorOpWeights>): AsyncGenerator<Operation, DDSFuzzTestState<SharedTreeFactory>>;
export interface FieldPathWithCount {
    fieldPath: FieldUpPath;
    fieldKey: FieldKey;
    count: number;
}
type FuzzField = {
    type: "optional";
    content: FuzzNode["boxedOptionalChild"];
} | {
    type: "sequence";
    content: FuzzNode["boxedSequenceChildren"];
} | {
    type: "required";
    content: FuzzNode["boxedRequiredChild"];
};
type FieldFilter = (field: FuzzField) => boolean;
export {};
//# sourceMappingURL=fuzzEditGenerators.d.ts.map