/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow strict
 */
import type {
  EditorState,
  LexicalEditor,
  LexicalNode,
  ElementNode,
  NodeCaret,
  CaretDirection,
  CaretRange,
  RangeSelection,
  SiblingCaret,
  RootMode,
  SplitAtPointCaretNextOptions,
  PointCaret,
  StateConfig,
  ValueOrUpdater
} from 'lexical';
declare export function $isBlockFullySelected(
  blockNode: ElementNode,
  selectionOrRange: RangeSelection | CaretRange<CaretDirection>,
): boolean;
declare export function addClassNamesToElement(
  element: HTMLElement,
  ...classNames: (typeof undefined | boolean | null | string)[]
): void;
declare export function removeClassNamesFromElement(
  element: HTMLElement,
  ...classNames: (typeof undefined | boolean | null | string)[]
): void;
declare export function isMimeType(
  file: File,
  acceptableMimeTypes: string[],
): boolean;
declare export function mediaFileReader(
  files: File[],
  acceptableMimeTypes: string[],
): Promise<Readonly<{file: File, result: string}>[]>;
export type DFSNode = Readonly<{
  depth: number,
  node: LexicalNode,
}>;
declare export function $dfs(
  startNode?: LexicalNode,
  endNode?: LexicalNode,
): DFSNode[];
type DFSIterator = {
  next: () => IteratorResult<DFSNode, void>;
  @@iterator: () => DFSIterator;
};
declare export function $dfsIterator(
  startNode?: LexicalNode,
  endNode?: LexicalNode,
): DFSIterator;
declare export function $dfsWithSlots(
  startNode?: LexicalNode,
  endNode?: LexicalNode,
): DFSNode[];
declare export function $dfsWithSlotsIterator(
  startNode?: LexicalNode,
  endNode?: LexicalNode,
): DFSIterator;
declare export function $getNextSiblingOrParentSibling(
  node: LexicalNode,
): null | [LexicalNode, number];
declare export function $getDepth(node: null | LexicalNode): number;
declare export function $getNextRightPreorderNode(
  startingNode: LexicalNode,
): LexicalNode | null;
declare export function $getNearestNodeOfType<T extends LexicalNode>(
  node: LexicalNode,
  klass: Class<T>,
): T | null;
export type DOMNodeToLexicalConversion = (element: Node) => LexicalNode;
export type DOMNodeToLexicalConversionMap = {
  [string]: DOMNodeToLexicalConversion,
};
declare export function $findMatchingParent(
  startingNode: LexicalNode,
  findFn: (LexicalNode) => boolean,
): LexicalNode | null;
declare export function mergeRegister(...func: (() => void)[]): () => void;
declare export function dedupeSelectionRects(
  rects: Iterable<ClientRect>,
): ClientRect[];
declare export function markSelection(
  editor: LexicalEditor,
  onReposition?: (node: HTMLElement[]) => void,
): () => void;
declare export function positionNodeOnRange(
  editor: LexicalEditor,
  range: Range,
  onReposition: (node: HTMLElement[]) => void,
): () => void;
declare export function selectionAlwaysOnDisplay(
  editor: LexicalEditor,
  onReposition?: (node: HTMLElement[]) => void,
): () => void;
declare export function $getNearestBlockElementAncestorOrThrow(
  startNode: LexicalNode,
): ElementNode;

declare export function registerNestedElementResolver<N extends ElementNode>(
  editor: LexicalEditor,
  targetNode: Class<N>,
  cloneNode: (from: N) => N,
  handleOverlap: (from: N, to: N) => void,
): () => void;

declare export function unstable_convertLegacyJSONEditorState(
  editor: LexicalEditor,
  maybeStringifiedEditorState: string,
): EditorState;

declare export function $restoreEditorState(
  editor: LexicalEditor,
  editorState: EditorState,
): void;


declare export function $insertNodeToNearestRoot<T extends LexicalNode>(node: T): T;
declare export function $insertNodeToNearestRootAtCaret<T extends LexicalNode>(node: T, caret: PointCaret<CaretDirection>, options?: SplitAtPointCaretNextOptions): T;

declare export function $insertNodeIntoLeaf(node: LexicalNode): void

