import { autoUpdate, hide, useFloating } from "@floating-ui/react-dom";
import { Button, CloseButton } from "@wix/design-system";
import { injectCss as imageInject } from "@wix/ricos/css/plugin-image-viewer.global.inject";
import { injectCss } from "@wix/ricos/css/ricos-viewer.global.inject";
import { RedoSmall, UndoSmall } from "@wix/wix-ui-icons-common";
import _ from "lodash";
import React, { useContext, useEffect, useMemo } from "react";
import ReactDOM from "react-dom";
import { isTextContainer } from "./../ricos-content/truncateContent/types.js";
import { EditorContext } from "./../ricos-context/editor-context.js";
import {
  ModalServiceContext,
  PluginsContext,
  UploadContext,
  useDevService,
  ZIndexContext
} from "./../ricos-context/index.js";
import { RicosContext } from "./../ricos-context/ricos-context.js";
import { CollapsibleListData_InitialExpandedItems, Node_Type } from "./../ricos-schema/legacy/wix/rich_content/v1/index.js";
import { RicosViewer } from "./../ricos-viewer-next/index.js";
import { FloatingContainer } from "./../_shared/floating-container.js";
import { handleFileUpload } from "./../_shared/handle-file-upload.js";
import { logInvariant } from "./../_shared/log-invariant.js";
import {
  PromptInputArea,
  WidgetPromptInputArea
} from "./../_shared/prompt-input-area/prompt-input-area.js";
import { quickStartViewerPlugins } from "./../_shared/quick-start-viewer-plugins/quick-start-viewer-plugins.js";
import { useWixInstanceService } from "./../_shared/request-service-provider/wix-instance-context.js";
import { useEditorProp, useEditorProps } from "./../_shared/some-editor-props-context.js";
import { unreachable } from "./../_shared/uncreachable.js";
import { pluginCardViewer } from "./../plugin-card/viewer.js";
import { useToolbarBoundary } from "./../toolbars-v3/toolbar-boundary.js";
import { CursorContext } from "./cursor-context.js";
import {
  INLINE_AI_ACCEPT_ALL_BUTTON,
  INLINE_AI_AI_REGION_ACCEPT_BUTTON,
  INLINE_AI_AI_REGION_COMPLETED,
  INLINE_AI_AI_REGION_VIEWER,
  INLINE_AI_BEFORE_AFTER_BUTTON,
  INLINE_AI_ERROR_CONTAINER,
  INLINE_AI_MODAL_ROOT,
  INLINE_AI_REJECT_ALL_BUTTON,
  INLINE_AI_STREAMING_CURSOR,
  INLINE_AI_STREAMING_CURSOR_FLAG,
  INLINE_AI_TEXT_RESPONSE_CONTAINER
} from "./data-hooks.js";
import { ErrorBoundary } from "./error-boundary.js";
import { useExecutePrompt } from "./execute-prompt.js";
import { HtmlPlaceholder } from "./html-placeholder.js";
import { ImagePlaceholder } from "./image-placeholder.js";
import { useInlineAiState } from "./inline-ai-context-provider.js";
import style from "./modal-scss.js";
import { getPagerAPI } from "./pager-api.js";
import { Pager } from "./pager.js";
import { PendingWidgetPlaceholder } from "./pending-widget-placeholder.js";
import { Spinner } from "./spinner.js";
import { INLINE_AI_PLUGIN_TYPE } from "./types.js";
const useActivePageFormat = (cardPluginConfig) => {
  const { adapter } = useContext(EditorContext);
  const storage = adapter?.tiptapEditor?.storage?.[Node_Type.CARD];
  const [format, setFormat] = React.useState(
    () => storage?.activePageFormat?.name ?? cardPluginConfig?.initialPageFormat
  );
  React.useEffect(() => {
    if (!storage?.onPageFormatChange) {
      return;
    }
    const update = () => setFormat(storage.activePageFormat?.name);
    storage.onPageFormatChange.add(update);
    return () => {
      storage.onPageFormatChange.delete(update);
    };
  }, [storage]);
  return format;
};
const INLINE_AI_MODAL_ID = "inline-ai";
const defaultState = {
  type: "typing",
  value: ""
};
const AiModalNg = React.forwardRef((props, _ref) => {
  const [_invocationId] = React.useState(() => crypto.randomUUID());
  const invocationId = props.invocationId ?? _invocationId;
  const { adapter } = useContext(EditorContext);
  const { t } = useContext(RicosContext) || {};
  const { uploadService } = useContext(UploadContext) || {};
  const devService = useDevService();
  const wixInstance = useWixInstanceService();
  const editorProps = useEditorProps();
  const editorPlugins = useContext(PluginsContext);
  const pluginConfig = editorPlugins?.getConfig(INLINE_AI_PLUGIN_TYPE);
  const { state, setState, stop } = useInlineAiState(invocationId, defaultState);
  const executePrompt = useExecutePrompt(pluginConfig.aiProjectId, pluginConfig.useWixelWidgets);
  const SelectedPromptInputArea = pluginConfig?.useWixelWidgets ? WidgetPromptInputArea : PromptInputArea;
  React.useEffect(() => {
    if (state.type === "closed") {
      modalService.closeModal(INLINE_AI_MODAL_ID);
      const actionableMessageCount = state.messages.filter(
        (m) => m.type === "insert" || m.type === "replace"
      ).length;
      const shouldEnablePreviewMode = actionableMessageCount > 1;
      const accepted = state.messages.filter(messageIsAcceptable).sort(orderInsertsFirst).map((m) => {
        const position = m.aiRegion ?? (m.type === "insert" ? { type: "afterNode", id: m.after || void 0 } : {
          type: "range",
          from: m.from,
          to: m.to,
          originalNodesRef: m.originalNodesRef
        });
        return { nodes: m.nodes, position };
      });
      queueMicrotask(() => {
        if (shouldEnablePreviewMode) {
          const storedMessages = state.messages;
          const storedChunks = state.chunks;
          const storedPrompt = state.prompt;
          adapter.tiptapEditor.commands.pushActionItem(() => {
            queueMicrotask(() => {
              const isModalOpen = modalService.isModalOpen(INLINE_AI_MODAL_ID);
              if (isModalOpen) {
                adapter.tiptapEditor.commands.removeAllAiRegions();
                modalService.closeModal(INLINE_AI_MODAL_ID);
                setState({
                  type: "closed",
                  messages: storedMessages,
                  chunks: storedChunks,
                  prompt: storedPrompt
                });
              } else {
                adapter.tiptapEditor.commands.undo();
              }
            });
          });
          adapter.tiptapEditor.commands.acceptRegions(accepted);
          adapter.tiptapEditor.commands.removeAllAiRegions();
          adapter.tiptapEditor.commands.pushActionItem(() => {
            queueMicrotask(() => {
              adapter.tiptapEditor.commands.undo();
              setState({
                type: "preview",
                // Clear aiRegion so useAiRegions creates fresh overlays
                messages: storedMessages.map((m) => {
                  if (m.type === "insert" || m.type === "replace") {
                    const { aiRegion, ...rest } = m;
                    return rest;
                  }
                  return m;
                }),
                activeMessageIndex: storedMessages.findLastIndex((m) => m.type !== "text"),
                chunks: storedChunks,
                prompt: storedPrompt
              });
              modalService.openModal({
                id: INLINE_AI_MODAL_ID,
                Component: AiModalNg,
                componentProps: {
                  invocationId,
                  onThemeChange: props.onThemeChange,
                  getCurrentTheme: props.getCurrentTheme
                },
                type: "exclusive",
                layout: "manual",
                positioning: {
                  referenceElement: getFakeReferenceElement(adapter.tiptapEditor),
                  placement: "bottom"
                },
                returnFocusToEditor: false
              });
            });
          });
        } else {
          adapter.tiptapEditor.commands.acceptRegions(accepted);
          adapter.tiptapEditor.commands.removeAllAiRegions();
        }
      });
      props.onDone?.({ type: "done" });
    }
    if (state.type === "error") {
      if (props.headless) {
        modalService.closeModal(INLINE_AI_MODAL_ID);
      }
      adapter.tiptapEditor.commands.removeAllAiRegions();
      props.onDone?.({ type: "error" });
    }
    if (state.type === "typing") {
      queueMicrotask(() => {
        adapter.tiptapEditor.commands.removeAllAiRegions();
      });
    }
  }, [state.type]);
  const ref = React.useRef(null);
  const { getPublicCommands } = useContext(EditorContext);
  const modalService = useContext(ModalServiceContext);
  React.useEffect(() => {
    if (state.type === "typing") {
      if (ref.current) {
        ref.current.focus();
      }
    }
  }, [state.type, ref.current]);
  const tiptapEditor = adapter.tiptapEditor;
  switch (state.type) {
    case "typing": {
      const attachments = state.attachments ?? [];
      const hasFileUpload = Boolean(uploadService);
      const handleFileUploadFn = async () => {
        if (!uploadService) {
          return;
        }
        await handleFileUpload({
          pluginId: "inline-ai",
          services: {
            uploadService,
            devService,
            wixInstance,
            editorProps,
            modals: modalService
          },
          callbacks: {
            onLoadingStart: (attachment) => {
              setState((s) => {
                if (s.type !== "typing") {
                  return s;
                }
                return {
                  ...s,
                  attachments: [...s.attachments ?? [], attachment]
                };
              });
            },
            onSuccess: (attachmentId, attachment) => {
              setState((s) => {
                if (s.type !== "typing") {
                  return s;
                }
                const existingAttachment = (s.attachments ?? []).find((a) => a.id === attachmentId);
                return {
                  ...s,
                  attachments: existingAttachment ? (s.attachments ?? []).map((a) => a.id === attachmentId ? attachment : a) : [...s.attachments ?? [], attachment]
                };
              });
            },
            onError: (attachmentId, attachment) => {
              setState((s) => {
                if (s.type !== "typing") {
                  return s;
                }
                const existingAttachment = (s.attachments ?? []).find((a) => a.id === attachmentId);
                if (!existingAttachment) {
                  return s;
                }
                const errorAttachment = {
                  id: attachmentId,
                  status: "error",
                  file: existingAttachment.file,
                  error: attachment.error
                };
                return {
                  ...s,
                  attachments: (s.attachments ?? []).map(
                    (a) => a.id === attachmentId ? errorAttachment : a
                  )
                };
              });
            }
          }
        });
      };
      return /* @__PURE__ */ React.createElement(
        FloatingContainer,
        {
          ...props,
          state: "overlay",
          editorContainer: tiptapEditor.options.element,
          onClickOutside: () => setState(toClosed)
        },
        /* @__PURE__ */ React.createElement("div", { "data-hook": INLINE_AI_MODAL_ROOT }, /* @__PURE__ */ React.createElement(
          SelectedPromptInputArea,
          {
            ref,
            dataHook: "inline-ai-prompt-input",
            onChange: (x) => setState((prev) => ({
              type: "typing",
              value: x,
              attachments: "attachments" in prev ? prev.attachments : []
            })),
            onSubmit: () => {
              const selection = getPublicCommands().getSelectionSlice() ?? [];
              const uploadedFiles = attachments.filter((a) => a.status === "ok").map((a) => ({
                fileUrl: a.file.url,
                mimeType: a.file.type
              })) ?? [];
              const interceptResult = props.interceptInlinePrompt?.(
                state.value,
                selection,
                uploadedFiles,
                invocationId
              );
              if (interceptResult === "replace") {
                return;
              }
              if (interceptResult === "stop") {
                setState(toClosed);
                modalService.closeModal(INLINE_AI_MODAL_ID);
                return;
              }
              if (!executePrompt) {
                logInvariant(new Error("Inline AI service not available"));
                return;
              }
              executePrompt({
                document: getPublicCommands().getContent(),
                prompt: state.value,
                selection,
                getCurrentTheme: props.getCurrentTheme,
                invocationId,
                onThemeChange: props.onThemeChange,
                files: attachments.filter((a) => a.status === "ok").map((a) => ({
                  fileUrl: a.file.url,
                  mimeType: a.file.type,
                  fileName: a.file.name ?? ""
                })) ?? []
              });
            },
            onCancel: () => {
            },
            value: state.value,
            zIndex: 1e3,
            maxLength: 1e3,
            attachments,
            onRemoveAttachment: (id) => {
              setState((s) => {
                if (s.type !== "typing") {
                  return s;
                }
                return {
                  ...s,
                  attachments: (s.attachments ?? []).filter((a) => a.id !== id)
                };
              });
            },
            fileUploadFn: hasFileUpload ? handleFileUploadFn : void 0
          }
        ))
      );
    }
    case "pending":
      if (props.headless) {
        return null;
      }
      return /* @__PURE__ */ React.createElement(
        FloatingContainer,
        {
          ...props,
          state: "top",
          editorContainer: tiptapEditor.options.element,
          onClickOutside: () => {
          }
        },
        /* @__PURE__ */ React.createElement(
          "div",
          {
            style: {
              display: "flex",
              justifyContent: "center",
              alignItems: "center"
            }
          },
          /* @__PURE__ */ React.createElement(Spinner, { onClick: () => stop({ headless: props.headless ?? false }) })
        )
      );
    case "streaming": {
      const aiMessages = state.messages;
      return /* @__PURE__ */ React.createElement(
        FloatingContainer,
        {
          ...props,
          state: "top",
          editorContainer: tiptapEditor.options.element,
          onClickOutside: () => {
          }
        },
        !props.headless && /* @__PURE__ */ React.createElement(
          "div",
          {
            style: {
              display: "flex",
              justifyContent: "center",
              alignItems: "center"
            }
          },
          /* @__PURE__ */ React.createElement(Spinner, { onClick: () => stop({ headless: props.headless ?? false }) })
        ),
        /* @__PURE__ */ React.createElement(
          AiRegionStreams,
          {
            closeModal: () => setState(toClosed),
            headless: props.headless,
            completed: false,
            aiMessages,
            activeMessageIndex: aiMessages.findLastIndex((m) => m.type !== "text"),
            setActiveMessageIndex: () => {
            },
            setAiMessages: (action) => {
              setState((s) => {
                if (s.type === "streaming") {
                  return {
                    ...s,
                    messages: typeof action === "function" ? action(s.messages) : action
                  };
                } else {
                  return s;
                }
              });
            },
            onAcceptAll: () => setState(toClosed),
            onRejectAll: () => setState(toRejected),
            t,
            generatingLabel: props.generatingLabel
          }
        )
      );
    }
    case "preview": {
      const aiMessages = state.messages;
      const textFromTextResponse = aiMessages.filter((m) => m.type === "text").map((m) => m.text).join("\n");
      const noActionMessages = !aiMessages.length || aiMessages.every((m) => m.type === "text");
      if (noActionMessages && props.headless) {
        return null;
      }
      if (noActionMessages) {
        return /* @__PURE__ */ React.createElement(
          FloatingContainer,
          {
            ...props,
            state: "top",
            editorContainer: tiptapEditor.options.element,
            onClickOutside: () => setState(toClosed)
          },
          /* @__PURE__ */ React.createElement(
            "div",
            {
              className: style.textResponseContainer,
              "data-hook": INLINE_AI_TEXT_RESPONSE_CONTAINER
            },
            textFromTextResponse,
            /* @__PURE__ */ React.createElement(
              CloseButton,
              {
                size: "medium",
                className: style.closeButton,
                onClick: () => setState(toClosed)
              }
            )
          )
        );
      }
      return /* @__PURE__ */ React.createElement(
        FloatingContainer,
        {
          ...props,
          state: "top",
          editorContainer: tiptapEditor.options.element,
          onClickOutside: (e) => {
            if (e.target instanceof Element && e.target.closest(".ProseMirror")) {
              setState(toClosed);
            }
          }
        },
        /* @__PURE__ */ React.createElement(
          AiRegionStreams,
          {
            closeModal: () => setState(toClosed),
            headless: false,
            completed: true,
            aiMessages,
            activeMessageIndex: state.activeMessageIndex,
            setActiveMessageIndex: (index) => {
              setState((s) => {
                if (s.type !== "preview") {
                  return s;
                }
                return { ...s, activeMessageIndex: index };
              });
            },
            setAiMessages: (action) => {
              setState((s) => {
                if (s.type === "preview") {
                  return {
                    ...s,
                    messages: typeof action === "function" ? action(s.messages) : action
                  };
                } else {
                  return s;
                }
              });
            },
            onAcceptAll: () => setState(toClosed),
            onRejectAll: () => setState(toRejected),
            t,
            generatingLabel: props.generatingLabel
          }
        )
      );
    }
    case "error": {
      if (props.headless) {
        return null;
      }
      return /* @__PURE__ */ React.createElement(
        FloatingContainer,
        {
          ...props,
          state: "top",
          editorContainer: tiptapEditor.options.element,
          onClickOutside: () => setState(toClosed)
        },
        /* @__PURE__ */ React.createElement("div", { className: style.errorResponseContainer, "data-hook": INLINE_AI_ERROR_CONTAINER }, state.error.message, /* @__PURE__ */ React.createElement(
          CloseButton,
          {
            size: "medium",
            className: style.closeButton,
            onClick: () => setState({ type: "typing", value: state.prompt })
          }
        ))
      );
    }
    case "closed": {
      return null;
    }
    default: {
      logInvariant(unreachable(state));
      return /* @__PURE__ */ React.createElement("div", null);
    }
  }
});
const StreamingCursor = () => {
  const cursorCtx = useContext(CursorContext);
  const isVisible = cursorCtx?.cursorState.showCursor ?? false;
  const label = cursorCtx?.cursorState.label;
  const cursorAnchorRef = cursorCtx?.cursorAnchorRef ?? null;
  const boundary = useToolbarBoundary();
  const zIndexService = useContext(ZIndexContext);
  const referenceElement = useMemo(
    () => ({
      getBoundingClientRect: () => {
        const anchor = cursorAnchorRef?.current;
        if (!anchor) {
          return new DOMRect(0, 0, 0, 0);
        }
        const anchorRect = anchor.getBoundingClientRect();
        return new DOMRect(anchorRect.left, anchorRect.top, 0, anchorRect.height);
      }
    }),
    [cursorAnchorRef, isVisible]
  );
  const {
    x,
    y,
    refs: { setReference, setFloating },
    strategy,
    middlewareData
  } = useFloating({
    placement: "right-start",
    middleware: [
      // Hide when reference element is gone or escaped boundary
      hide({ strategy: "referenceHidden" }),
      ...boundary ? [hide({ strategy: "escaped", boundary })] : []
    ],
    whileElementsMounted: (referenceEl, floatingEl, update) => autoUpdate(referenceEl, floatingEl, update, {
      animationFrame: true
    })
  });
  useEffect(() => {
    setReference(referenceElement);
  }, [setReference, referenceElement]);
  const isHidden = !isVisible || middlewareData.hide?.referenceHidden || middlewareData.hide?.escaped || x === null && y === null;
  return /* @__PURE__ */ React.createElement(
    "span",
    {
      ref: setFloating,
      className: style.streamingCursor,
      "data-hook": INLINE_AI_STREAMING_CURSOR,
      style: {
        position: strategy,
        left: x ?? 0,
        top: y ?? 0,
        pointerEvents: "none",
        display: isHidden ? "none" : "block",
        zIndex: zIndexService?.getZIndex("DIALOG") ?? 10
      }
    },
    label && /* @__PURE__ */ React.createElement("span", { className: style.streamingCursorFlag, "data-hook": INLINE_AI_STREAMING_CURSOR_FLAG }, label)
  );
};
const getCardIndexOffset = (editor, message) => {
  if (!editor) {
    return 0;
  }
  const doc = editor.state.doc;
  const targetId = message.type === "replace" ? message.from : message.after;
  if (!targetId) {
    return 0;
  }
  let offset = 0;
  let found = false;
  doc.descendants((node, _pos) => {
    if (found) {
      return false;
    }
    if (node.attrs.id === targetId) {
      found = true;
      if (message.type === "insert" && node.type.name === "CARD") {
        offset++;
      }
      return false;
    }
    if (node.type.name === "CARD") {
      offset++;
      return false;
    }
  });
  return offset;
};
const getReplacedCardCount = (editor, message) => {
  if (!editor) {
    return 0;
  }
  const doc = editor.state.doc;
  let count = 0;
  let inRange = false;
  doc.descendants((node, _pos) => {
    if (node.attrs.id === message.from) {
      inRange = true;
    }
    if (inRange && node.type.name === "CARD") {
      count++;
    }
    if (node.attrs.id === message.to) {
      inRange = false;
      return false;
    }
  });
  return count;
};
const AiRegionStreams = ({
  closeModal,
  aiMessages,
  setAiMessages,
  completed,
  headless,
  activeMessageIndex,
  setActiveMessageIndex,
  onAcceptAll,
  onRejectAll,
  t,
  generatingLabel
}) => {
  useAiRegions(aiMessages, setAiMessages);
  const { adapter } = useContext(EditorContext);
  const tiptapEditor = adapter?.tiptapEditor;
  const editorPlugins = useContext(PluginsContext);
  const cardPluginConfig = editorPlugins?.getConfig(Node_Type.CARD);
  const activePageFormat = useActivePageFormat(cardPluginConfig);
  React.useEffect(() => {
    const storage = tiptapEditor?.storage?.[Node_Type.CARD];
    if (!storage) {
      return;
    }
    const insertions = aiMessages.filter(
      (m) => m.type !== "text" && m.type !== "theme" && !m.viewBefore
    ).map((m) => {
      const newCardCount = m.nodes.filter((n) => n.type === Node_Type.CARD).length;
      const replacedCardCount = m.type === "replace" ? getReplacedCardCount(tiptapEditor, m) : 0;
      const netCount = newCardCount - replacedCardCount;
      if (netCount === 0) {
        return null;
      }
      const afterCardIndex = getCardIndexOffset(tiptapEditor, m);
      return { afterCardIndex, count: netCount };
    }).filter((v) => v !== null);
    storage.pendingCardInsertions = insertions;
    storage.onPendingCardInsertionsChange.forEach((cb) => cb());
    return () => {
      storage.pendingCardInsertions = [];
      storage.onPendingCardInsertionsChange.forEach((cb) => cb());
    };
  }, [aiMessages, tiptapEditor]);
  const aiMessagesRef = React.useRef(aiMessages);
  aiMessagesRef.current = aiMessages;
  const lastStreamingMessageIndex = React.useMemo(() => {
    if (completed) {
      return -1;
    }
    for (let i = aiMessages.length - 1; i >= 0; i--) {
      const msg = aiMessages[i];
      if (msg && msg.type !== "text" && msg.type !== "theme" && !msg.completed) {
        return i;
      }
    }
    return -1;
  }, [aiMessages, completed]);
  const showCursor = !completed && lastStreamingMessageIndex !== -1;
  const cursorCtx = useContext(CursorContext);
  const resolvedLabel = generatingLabel !== void 0 ? generatingLabel : t("InlineAi_Generating");
  React.useEffect(() => {
    cursorCtx?.setCursorState({ showCursor, label: resolvedLabel });
    return () => {
      cursorCtx?.setCursorState({ showCursor: false, label: null });
    };
  }, [showCursor, resolvedLabel]);
  const actionableMessageCount = aiMessages.filter((m) => m.type !== "text").length;
  const activeMessage = aiMessages[activeMessageIndex];
  const pagerAPI = getPagerAPI(
    aiMessages,
    activeMessageIndex,
    setActiveMessageIndex,
    setAiMessages
  );
  return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: style.bottomContainer }, completed && activeMessage && !headless && /* @__PURE__ */ React.createElement("div", { className: style.bottomControls }, actionableMessageCount > 1 && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
    Pager,
    {
      current: activeMessageIndex,
      total: actionableMessageCount,
      onPrev: () => {
        pagerAPI.onPrev();
      },
      onNext: () => {
        pagerAPI.onNext();
      }
    }
  ), /* @__PURE__ */ React.createElement(VerticalDivider, null), /* @__PURE__ */ React.createElement(
    Button,
    {
      className: "ai-region-button",
      size: "small",
      priority: "secondary",
      prefixIcon: activeMessage.viewBefore ? /* @__PURE__ */ React.createElement(RedoSmall, null) : /* @__PURE__ */ React.createElement(UndoSmall, null),
      onClick: () => {
        pagerAPI.toggleUndo();
      },
      dataHook: INLINE_AI_BEFORE_AFTER_BUTTON
    },
    activeMessage.viewBefore ? t("InlineAi_After") : t("InlineAi_Before")
  )), /* @__PURE__ */ React.createElement(
    Button,
    {
      className: "ai-region-button",
      size: "small",
      priority: "secondary",
      onClick: onAcceptAll,
      dataHook: INLINE_AI_ACCEPT_ALL_BUTTON
    },
    t("InlineAi_AcceptAll")
  ), /* @__PURE__ */ React.createElement(
    Button,
    {
      className: "ai-region-button",
      size: "small",
      priority: "secondary",
      onClick: onRejectAll,
      dataHook: INLINE_AI_REJECT_ALL_BUTTON
    },
    t("InlineAi_RejectAll")
  )), aiMessages.map((message, index) => {
    if (!message) {
      return null;
    }
    if (message.type === "text" || message.type === "theme") {
      return null;
    }
    if (!message.aiRegion) {
      return null;
    }
    const overlay = message.aiRegion.overlay;
    if (!overlay) {
      return null;
    }
    return ReactDOM.createPortal(
      /* @__PURE__ */ React.createElement(
        "div",
        {
          className: "ricos-ai-region-viewer",
          style: { position: "relative" },
          "data-hook": `${INLINE_AI_AI_REGION_VIEWER}${completed ? ` ${INLINE_AI_AI_REGION_COMPLETED}` : ""}`
        },
        /* @__PURE__ */ React.createElement(
          Viewer,
          {
            nodes: !message.viewBefore ? message.nodes : message.type === "replace" ? message.originalNodesRef.current || [] : [],
            isRoot: message.isRootLevel,
            isStreaming: !completed,
            isCompleted: !!message.completed,
            isActive: activeMessageIndex === index && pagerAPI.total > 1,
            onHover: () => {
              if (!completed) {
                return;
              }
              if (activeMessageIndex !== index) {
                setActiveMessageIndex(index);
              }
            },
            onClick: () => {
              if (!completed) {
                return;
              }
              closeModal();
            },
            cardPluginConfig,
            activePageFormat,
            cardIndexOffset: getCardIndexOffset(tiptapEditor, message)
          }
        ),
        /* @__PURE__ */ React.createElement(
          "div",
          {
            style: {
              display: "flex",
              gap: "8px",
              marginTop: "8px",
              position: "absolute",
              left: "0",
              right: "0",
              zIndex: 1e3
            }
          }
        )
      ),
      overlay
    );
  })));
};
const useAiRegions = (aiMessages, setAiMessages) => {
  const commands = useContext(EditorContext).adapter.tiptapEditor.commands;
  React.useEffect(() => {
    const aiMessagesWithoutRegions = aiMessages.filter(
      (m) => m.type !== "text" && m.type !== "theme" && !m.aiRegion
    );
    if (aiMessagesWithoutRegions.length === 0) {
      return;
    }
    setAiMessages((aiMessages2) => {
      const newRegions = [];
      const newAiMessages = aiMessages2.map((m) => {
        if (m.type === "text" || m.type === "theme" || m.aiRegion) {
          return m;
        }
        if (m.type === "insert") {
          const aiRegion = {
            type: "afterNode",
            id: m.after || void 0,
            overlay: document.createElement("div")
          };
          newRegions.push(aiRegion);
          return { ...m, aiRegion };
        }
        if (m.type === "replace") {
          const aiRegion = {
            type: "range",
            from: m.from,
            to: m.to,
            overlay: document.createElement("div"),
            originalNodesRef: m.originalNodesRef
          };
          newRegions.push(aiRegion);
          return { ...m, aiRegion };
        }
        logInvariant(unreachable(m));
        return m;
      });
      newRegions.length && commands.addAiRegions(newRegions);
      return newAiMessages;
    });
  }, [aiMessages, setAiMessages]);
};
const patchCollapsibleListsDuringStreaming = (nodes) => {
  return nodes.map((node) => {
    if (node.type === "COLLAPSIBLE_LIST") {
      return {
        ...node,
        collapsibleListData: {
          ...node.collapsibleListData,
          initialExpandedItems: CollapsibleListData_InitialExpandedItems.ALL
        },
        nodes: node.nodes ? patchCollapsibleListsDuringStreaming(node.nodes) : node.nodes
      };
    }
    if (node.type === "COLLAPSIBLE_ITEM") {
      const children = node.nodes ?? [];
      const hasTitle = children.some((child) => child.type === "COLLAPSIBLE_ITEM_TITLE");
      const hasBody = children.some((child) => child.type === "COLLAPSIBLE_ITEM_BODY");
      if (hasTitle && !hasBody) {
        const emptyBody = {
          type: "COLLAPSIBLE_ITEM_BODY",
          id: `${node.id}-body`,
          nodes: [
            {
              type: "PARAGRAPH",
              id: `${node.id}-body-p`,
              nodes: []
            }
          ]
        };
        return {
          ...node,
          nodes: [...patchCollapsibleListsDuringStreaming(children), emptyBody]
        };
      }
      return {
        ...node,
        nodes: children.length > 0 ? patchCollapsibleListsDuringStreaming(children) : children
      };
    }
    if (node.nodes && node.nodes.length > 0) {
      return {
        ...node,
        nodes: patchCollapsibleListsDuringStreaming(node.nodes)
      };
    }
    return node;
  });
};
const Viewer = React.memo(
  ({
    nodes,
    isStreaming,
    isCompleted,
    isActive,
    onClick,
    onHover,
    isRoot,
    onContainerRef,
    cardPluginConfig,
    activePageFormat,
    cardIndexOffset
  }) => {
    const containerRef = React.useRef(null);
    React.useEffect(() => {
      injectCss();
      imageInject();
    }, []);
    React.useEffect(() => {
      onContainerRef?.(containerRef.current);
      return () => onContainerRef?.(null);
    }, [onContainerRef]);
    const nodesWithCursor = isStreaming && !isCompleted ? appendNodeToLastNonEmptyTextContainer(nodes, {
      type: "CURSOR"
    }).nodes : nodes;
    const nodesWithPatchedCollapsibleLists = isStreaming && !isCompleted ? patchCollapsibleListsDuringStreaming(nodesWithCursor) : nodesWithCursor;
    const content = { nodes: nodesWithPatchedCollapsibleLists };
    const { theme } = React.useContext(RicosContext);
    const helpers = useEditorProp((p) => p.helpers);
    const instance = useEditorProp((p) => p.instance);
    return /* @__PURE__ */ React.createElement(ErrorBoundary, { retryProp: content }, /* @__PURE__ */ React.createElement(
      "div",
      {
        ref: containerRef,
        style: {
          // backgroundColor: isActive ? '#A8CAFF' : '#E7F0FF',
          background: !isStreaming ? isActive ? bgActive : bgDefault : void 0,
          position: "relative"
        }
      },
      /* @__PURE__ */ React.createElement(
        RicosViewer,
        {
          theme: {
            ...theme,
            customStyles: _.omit(theme.customStyles, "container"),
            breakout: isRoot ? theme?.breakout : void 0
          },
          plugins: [
            {
              config: {},
              type: "html_placeholder",
              // @ts-ignore
              nodeViewRenderers: {
                EXTERNAL: Placeholder
              }
            },
            {
              config: {},
              type: "cursor_placeholder",
              // @ts-ignore - CURSOR is a custom node type for inline AI streaming
              nodeViewRenderers: { CURSOR: CursorPlaceholder }
            },
            ...quickStartViewerPlugins(),
            pluginCardViewer({
              mode: "editor",
              renderCardHeader: cardPluginConfig.renderCardHeader,
              pageFormat: activePageFormat,
              cardIndexOffset
            })
          ],
          content,
          helpers,
          instance
        }
      ),
      /* @__PURE__ */ React.createElement(
        "button",
        {
          type: "button",
          className: "ai-region-button",
          "data-hook": INLINE_AI_AI_REGION_ACCEPT_BUTTON,
          style: {
            position: "absolute",
            top: 0,
            left: 0,
            right: 0,
            bottom: 0,
            opacity: 0,
            zIndex: 10,
            cursor: "wait"
          },
          onClick,
          onMouseEnter: onHover
        }
      )
    ));
  }
);
const appendNodeToLastNonEmptyTextContainer = (nodes, appendedNode) => {
  for (let i = nodes.length - 1; i >= 0; i--) {
    const node = nodes[i];
    if (!node) {
      continue;
    }
    if (isTextContainer(node.type)) {
      if (node.nodes?.length) {
        return {
          nodes: [
            ...nodes.slice(0, i),
            {
              ...node,
              nodes: [...node.nodes ?? [], appendedNode]
            },
            ...nodes.slice(i + 1)
          ],
          appended: true
        };
      }
      continue;
    }
    if (node.nodes?.length) {
      const result = appendNodeToLastNonEmptyTextContainer(node.nodes, appendedNode);
      if (result.appended) {
        return {
          nodes: [
            ...nodes.slice(0, i),
            { ...node, nodes: result.nodes },
            ...nodes.slice(i + 1)
          ],
          appended: true
        };
      }
    }
  }
  return { nodes, appended: false };
};
const CursorPlaceholder = () => {
  const cursorAnchorRef = React.useContext(CursorContext)?.cursorAnchorRef ?? null;
  return /* @__PURE__ */ React.createElement(
    "span",
    {
      ref: cursorAnchorRef,
      style: {
        display: "inline-block",
        width: 0,
        height: "1em",
        // Inherits the font size of the parent, matching line height
        verticalAlign: "middle"
      }
    }
  );
};
const messageIsAcceptable = (m) => (m.type === "insert" || m.type === "replace") && !m.viewBefore;
const VerticalDivider = () => /* @__PURE__ */ React.createElement(
  "div",
  {
    style: {
      width: "1px",
      backgroundColor: "#DFE5EB",
      height: "100%"
    }
  }
);
const Placeholder = ({ node, nodeIndex, NodeViewContainer }) => {
  const innerNode = node.externalData;
  return /* @__PURE__ */ React.createElement(NodeViewContainer, { node: innerNode, nodeIndex }, innerNode.type === "HTML" ? /* @__PURE__ */ React.createElement(HtmlPlaceholder, { node: innerNode }) : innerNode.type === "IMAGE" ? /* @__PURE__ */ React.createElement(ImagePlaceholder, { node: innerNode }) : /* @__PURE__ */ React.createElement(PendingWidgetPlaceholder, { node }));
};
const orderInsertsFirst = (a, b) => {
  if (a.type === b.type) {
    return 0;
  }
  return a.type === "insert" ? -1 : 1;
};
const getFakeReferenceElement = (editor) => {
  const { view } = editor;
  const { from, to } = view.state.selection;
  const getRect = () => {
    if (from === to) {
      const coords = view.coordsAtPos(from);
      return new DOMRect(coords.left, coords.top, 0, coords.bottom - coords.top);
    }
    const start = view.coordsAtPos(from);
    const end = view.coordsAtPos(to);
    return new DOMRect(
      Math.min(start.left, end.left),
      start.top,
      Math.abs(end.left - start.left),
      end.bottom - start.top
    );
  };
  return {
    getBoundingClientRect: getRect,
    focus: () => view.focus()
  };
};
const toClosed = (s) => s.type === "closed" ? s : {
  type: "closed",
  messages: "messages" in s ? s.messages : [],
  chunks: "chunks" in s ? s.chunks : [],
  prompt: "prompt" in s ? s.prompt : ""
};
const toRejected = (s) => {
  if (s.type === "closed") {
    return s;
  }
  const messages = "messages" in s ? s.messages : [];
  const rejectedMessages = messages.map((m) => {
    if (m.type === "insert" || m.type === "replace") {
      return { ...m, viewBefore: true };
    }
    return m;
  });
  return {
    type: "closed",
    messages: rejectedMessages,
    chunks: "chunks" in s ? s.chunks : [],
    prompt: "prompt" in s ? s.prompt : ""
  };
};
const bgActive = "hsla(0, 10%, 80%, 0.6)";
const bgDefault = "hsla(0, 10%, 80%, 0.4)";
export {
  AiModalNg,
  INLINE_AI_MODAL_ID,
  StreamingCursor
};
//# sourceMappingURL=modal.js.map
