// packages/editor/src/components/collab-sidebar/hooks.js
import { __ } from "@wordpress/i18n";
import {
  useState,
  useEffect,
  useMemo,
  useSyncExternalStore
} from "@wordpress/element";
import { useEntityRecords, store as coreStore } from "@wordpress/core-data";
import { useDispatch, useRegistry, useSelect } from "@wordpress/data";
import {
  store as blockEditorStore,
  privateApis as blockEditorPrivateApis
} from "@wordpress/block-editor";
import { store as noticesStore } from "@wordpress/notices";
import { getScrollContainer } from "@wordpress/dom";
import { decodeEntities } from "@wordpress/html-entities";
import { store as interfaceStore } from "@wordpress/interface";
import { store as editorStore } from "../../store/index.mjs";
import { FLOATING_NOTES_SIDEBAR } from "./constants.mjs";
import { unlock } from "../../lock-unlock.mjs";
import { createBoardStore } from "./board-store.mjs";
import { calculateNotePositions } from "./utils.mjs";
var { cleanEmptyObject } = unlock(blockEditorPrivateApis);
function useNoteThreads(postId) {
  const queryArgs = {
    post: postId,
    type: "note",
    status: "all",
    per_page: -1
  };
  const { records: threads } = useEntityRecords(
    "root",
    "comment",
    queryArgs,
    { enabled: !!postId && typeof postId === "number" }
  );
  const { getBlockAttributes } = useSelect(blockEditorStore);
  const { clientIds } = useSelect((select) => {
    const { getClientIdsWithDescendants } = select(blockEditorStore);
    return {
      clientIds: getClientIdsWithDescendants()
    };
  }, []);
  const { notes, unresolvedNotes } = useMemo(() => {
    if (!threads || threads.length === 0) {
      return { notes: [], unresolvedNotes: [] };
    }
    const blocksWithNotes = {};
    const clientIdByNoteId = /* @__PURE__ */ new Map();
    for (const clientId of clientIds) {
      const noteId = getBlockAttributes(clientId)?.metadata?.noteId;
      if (noteId) {
        const key = String(noteId);
        blocksWithNotes[clientId] = key;
        clientIdByNoteId.set(key, clientId);
      }
    }
    const threadsById = /* @__PURE__ */ new Map();
    const rootThreads = [];
    for (const item of threads) {
      const thread = {
        ...item,
        reply: [],
        blockClientId: item.parent === 0 ? clientIdByNoteId.get(String(item.id)) ?? null : null
      };
      threadsById.set(item.id, thread);
      if (item.parent === 0) {
        rootThreads.push(thread);
      }
    }
    for (const item of threads) {
      if (item.parent !== 0) {
        threadsById.get(item.parent)?.reply.unshift(threadsById.get(item.id));
      }
    }
    if (rootThreads.length === 0) {
      return { notes: [], unresolvedNotes: [] };
    }
    const unresolved = [];
    const resolved = [];
    for (const noteId of Object.values(blocksWithNotes)) {
      const thread = threadsById.get(Number(noteId)) ?? threadsById.get(noteId);
      if (!thread) {
        continue;
      }
      if (thread.status === "hold") {
        unresolved.push(thread);
      } else if (thread.status === "approved") {
        resolved.push(thread);
      }
    }
    const orphans = rootThreads.filter(
      (thread) => !thread.blockClientId
    );
    return {
      notes: [...unresolved, ...resolved, ...orphans],
      unresolvedNotes: unresolved
    };
  }, [clientIds, threads, getBlockAttributes]);
  return {
    notes,
    unresolvedNotes
  };
}
function useNoteActions() {
  const { createNotice } = useDispatch(noticesStore);
  const { saveEntityRecord, deleteEntityRecord } = useDispatch(coreStore);
  const { getCurrentPostId } = useSelect(editorStore);
  const { getBlockAttributes, getSelectedBlockClientId } = useSelect(blockEditorStore);
  const { updateBlockAttributes } = useDispatch(blockEditorStore);
  const onError = (error) => {
    const errorMessage = error.message && error.code !== "unknown_error" ? decodeEntities(error.message) : __("An error occurred while performing an update.");
    createNotice("error", errorMessage, {
      type: "snackbar",
      isDismissible: true
    });
  };
  const onCreate = async ({ content, parent }) => {
    try {
      const savedRecord = await saveEntityRecord(
        "root",
        "comment",
        {
          post: getCurrentPostId(),
          content,
          status: "hold",
          type: "note",
          parent: parent || 0
        },
        { throwOnError: true }
      );
      if (!parent && savedRecord?.id) {
        const clientId = getSelectedBlockClientId();
        const metadata = getBlockAttributes(clientId)?.metadata;
        updateBlockAttributes(clientId, {
          metadata: {
            ...metadata,
            noteId: savedRecord.id
          }
        });
      }
      createNotice(
        "snackbar",
        parent ? __("Reply added.") : __("Note added."),
        {
          type: "snackbar",
          isDismissible: true
        }
      );
      return savedRecord;
    } catch (error) {
      onError(error);
    }
  };
  const onEdit = async ({ id, content, status }) => {
    const messageType = status ? status : "updated";
    const messages = {
      approved: __("Note marked as resolved."),
      hold: __("Note reopened."),
      updated: __("Note updated.")
    };
    try {
      if (status === "approved" || status === "hold") {
        await saveEntityRecord(
          "root",
          "comment",
          {
            id,
            status
          },
          {
            throwOnError: true
          }
        );
        const newNoteData = {
          post: getCurrentPostId(),
          content: content || "",
          // Empty content for resolve, content for reopen.
          type: "note",
          status,
          parent: id,
          meta: {
            _wp_note_status: status === "approved" ? "resolved" : "reopen"
          }
        };
        await saveEntityRecord("root", "comment", newNoteData, {
          throwOnError: true
        });
      } else {
        const updateData = {
          id,
          content,
          status
        };
        await saveEntityRecord("root", "comment", updateData, {
          throwOnError: true
        });
      }
      createNotice(
        "snackbar",
        messages[messageType] ?? __("Note updated."),
        {
          type: "snackbar",
          isDismissible: true
        }
      );
    } catch (error) {
      onError(error);
    }
  };
  const onDelete = async (note) => {
    try {
      await deleteEntityRecord("root", "comment", note.id, void 0, {
        throwOnError: true
      });
      if (!note.parent) {
        const clientId = getSelectedBlockClientId();
        const metadata = getBlockAttributes(clientId)?.metadata;
        updateBlockAttributes(clientId, {
          metadata: cleanEmptyObject({
            ...metadata,
            noteId: void 0
          })
        });
      }
      createNotice("snackbar", __("Note deleted."), {
        type: "snackbar",
        isDismissible: true
      });
    } catch (error) {
      onError(error);
    }
  };
  return { onCreate, onEdit, onDelete };
}
function useEnableFloatingSidebar(enabled = false) {
  const registry = useRegistry();
  useEffect(() => {
    if (!enabled) {
      return;
    }
    const { getActiveComplementaryArea } = registry.select(interfaceStore);
    const { disableComplementaryArea, enableComplementaryArea } = registry.dispatch(interfaceStore);
    const unsubscribe = registry.subscribe(() => {
      if (getActiveComplementaryArea("core") === null) {
        enableComplementaryArea("core", FLOATING_NOTES_SIDEBAR);
      }
    });
    return () => {
      unsubscribe();
      if (getActiveComplementaryArea("core") === FLOATING_NOTES_SIDEBAR) {
        disableComplementaryArea("core", FLOATING_NOTES_SIDEBAR);
      }
    };
  }, [enabled, registry]);
}
function useFloatingBoard({
  threads,
  selectedNoteId,
  isFloating,
  sidebarRef
}) {
  const [notePositions, setNotePositions] = useState({});
  const [store] = useState(createBoardStore);
  const heights = useSyncExternalStore(store.subscribe, store.getSnapshot);
  useEffect(() => {
    if (!isFloating || !sidebarRef?.current) {
      return;
    }
    const panel = sidebarRef.current;
    const blockEl = store.getFirstBlockElement();
    const rootEl = blockEl?.closest(".is-root-container") ?? blockEl;
    const canvas = rootEl ? getScrollContainer(rootEl) : null;
    const applyScroll = () => {
      panel.style.setProperty(
        "--canvas-scroll",
        `${-(canvas?.scrollTop ?? 0)}px`
      );
    };
    const rafId = window.requestAnimationFrame(() => {
      const result = calculateNotePositions({
        threads,
        selectedNoteId,
        blockRects: store.getBlockRects(),
        heights,
        scrollTop: canvas?.scrollTop ?? 0
      });
      setNotePositions(result.positions);
      applyScroll();
    });
    const view = canvas?.ownerDocument?.defaultView;
    const listenerOptions = { passive: true, capture: true };
    view?.addEventListener("scroll", applyScroll, listenerOptions);
    return () => {
      window.cancelAnimationFrame(rafId);
      view?.removeEventListener("scroll", applyScroll, listenerOptions);
    };
  }, [sidebarRef, heights, isFloating, selectedNoteId, store, threads]);
  return {
    notePositions,
    registerThread: store.registerThread,
    unregisterThread: store.unregisterThread
  };
}
export {
  useEnableFloatingSidebar,
  useFloatingBoard,
  useNoteActions,
  useNoteThreads
};
//# sourceMappingURL=hooks.mjs.map
