import type { Node, NodeId, NodeTree } from '@craftjs/core';

/**
 * Tiny shared clipboard for canvas node copy/paste.
 *
 * Both the context menu (right-click Copy/Paste) and the keyboard shortcuts
 * hook (Ctrl/Cmd+C / Ctrl/Cmd+V) read and write this single module-level
 * store, so copying a node via one entry point and pasting via the other
 * behaves consistently instead of each maintaining its own clipboard.
 *
 * Deliberately not React state -- nothing in the UI needs to re-render
 * reactively when the clipboard changes; consumers just read the current
 * value at the moment they need it (on paste, or when a menu opens).
 *
 * Historical bug (cross-page copy/paste): this used to store only the copied
 * node's bare id (`clipboardNodeId`) and re-resolve it via `query.node(id)`
 * at paste time. That works fine same-page, but the moment the user switches
 * pages the canvas is re-deserialized to the target page's Craft.js state --
 * the copied id no longer exists in `query` at all -- so a cross-page paste
 * silently no-op'd (or threw, caught, and swallowed). Storing a detached
 * TREE SNAPSHOT at copy time instead means paste never needs to look the
 * source id up again: it just hands the snapshot to `regenerateTreeIds` +
 * `actions.addNodeTree`, which works identically regardless of which page's
 * state is currently loaded on the canvas.
 */
let clipboardTree: NodeTree | null = null;

/**
 * Deep, detached clone of a live Craft.js `NodeTree` (as returned by
 * `query.node(id).toNodeTree()`).
 *
 * Not a plain `structuredClone(tree)`: for a REAL (live) Craft.js node,
 * `data.type` is the actual component function/class reference (not a
 * serializable `{resolvedName}` wrapper) -- `structuredClone` cannot clone a
 * function and throws `DataCloneError` (see the identical note on
 * `regenerateTreeIds` in `utils/craft-tree.ts`, which hit this exact bug
 * historically). `type` is a stable reference shared by every node of that
 * component across the whole app (it doesn't change per page), so it's safe
 * to keep by reference -- only the mutable per-node data (`props`, `custom`,
 * `nodes`, `linkedNodes`) needs an actual deep copy so a later mutation (a
 * subsequent paste's `setProp`, or a fresh copy of the same live node)
 * can never reach back into this stored snapshot.
 */
function cloneNodeTree(tree: NodeTree): NodeTree {
  const nodes: Record<NodeId, Node> = {};
  for (const [id, node] of Object.entries(tree.nodes)) {
    nodes[id] = {
      ...node,
      data: {
        ...node.data,
        props: structuredClone(node.data.props),
        custom: structuredClone(node.data.custom),
        nodes: [...(node.data.nodes || [])],
        linkedNodes: { ...(node.data.linkedNodes || {}) },
      },
    };
  }
  return { rootNodeId: tree.rootNodeId, nodes };
}

/**
 * Returns the tree snapshot currently on the clipboard, or null if empty.
 * The returned tree is safe to hand straight to `regenerateTreeIds` --
 * `regenerateTreeIds` never mutates its input, so repeated pastes of the
 * same clipboard contents (including across a page switch) all work off the
 * same untouched snapshot.
 */
export function getClipboardTree(): NodeTree | null {
  return clipboardTree;
}

/**
 * Sets (or clears, with `null`) the tree snapshot on the clipboard. The tree
 * is deep-cloned before being stored (see `cloneNodeTree`) so it is fully
 * detached from the live Craft.js node it was captured from -- it survives
 * that node being deleted, mutated, or (the whole point) the canvas being
 * re-deserialized to a different page entirely.
 */
export function setClipboardTree(tree: NodeTree | null): void {
  clipboardTree = tree ? cloneNodeTree(tree) : null;
}