declare export function $wrapNodeInElement(
  node: LexicalNode,
  createElementNode: () => ElementNode,
): ElementNode;

declare export function objectKlassEquals<T>(
  object: unknown,
  objectClass: Class<T>,
): boolean;

declare export function $filter<T>(
  nodes: LexicalNode[],
  filterFn: (node: LexicalNode) => null | T,
): T[];

declare export function $handleIndentAndOutdent(
  indentOrOutdent: (block: ElementNode) => void,
): boolean;

declare export function $insertFirst(parent: ElementNode, node: LexicalNode): void;

declare export function $splitNode(
  node: ElementNode,
  offset: number,
): [ElementNode | null, ElementNode];

declare export function calculateZoomLevel(element: Element | null, useManualZoom?: boolean): number;

declare export function $isEditorIsNestedEditor(editor: LexicalEditor): boolean;

declare export function $unwrapAndFilterDescendants(
  root: ElementNode,
  $predicate: (node: LexicalNode) => boolean,
): boolean;

declare export function $firstToLastIterator(node: ElementNode): Iterable<LexicalNode>;

declare export function $lastToFirstIterator(node: ElementNode): Iterable<LexicalNode>;

declare export function $unwrapNode(node: ElementNode): void;

declare export function $getAdjacentCaret<D extends CaretDirection>(
  caret: null | NodeCaret<D>,
): null | SiblingCaret<LexicalNode, D>;

declare export function $reverseDfs(
  startNode?: LexicalNode,
  endNode?: LexicalNode,
): DFSNode[];

declare export function $reverseDfsIterator(
  startNode?: LexicalNode,
  endNode?: LexicalNode,
): Iterable<DFSNode>;

declare export function $reverseDfsWithSlots(
  startNode?: LexicalNode,
  endNode?: LexicalNode,
): DFSNode[];

declare export function $reverseDfsWithSlotsIterator(
  startNode?: LexicalNode,
  endNode?: LexicalNode,
): DFSIterator;

declare export function $descendantsMatching<T extends LexicalNode>(
  children: LexicalNode[],
  $predicate: (node: LexicalNode) => node is T,
): T[];
declare export function $descendantsMatching(
  children: LexicalNode[],
  $predicate: (node: LexicalNode) => boolean,
): LexicalNode[];

declare export function $getAdjacentSiblingOrParentSiblingCaret<
  D extends CaretDirection,
>(
  startCaret: NodeCaret<D>,
  rootMode?: RootMode,
): null | [NodeCaret<D>, number];

export type StateConfigWrapper<K extends string, V> = {
  readonly stateConfig: StateConfig<K, V>;
  readonly $get: <T extends LexicalNode>(node: T) => V;
  readonly $set: <T extends LexicalNode>(
    node: T,
    valueOrUpdater: ValueOrUpdater<V>,
  ) => T;
  /** `[$get, $set]` */
  readonly accessors: Readonly<[$get: StateConfigWrapper<K, V>['$get'], $set: StateConfigWrapper<K, V>['$set']]>;
  /**
   * `() => function () { return $get(this) }`
   *
   * Should be called with an explicit `this` type parameter.
   *
   * @example
   * ```ts
   * class MyNode {
   *   // …
   *   myGetter = myWrapper.makeGetterMethod<this>();
   * }
   * ```
   */
  makeGetterMethod<T extends LexicalNode>(): (this: T) => V;
  /**
   * `() => function (valueOrUpdater) { return $set(this, valueOrUpdater) }`
   *
   * Must be called with an explicit `this` type parameter.
   *
   * @example
   * ```ts
   * class MyNode {
   *   // …
   *   mySetter = myWrapper.makeSetterMethod<this>();
   * }
   * ```
   */
  makeSetterMethod<T extends LexicalNode>(): (
    this: T,
    valueOrUpdater: ValueOrUpdater<V>,
  ) => T;
};

declare export function makeStateWrapper<K extends string, V>(
  stateConfig: StateConfig<K, V>,
): StateConfigWrapper<K, V>;

declare export function getScrollParent(
  element: HTMLElement,
  includeHidden: boolean,
): HTMLElement | HTMLBodyElement;
