import { jsx, jsxs, Fragment } from "react/jsx-runtime";
import { Component, useEnvironment, useDesugaredRelativeSingleField, VariableInputTransformer, HasMany, SugaredField } from "@contember/react-binding";
import { emptyArray, useReferentiallyStableCallback, noop } from "@contember/react-utils";
import { FieldContainer, EditorCanvas } from "@contember/ui";
import { useMemo, useState, useCallback, useLayoutEffect, Fragment as Fragment$1 } from "react";
import { Range, Transforms } from "slate";
import { Slate } from "slate-react";
import { TextareaField } from "@contember/react-form-fields-ui";
import { shouldCancelStart } from "@contember/utilities";
import { EditableCanvas } from "../baseEditor/EditableCanvas.js";
import { RichEditor } from "../RichEditor.js";
import { BlockHoveringToolbarContents } from "./BlockHoveringToolbarContents.js";
import { ReferencesProvider } from "./references/ReferencesProvider.js";
import { useGetReferencedEntity } from "./references/useGetReferencedEntity.js";
import { useInsertElementWithReference } from "./references/useInsertElementWithReference.js";
import { SortedBlocksContext } from "./state/SortedBlocksContext.js";
import { useBlockEditorState } from "./state/useBlockEditorState.js";
import { useEditorReferenceBlocks } from "./templating/useEditorReferenceBlocks.js";
import { useCreateElementReference } from "./references/useCreateElementReference.js";
import { initBlockEditor } from "./editor/initBlockEditor.js";
import { SortableRepeaterContainer } from "../../collections/Repeater/SortableRepeaterContainer.js";
import { ContentOutlet } from "./templating/ContentOutlet.js";
import { RichTextField } from "../RichTextField/RichTextField.js";
import { useDiscriminatedData } from "../../discrimination/useDiscriminatedData.js";
import { useNormalizedBlocks } from "../../blocks/useNormalizedBlocks.js";
import { getDiscriminatedBlock } from "../../blocks/getDiscriminatedBlock.js";
import { createEditorWithEssentials } from "../baseEditor/createEditorWithEssentials.js";
import { paragraphElementType } from "../plugins/element/paragraphs/ParagraphElement.js";
import { HoveringToolbars } from "../toolbars/HoveringToolbars.js";
import { Repeater } from "../../collections/Repeater/Repeater.js";
const BlockEditorComponent = Component(
  (props) => {
    const environment = useEnvironment();
    assertStaticBlockEditorInvariants();
    const {
      contentField,
      label,
      placeholder,
      sortableBy,
      children,
      size,
      leadingFieldBackedElements = emptyArray,
      trailingFieldBackedElements = emptyArray,
      referencesField,
      referenceDiscriminationField,
      monolithicReferencesMode = false,
      embedReferenceDiscriminateBy,
      embedContentDiscriminationField,
      embedHandlers = emptyArray,
      toolbarScheme,
      inlineButtons = defaultInlineButtons,
      blockButtons,
      otherBlockButtons,
      showToolbarLabels,
      plugins,
      augmentEditor,
      augmentEditorBuiltins,
      ...blockListProps
    } = props;
    const desugaredReferenceDiscriminationField = useDesugaredRelativeSingleField(referenceDiscriminationField);
    const desugaredEmbedContentDiscriminationField = useDesugaredRelativeSingleField(embedContentDiscriminationField);
    const editorReferenceBlocks = useEditorReferenceBlocks(children);
    const discriminatedEmbedHandlers = useDiscriminatedData(embedHandlers);
    const embedReferenceDiscriminant = useMemo(() => {
      if (embedReferenceDiscriminateBy !== void 0) {
        return VariableInputTransformer.transformValue(embedReferenceDiscriminateBy, environment);
      }
      return void 0;
    }, [embedReferenceDiscriminateBy, environment]);
    const embedSubBlocks = useNormalizedBlocks(
      embedReferenceDiscriminant !== void 0 ? getDiscriminatedBlock(editorReferenceBlocks, embedReferenceDiscriminant)?.datum.children : void 0
      // TODO this may crash
    );
    const [baseEditor] = useState(() => createEditorWithEssentials(paragraphElementType));
    const { nodes, onChange, sortedBlocksRef, refreshBlocks } = useBlockEditorState({
      editor: baseEditor,
      blockList: blockListProps,
      contentField,
      sortableBy,
      monolithicReferencesMode,
      referencesField
    });
    const getReferencedEntity = useGetReferencedEntity({
      monolithicReferencesMode,
      referencesField,
      sortedBlocksRef
    });
    const createElementReference = useCreateElementReference({
      sortedBlocksRef,
      monolithicReferencesMode,
      referenceDiscriminationField,
      referencesField,
      refreshBlocks
    });
    const stableCreateElementReference = useReferentiallyStableCallback(createElementReference);
    const insertElementWithReference = useInsertElementWithReference({
      editor: baseEditor,
      createElementReference: stableCreateElementReference
    });
    const stableGetReferencedEntity = useReferentiallyStableCallback(getReferencedEntity);
    const [editor] = useState(() => initBlockEditor({
      editor: baseEditor,
      augmentEditor,
      augmentEditorBuiltins,
      editorReferenceBlocks,
      embedContentDiscriminationField: desugaredEmbedContentDiscriminationField,
      embedHandlers: discriminatedEmbedHandlers,
      embedReferenceDiscriminateBy: embedReferenceDiscriminant,
      embedSubBlocks,
      plugins,
      referenceDiscriminationField: desugaredReferenceDiscriminationField,
      createElementReferences: stableCreateElementReference,
      getReferencedEntity: stableGetReferencedEntity,
      insertElementWithReference
    }));
    const shouldDisplayInlineToolbar = useCallback(() => {
      const selection = baseEditor.selection;
      return !(!selection || Range.isCollapsed(selection));
    }, [baseEditor]);
    const leadingElements = useFieldBackedElementFields(leadingFieldBackedElements);
    const trailingElements = useFieldBackedElementFields(trailingFieldBackedElements);
    const [_, setMeaninglessState] = useState(0);
    useLayoutEffect(() => {
      if (editor.children !== nodes && JSON.stringify(editor.children) !== JSON.stringify(nodes)) {
        editor.children = nodes;
        setMeaninglessState((meaninglessState) => meaninglessState + 1);
      }
    }, [nodes, editor]);
    return /* @__PURE__ */ jsx(FieldContainer, { label, useLabelElement: false, children: /* @__PURE__ */ jsx(ReferencesProvider, { getReferencedEntity, children: /* @__PURE__ */ jsx(SortedBlocksContext.Provider, { value: sortedBlocksRef.current, children: /* @__PURE__ */ jsx(
      SortableRepeaterContainer,
      {
        axis: "y",
        lockAxis: "y",
        helperClass: "is-dragged",
        lockToContainerEdges: true,
        useWindowAsScrollContainer: true,
        useDragHandle: true,
        onSortEnd: useCallback((data) => {
          Transforms.moveNodes(editor, {
            at: [data.oldIndex],
            to: [data.newIndex]
          });
        }, [editor]),
        shouldCancelStart,
        children: /* @__PURE__ */ jsx(Slate, { editor, value: nodes, onChange, children: /* @__PURE__ */ jsx(
          EditorCanvas,
          {
            inset: "hovering-toolbar",
            underlyingComponent: EditableCanvas,
            componentProps: {
              renderElement: baseEditor.renderElement,
              renderLeaf: baseEditor.renderLeaf,
              onKeyDown: baseEditor.onKeyDown,
              onFocusCapture: baseEditor.onFocus,
              onBlurCapture: baseEditor.onBlur,
              onDOMBeforeInput: baseEditor.onDOMBeforeInput,
              onDrop: (e) => {
                e.preventDefault();
              },
              placeholder,
              leading: leadingElements,
              trailing: trailingElements
            },
            size: size ?? "large",
            children: useMemo(
              () => /* @__PURE__ */ jsx(
                HoveringToolbars,
                {
                  toolbarScheme,
                  shouldDisplayInlineToolbar,
                  inlineButtons,
                  showLabels: showToolbarLabels,
                  blockButtons: /* @__PURE__ */ jsx(
                    BlockHoveringToolbarContents,
                    {
                      editorReferenceBlocks,
                      blockButtons,
                      otherBlockButtons,
                      showLabels: showToolbarLabels
                    }
                  )
                }
              ),
              [blockButtons, editorReferenceBlocks, inlineButtons, otherBlockButtons, shouldDisplayInlineToolbar, showToolbarLabels, toolbarScheme]
            )
          }
        ) })
      }
    ) }) }) });
  },
  (props, environment) => {
    assertStaticBlockEditorInvariants();
    const embedHandlers = Array.from(props.embedHandlers || []);
    const inlineButtons = props.inlineButtons ? (Array.isArray(props.inlineButtons[0]) ? props.inlineButtons : [props.inlineButtons]).flat() : emptyArray;
    const references = !!(props.referencesField && props.referenceDiscriminationField) && /* @__PURE__ */ jsxs(
      HasMany,
      {
        ...typeof props.referencesField === "string" ? { field: props.referencesField } : props.referencesField,
        initialEntityCount: 0,
        children: [
          inlineButtons.filter((button) => "referenceContent" in button).map(({ referenceContent: Content }, i) => {
            return /* @__PURE__ */ jsx(Content, { referenceId: "", editor: 0, selection: null, onSuccess: noop, onCancel: noop }, i);
          }),
          /* @__PURE__ */ jsx(SugaredField, { field: props.referenceDiscriminationField }),
          props.children,
          props.embedContentDiscriminationField && /* @__PURE__ */ jsxs(Fragment, { children: [
            /* @__PURE__ */ jsx(SugaredField, { field: props.embedContentDiscriminationField }),
            embedHandlers.map((handler, i) => /* @__PURE__ */ jsx(Fragment$1, { children: handler.staticRender(environment) }, i))
          ] })
        ]
      }
    );
    return /* @__PURE__ */ jsxs(Fragment, { children: [
      props.leadingFieldBackedElements?.map((item, i) => "element" in item ? /* @__PURE__ */ jsx(Fragment$1, { children: item.element }, `leading_${i}`) : /* @__PURE__ */ jsx(SugaredField, { field: item.field }, `leading_${i}`)),
      props.trailingFieldBackedElements?.map((item, i) => "element" in item ? /* @__PURE__ */ jsx(Fragment$1, { children: item.element }, `trailing_${i}`) : /* @__PURE__ */ jsx(SugaredField, { field: item.field }, `trailing_${i}`)),
      /* @__PURE__ */ jsxs(Repeater, { ...props, label: props.label ?? "", initialEntityCount: 0, children: [
        /* @__PURE__ */ jsx(SugaredField, { field: props.sortableBy }),
        /* @__PURE__ */ jsx(SugaredField, { field: props.contentField }),
        !props.monolithicReferencesMode && references
      ] }),
      props.monolithicReferencesMode && references
    ] });
  },
  "BlockEditor"
);
const BlockEditor = Object.assign(BlockEditorComponent, {
  ContentOutlet
});
const useFieldBackedElementFields = (elements) => {
  return /* @__PURE__ */ jsx(Fragment, { children: elements.map((el, i) => {
    if ("element" in el) {
      return /* @__PURE__ */ jsx(Fragment$1, { children: el.element }, i);
    }
    if (el.format === "plainText") {
      return /* @__PURE__ */ jsx(
        TextareaField,
        {
          distinction: "seamless",
          field: el.field,
          label: void 0,
          placeholder: el.placeholder,
          size: el.size
        },
        i
      );
    }
    return /* @__PURE__ */ jsx(RichTextField, { field: el.field, label: void 0, placeholder: el.placeholder, distinction: el.distinction ?? "seamless" }, i);
  }) });
};
const assertStaticBlockEditorInvariants = (props, environment) => {
};
const RB = RichEditor.buttons;
const defaultInlineButtons = [
  [RB.bold, RB.italic, RB.underline, RB.anchor],
  [RB.headingOne, RB.headingTwo],
  [RB.strikeThrough, RB.code]
];
export {
  BlockEditor
};
//# sourceMappingURL=BlockEditor.js.map
