import { CreateIfNotExistsMutation, CreateMutation, CreateOrReplaceMutation, DeleteMutation, PatchMutation, Path } from "@sanity/types";
/**
 * Sanity document with a guaranteed `_id` and `_type`
 *
 * @internal
 */
interface Doc {
  _id: string;
  _type: string;
  _rev?: string;
  _updatedAt?: string;
  _createdAt?: string;
  [attribute: string]: unknown;
}
/**
 * Internal mutation body representation - note that theoretically a
 * mutation can only hold one of these operations each, but for sake
 * of simpler code it is bundled together as one here
 *
 * @internal
 */
interface Mut {
  create?: CreateMutation['create'];
  createIfNotExists?: CreateIfNotExistsMutation['createIfNotExists'];
  createOrReplace?: CreateOrReplaceMutation['createOrReplace'];
  delete?: DeleteMutation['delete'];
  patch?: PatchMutation['patch'];
}
/**
 * Parameters attached to the mutation
 *
 * @internal
 */
interface MutationParams {
  transactionId?: string;
  transition?: string;
  identity?: string;
  previousRev?: string;
  resultRev?: string;
  mutations: Mut[];
  timestamp?: string;
  effects?: {
    apply: unknown;
    revert: unknown;
  };
}
/**
 * A mutation describing a number of operations on a single document.
 * This should be considered an immutable structure. Mutations are compiled
 * on first application, and any changes in properties will not effectively
 * change its behavior after that.
 *
 * @internal
 */
declare class Mutation {
  params: MutationParams;
  compiled?: (doc: Doc | null) => Doc | null;
  _appliesToMissingDocument: boolean | undefined;
  constructor(options: MutationParams);
  get transactionId(): string | undefined;
  get transition(): string | undefined;
  get identity(): string | undefined;
  get previousRev(): string | undefined;
  get resultRev(): string | undefined;
  get mutations(): Mut[];
  get timestamp(): Date | undefined;
  get effects(): {
    apply: unknown;
    revert: unknown;
  } | undefined;
  assignRandomTransactionId(): void;
  appliesToMissingDocument(): boolean;
  compile(): void;
  apply(document: Doc | null): Doc | null;
  static applyAll(document: Doc | null, mutations: Mutation[]): Doc | null;
  static squash(document: Doc | null, mutations: Mutation[]): Mutation;
}
/**
 * @internal
 */
interface SubmissionResponder {
  success: () => void;
  failure: () => void;
}
/**
 * Models a document as it is changed by our own local patches and remote patches coming in from
 * the server. Consolidates incoming patches with our own submitted patches and maintains two
 * versions of the document. EDGE is the optimistic document that the user sees that will always
 * immediately reflect whatever she is doing to it, and HEAD which is the confirmed version of the
 * document consistent with the mutations we have received from the server. As long as nothing out of
 * the ordinary happens, we can track all changes by hooking into the onMutation callback, but we
 * must also respect onRebase events that fire when we have to backtrack because one of our optimistically
 * applied patches were rejected, or some bastard was able to slip a mutation in between ours own.
 *
 * @internal
 */
declare class Document {
  /**
   * Incoming patches from the server waiting to be applied to HEAD
   */
  incoming: Mutation[];
  /**
   * Patches we know has been subitted to the server, but has not been seen yet in the return channel
   * so we can't be sure about the ordering yet (someone else might have slipped something between them)
   */
  submitted: Mutation[];
  /**
   * Pending mutations
   */
  pending: Mutation[];
  /**
   * Our model of the document according to the incoming patches from the server
   */
  HEAD: Doc | null;
  /**
   * Our optimistic model of what the document will probably look like as soon as all our patches
   * have been processed. Updated every time we stage a new mutation, but also might revert back
   * to previous states if our mutations fail, or could change if unexpected mutations arrive
   * between our own. The `onRebase` callback will be called when EDGE changes in this manner.
   */
  EDGE: Doc | null;
  /**
   * Called with the EDGE document when that document changes for a reason other than us staging
   * a new patch or receiving a mutation from the server while our EDGE is in sync with HEAD:
   * I.e. when EDGE changes because the order of mutations has changed in relation to our
   * optimistic predictions.
   */
  onRebase?: (edge: Doc | null, incomingMutations: Mutation[], pendingMutations: Mutation[]) => void;
  /**
   * Called when we receive a patch in the normal order of things, but the mutation is not ours
   */
  onMutation?: (msg: {
    mutation: Mutation;
    document: Doc | null;
    remote: boolean;
  }) => void;
  /**
   * Called when consistency state changes with the boolean value of the current consistency state
   */
  onConsistencyChanged?: (isConsistent: boolean) => void;
  /**
   * Called whenever a new incoming mutation comes in. These are always ordered correctly.
   */
  onRemoteMutation?: (mut: Mutation) => void;
  /**
   * We are consistent when there are no unresolved mutations of our own, and no un-applicable
   * incoming mutations. When this has been going on for too long, and there has been a while
   * since we staged a new mutation, it is time to reset your state.
   */
  inconsistentAt: Date | null;
  /**
   * The last time we staged a patch of our own. If we have been inconsistent for a while, but it
   * hasn't been long since we staged a new mutation, the reason is probably just because the user
   * is typing or something.
   *
   * Should be used as a guard against resetting state for inconsistency reasons.
   */
  lastStagedAt: Date | null;
  constructor(doc: Doc | null);
  reset(doc: Doc | null): void;
  arrive(mutation: Mutation): void;
  stage(mutation: Mutation, silent?: boolean): SubmissionResponder;
  isConsistent(): boolean;
  considerIncoming(): void;
  updateConsistencyFlag(): void;
  applyIncoming(mut: Mutation | undefined): boolean;
  /**
   * Returns true if there are unresolved mutations between HEAD and EDGE, meaning we have
   * mutations that are still waiting to be either submitted, or to be confirmed by the server.
   *
   * @returns true if there are unresolved mutations between HEAD and EDGE, false otherwise
   */
  hasUnresolvedMutations(): boolean;
  /**
   * When an incoming mutation is applied to HEAD, this is called to remove the mutation from
   * the unresolved state. If the newly applied patch is the next upcoming unresolved mutation,
   * no rebase is needed, but we might have the wrong idea about the ordering of mutations, so in
   * that case we are given the flag `needRebase` to tell us that this mutation arrived out of
   * order in terms of our optimistic version, so a rebase is needed.
   *
   * @param txnId - Transaction ID of the remote mutation
   * @returns true if rebase is needed, false otherwise
   */
  consumeUnresolved(txnId: string): boolean;
  pendingSuccessfullySubmitted(pendingTxnId: string): void;
  pendingFailed(pendingTxnId: string): void;
  rebase(incomingMutations: Mutation[]): void;
}
/**
 * Implements a buffer for mutations that incrementally optimises the mutations by
 * eliminating set-operations that overwrite earlier set-operations, and rewrite
 * set-operations that change strings into other strings into diffMatchPatch operations.
 *
 * @internal
 */
declare class SquashingBuffer {
  /**
   * The document forming the basis of this squash
   */
  BASIS: Doc | null;
  /**
   * The document after the out-Mutation has been applied, but before the staged
   * operations are committed.
   */
  PRESTAGE: Doc | null;
  /**
   * setOperations contain the latest set operation by path. If the set-operations are
   * updating strings to new strings, they are rewritten as diffMatchPatch operations,
   * any new set operations on the same paths overwrites any older set operations.
   * Only set-operations assigning plain values to plain values gets optimized like this.
   */
  setOperations: Record<string, Mut | undefined>;
  /**
   * `documentPresent` is true whenever we know that the document must be present due
   * to preceeding mutations. `false` implies that it may or may not already exist.
   */
  documentPresent: boolean;
  /**
   * The operations in the out-Mutation are not able to be optimized any further
   */
  out: Mut[];
  /**
   * Staged mutation operations
   */
  staged: Mut[];
  constructor(doc: Doc | null);
  add(mut: Mutation): void;
  hasChanges(): boolean;
  /**
   * Extracts the mutations in this buffer.
   * After this is done, the buffer lifecycle is over and the client should
   * create an new one with the new, updated BASIS.
   *
   * @param txnId - Transaction ID
   * @returns A `Mutation` instance if we had outgoing mutations pending, null otherwise
   */
  purge(txnId?: string): Mutation | null;
  addOperation(op: Mut): void;
  /**
   * Attempt to perform one single set operation in an optimised manner, return value
   * reflects whether or not the operation could be performed.
   * @param path - The JSONPath to the set operation in question
   * @param nextValue - The value to be set
   * @returns True of optimized, false otherwise
   */
  optimiseSetOperation(path: string, nextValue: unknown): boolean;
  stashStagedOperations(): void;
  /**
   * Rebases given the new base-document
   *
   * @param newBasis - New base document to rebase on
   * @returns New "edge" document with buffered changes integrated
   */
  rebase(newBasis: Doc | null): Doc | null;
}
/**
 * @internal
 */
interface CommitHandlerMessage {
  mutation: Mutation;
  success: () => void;
  failure: () => void;
  cancel: (error: Error) => void;
}
/**
 * @internal
 */
declare class BufferedDocument {
  private mutations;
  /**
   * The Document we are wrapping
   */
  document: Document;
  /**
   * The Document with local changes applied
   */
  LOCAL: Doc | null;
  /**
   * Commits that are waiting to be delivered to the server
   */
  private commits;
  /**
   * Local mutations that are not scheduled to be committed yet
   */
  buffer: SquashingBuffer;
  /**
   * Assignable event handler for when the buffered document applies a mutation
   */
  onMutation?: (message: {
    mutation: Mutation;
    document: Doc | null;
    remote: boolean;
  }) => void;
  /**
   * Assignable event handler for when a remote mutation happened
   */
  onRemoteMutation?: Document['onRemoteMutation'];
  /**
   * Assignable event handler for when the buffered document rebased
   */
  onRebase?: (localDoc: Doc | null, remoteMutations: Mut[], localMutations: Mut[]) => void;
  /**
   * Assignable event handler for when the document is deleted
   */
  onDelete?: (doc: Doc | null) => void;
  /**
   * Assignable event handler for when the state of consistency changed
   */
  onConsistencyChanged?: (isConsistent: boolean) => void;
  /**
   * Assignable event handler for when the buffered document should commit changes
   */
  commitHandler?: (msg: CommitHandlerMessage) => void;
  /**
   * Whether or not we are currently commiting
   */
  committerRunning: boolean;
  constructor(doc: Doc | null);
  reset(doc: Doc | null): void;
  add(mutation: Mutation): void;
  arrive(mutation: Mutation): void;
  commit(): Promise<void>;
  performCommits(): void;
  _cycleCommitter(): void;
  handleDocRebase(edge: Doc | null, remoteMutations: Mutation[], localMutations: Mutation[]): void;
  handleDocumentDeleted(): void;
  handleDocMutation(msg: {
    mutation: Mutation;
    document: Doc | null;
    remote: boolean;
  }): void;
  rebase(remoteMutations: Mutation[], localMutations: Mutation[]): void;
  handleDocConsistencyChanged(isConsistent: boolean): void;
}
/**
 * Converts a path in array form to a JSONPath string
 *
 * @param pathArray - Array of path segments
 * @returns String representation of the path
 * @internal
 */
declare function arrayToJSONMatchPath(pathArray: Path): string;
/**
 * Extracts values matching the given JsonPath
 *
 * @param path - Path to extract
 * @param value - Value to extract from
 * @returns An array of values matching the given path
 * @public
 */
declare function extract(path: string, value: unknown): unknown[];
/**
 * Extracts a value for the given JsonPath, and includes the specific path of where it was found
 *
 * @param path - Path to extract
 * @param value - Value to extract from
 * @returns An array of objects with `path` and `value` keys
 * @internal
 */
declare function extractWithPath(path: string, value: unknown): {
  path: (string | number)[];
  value: unknown;
}[];
export { BufferedDocument, type CommitHandlerMessage, type Doc, type Document, type Mut, Mutation, type MutationParams, type SquashingBuffer, type SubmissionResponder, arrayToJSONMatchPath, extract, extractWithPath };