import { generateId } from "./../../ricos-content/index.js";
import { PmNodeType } from "./../../_shared/pm-node-types.js";
import {
  findParentNodeClosestToPos
} from "./../../tiptap/core/index.js";
import { findNodeById } from "./../../tiptap-editor/helpers/index.js";
import { createRicosNodeExtension } from "./../../tiptap-extensions/index.js";
import {
  DEFAULT_LABEL,
  DEFAULT_STAR_RATING_LABEL,
  MAX_ITEMS,
  SMART_BLOCK_DEFAULTS
} from "../consts.js";
import { getCellColorAttr, hasShapeSupport } from "../utils/variant-features.js";
import { createSmartBlockCellNormalizingPlugin } from "./smart-block-cell-normalizing.js";
import {
  createSmartBlockColorPreviewPlugin,
  SMART_BLOCK_COLOR_PREVIEW_KEY
} from "./smart-block-color-preview-plugin.js";
import { SmartBlock } from "./smart-block.js";
const resolveSmartBlockNodeFromState = (state) => {
  const sel = state.selection;
  if (sel.node?.type?.name === PmNodeType.SMART_BLOCK) {
    return { node: sel.node, pos: sel.from };
  }
  return findParentNodeClosestToPos(
    state.doc.resolve(state.selection.from),
    (node) => node.type?.name === PmNodeType.SMART_BLOCK
  );
};
const createSetSmartBlockAttrCommand = (attrName) => (value) => ({ tr, state, dispatch }) => {
  const smartBlockNode = resolveSmartBlockNodeFromState(state);
  if (!smartBlockNode) {
    return false;
  }
  tr.setNodeAttribute(smartBlockNode.pos, attrName, value);
  if (dispatch) {
    dispatch(tr);
  }
  return true;
};
const createAddSmartBlockCellCommand = (position) => (cellId) => ({ tr, state, dispatch }) => {
  const nodeWithPos = findNodeById(tr, cellId)[0];
  if (!nodeWithPos) {
    return false;
  }
  const parent = findParentNodeClosestToPos(
    tr.doc.resolve(nodeWithPos.pos),
    (node) => node.type?.name === PmNodeType.SMART_BLOCK
  );
  if (parent && parent.node.childCount >= MAX_ITEMS) {
    return false;
  }
  const type = parent?.node?.attrs?.type ?? null;
  const cellType = state.schema.nodes[PmNodeType.SMART_BLOCK_CELL];
  const paragraphType = state.schema.nodes["PARAGRAPH"];
  const uniformDesign = parent?.node ? getUniformCellDesign(parent.node) : null;
  const uniformShape = parent?.node ? getUniformCellShape(parent.node, type) : null;
  const newCell = cellType.create(
    {
      id: generateId(),
      type,
      label: getNewCellLabel(type),
      ...uniformDesign,
      ...uniformShape
    },
    paragraphType.create({ id: generateId() })
  );
  const insertPos = position === "before" ? nodeWithPos.pos : nodeWithPos.pos + nodeWithPos.node.nodeSize;
  tr.insert(insertPos, newCell);
  if (dispatch) {
    dispatch(tr);
  }
  return true;
};
const getNewCellLabel = (type) => {
  if (type === "STAR_RATING") {
    return DEFAULT_STAR_RATING_LABEL;
  }
  if (type === "STATS" || type === "BAR_STATS" || type === "CIRCLE_STATS") {
    return DEFAULT_LABEL;
  }
  return void 0;
};
const CELL_DESIGN_ATTRS = [
  "accentColor",
  "backgroundColor",
  "borderColor",
  "borderWidth",
  "borderRadius"
];
const getUniformCellDesign = (smartBlockNode) => {
  const cells = [];
  smartBlockNode.forEach((child) => {
    if (child.type.name === PmNodeType.SMART_BLOCK_CELL) {
      cells.push(child);
    }
  });
  if (cells.length === 0) {
    return null;
  }
  const first = cells[0].attrs;
  const allSame = cells.every(
    (cell) => CELL_DESIGN_ATTRS.every((attr) => cell.attrs[attr] === first[attr])
  );
  if (!allSame) {
    return null;
  }
  return Object.fromEntries(CELL_DESIGN_ATTRS.map((k) => [k, first[k]]));
};
const getUniformCellShape = (smartBlockNode, type) => {
  if (!type || !hasShapeSupport(type)) {
    return null;
  }
  const cells = [];
  smartBlockNode.forEach((child) => {
    if (child.type.name === PmNodeType.SMART_BLOCK_CELL) {
      cells.push(child);
    }
  });
  if (cells.length === 0) {
    return null;
  }
  const first = cells[0].attrs;
  const allSame = cells.every(
    (cell) => JSON.stringify(cell.attrs.shape) === JSON.stringify(first.shape) && cell.attrs.shapeColor === first.shapeColor
  );
  if (!allSame) {
    return null;
  }
  return { shape: first.shape, shapeColor: first.shapeColor };
};
const smartBlockExtension = createRicosNodeExtension({
  name: PmNodeType.SMART_BLOCK,
  groups: ["custom-styles", "shortcuts-enabled", "node-container"],
  type: "react-node",
  Component: SmartBlock,
  reconfigure: (config, _extensions, _props, settings, services) => {
    return {
      ...config,
      draggable: !services.context.isMobile,
      addOptions: () => {
        return {
          services,
          converters: services.converters,
          ...settings
        };
      }
    };
  },
  createExtensionConfig() {
    return {
      name: this.name,
      isolating: true,
      group: "block",
      selectable: true,
      draggable: true,
      content: `${PmNodeType.SMART_BLOCK_CELL}{1,${MAX_ITEMS}}`,
      parseHTML() {
        return [
          {
            tag: "div[data-smart-block]",
            getAttrs: (node) => ({
              type: node.getAttribute("data-type") || SMART_BLOCK_DEFAULTS.type,
              columnSize: node.getAttribute("data-column-size") || SMART_BLOCK_DEFAULTS.columnSize,
              orientation: node.getAttribute("data-orientation") || null,
              backgroundColor: node.getAttribute("data-background-color") || null,
              borderColor: node.getAttribute("data-border-color") || null,
              borderWidth: node.getAttribute("data-border-width") ? Number(node.getAttribute("data-border-width")) : null,
              borderRadius: node.getAttribute("data-border-radius") ? Number(node.getAttribute("data-border-radius")) : null
            })
          }
        ];
      },
      renderHTML({ node }) {
        return [
          "div",
          {
            "data-smart-block": true,
            "data-type": node.attrs.type,
            "data-column-size": node.attrs.columnSize ?? null,
            "data-orientation": node.attrs.orientation ?? null,
            "data-background-color": node.attrs.backgroundColor ?? null,
            "data-border-color": node.attrs.borderColor ?? null,
            "data-border-width": node.attrs.borderWidth ?? null,
            "data-border-radius": node.attrs.borderRadius ?? null
          },
          0
        ];
      },
      addAttributes: () => ({
        type: {
          default: SMART_BLOCK_DEFAULTS.type
        },
        columnSize: {
          default: SMART_BLOCK_DEFAULTS.columnSize
        },
        orientation: null,
        backgroundColor: null,
        borderColor: null,
        borderWidth: null,
        borderRadius: null
      }),
      addCommands() {
        const converters = this.options.converters;
        return {
          insertSmartBlock: (type) => ({ chain, state }) => {
            const content = [
              createDefaultCell(type),
              createDefaultCell(type),
              createDefaultCell(type)
            ];
            const range = { from: state.selection.from, to: state.selection.to };
            return chain().focus().insertContentAt(range, [
              { type: "PARAGRAPH", attrs: { id: generateId() } },
              {
                type: PmNodeType.SMART_BLOCK,
                attrs: {
                  id: generateId(),
                  type
                },
                content
              },
              { type: "PARAGRAPH", attrs: { id: generateId() } }
            ]).run();
          },
          insertSmartBlockPreset: (content) => ({ chain, state }) => {
            const pmContent = converters.toPm(content).content;
            const range = { from: state.selection.from, to: state.selection.to };
            return chain().focus().insertContentAt(range, pmContent).run();
          },
          switchSmartBlockVariant: (type, options) => ({ tr, state, dispatch }) => {
            const smartBlockNode = resolveSmartBlockNodeFromState(state);
            if (!smartBlockNode) {
              return false;
            }
            tr.setNodeAttribute(smartBlockNode.pos, "type", type);
            const { node: parentNode, pos: parentPos } = smartBlockNode;
            parentNode.forEach((child, offset) => {
              if (child.type.name === PmNodeType.SMART_BLOCK_CELL) {
                const cellPos = parentPos + 1 + offset;
                tr.setNodeAttribute(cellPos, "type", type);
                const isStatVariant = type === "STATS" || type === "CIRCLE_STATS" || type === "BAR_STATS";
                if (isStatVariant && !child.attrs.label) {
                  tr.setNodeAttribute(cellPos, "label", DEFAULT_LABEL);
                } else if (type === "STAR_RATING" && !child.attrs.label) {
                  tr.setNodeAttribute(cellPos, "label", DEFAULT_STAR_RATING_LABEL);
                }
                const oldColorAttr = getCellColorAttr(child.attrs.type);
                const newColorAttr = getCellColorAttr(type);
                if (oldColorAttr !== newColorAttr) {
                  tr.setNodeAttribute(cellPos, newColorAttr, child.attrs[oldColorAttr] ?? null);
                  tr.setNodeAttribute(cellPos, oldColorAttr, null);
                }
              }
            });
            if (options?.isPreview) {
              tr.setMeta("addToHistory", false);
            }
            if (dispatch) {
              dispatch(tr);
            }
            return true;
          },
          setSmartBlockColumnSize: createSetSmartBlockAttrCommand("columnSize"),
          setSmartBlockOrientation: createSetSmartBlockAttrCommand("orientation"),
          applyDesignToAllCells: (design) => ({ tr, state, dispatch }) => {
            const { selection } = state;
            const nodeSelection = selection;
            let smartBlockNode;
            if (nodeSelection.node?.type?.name === PmNodeType.SMART_BLOCK) {
              smartBlockNode = { node: nodeSelection.node, pos: nodeSelection.from };
            } else {
              smartBlockNode = findParentNodeClosestToPos(
                state.doc.resolve(state.selection.from),
                (node) => node.type?.name === PmNodeType.SMART_BLOCK
              );
            }
            if (!smartBlockNode) {
              return false;
            }
            const { node: parentNode, pos: parentPos } = smartBlockNode;
            const type = parentNode.attrs.type;
            const isJoinedBoxes = type === "SOLID_JOINED_BOXES";
            parentNode.forEach((child, offset) => {
              if (child.type.name === PmNodeType.SMART_BLOCK_CELL) {
                const cellPos = parentPos + 1 + offset;
                if ("color" in design) {
                  tr.setNodeAttribute(cellPos, getCellColorAttr(type), design.color);
                }
                if (!isJoinedBoxes) {
                  if (design.borderColor !== void 0) {
                    tr.setNodeAttribute(cellPos, "borderColor", design.borderColor);
                  }
                  if (design.borderWidth !== void 0) {
                    tr.setNodeAttribute(cellPos, "borderWidth", design.borderWidth);
                  }
                  if (design.borderRadius !== void 0) {
                    tr.setNodeAttribute(cellPos, "borderRadius", design.borderRadius);
                  }
                }
              }
            });
            if (isJoinedBoxes) {
              if (design.borderColor !== void 0) {
                tr.setNodeAttribute(parentPos, "borderColor", design.borderColor);
              }
              if (design.borderWidth !== void 0) {
                tr.setNodeAttribute(parentPos, "borderWidth", design.borderWidth);
              }
              if (design.borderRadius !== void 0) {
                tr.setNodeAttribute(parentPos, "borderRadius", design.borderRadius);
              }
            }
            if (dispatch) {
              dispatch(tr);
            }
            return true;
          },
          addSmartBlockCell: (smartBlockId) => ({ tr, state, dispatch }) => {
            let smartBlockNode;
            if (smartBlockId) {
              const nodeWithPos = findNodeById(tr, smartBlockId)[0];
              smartBlockNode = nodeWithPos ? { node: nodeWithPos.node, pos: nodeWithPos.pos } : null;
            } else {
              smartBlockNode = findParentNodeClosestToPos(
                state.doc.resolve(state.selection.from),
                (node) => node.type?.name === PmNodeType.SMART_BLOCK
              );
            }
            if (!smartBlockNode) {
              return false;
            }
            if (smartBlockNode.node.childCount >= MAX_ITEMS) {
              return false;
            }
            const type = smartBlockNode.node.attrs?.type ?? null;
            const cellType = state.schema.nodes[PmNodeType.SMART_BLOCK_CELL];
            const paragraphType = state.schema.nodes["PARAGRAPH"];
            const uniformDesign = getUniformCellDesign(smartBlockNode.node);
            const uniformShape = getUniformCellShape(smartBlockNode.node, type);
            const newCell = cellType.create(
              {
                id: generateId(),
                type,
                label: getNewCellLabel(type),
                ...uniformDesign,
                ...uniformShape
              },
              paragraphType.create({ id: generateId() })
            );
            const insertPos = smartBlockNode.pos + smartBlockNode.node.nodeSize - 1;
            tr.insert(insertPos, newCell);
            if (dispatch) {
              dispatch(tr);
            }
            return true;
          },
          addSmartBlockCellBefore: createAddSmartBlockCellCommand("before"),
          addSmartBlockCellAfter: createAddSmartBlockCellCommand("after"),
          duplicateSmartBlockCell: (cellId) => ({ tr, state, dispatch }) => {
            const nodeWithPos = findNodeById(tr, cellId)[0];
            if (!nodeWithPos) {
              return false;
            }
            const parent = findParentNodeClosestToPos(
              tr.doc.resolve(nodeWithPos.pos),
              (node) => node.type?.name === PmNodeType.SMART_BLOCK
            );
            if (parent && parent.node.childCount >= MAX_ITEMS) {
              return false;
            }
            const json = nodeWithPos.node.toJSON();
            remapNodeIds(json);
            const clone = state.schema.nodeFromJSON(json);
            tr.insert(nodeWithPos.pos + nodeWithPos.node.nodeSize, clone);
            if (dispatch) {
              dispatch(tr);
            }
            return true;
          },
          selectSmartBlock: () => ({ chain, state }) => {
            const result = findParentNodeClosestToPos(
              state.doc.resolve(state.selection.from),
              (node) => node.type?.name === PmNodeType.SMART_BLOCK
            );
            if (!result) {
              return false;
            }
            return chain().setNodeSelection(result.pos).focus(void 0, { scrollIntoView: false }).run();
          },
          applyShapeToAllCells: ({ src }) => ({ tr, state, dispatch }) => {
            const smartBlockNode = resolveSmartBlockNodeFromState(state);
            if (!smartBlockNode) {
              return false;
            }
            const { node: parentNode, pos: parentPos } = smartBlockNode;
            parentNode.forEach((child, offset) => {
              if (child.type.name === PmNodeType.SMART_BLOCK_CELL) {
                const cellPos = parentPos + 1 + offset;
                tr.setNodeAttribute(cellPos, "shape", { src });
              }
            });
            if (dispatch) {
              dispatch(tr);
            }
            return true;
          },
          setShapeColorForAllCells: (color) => ({ tr, state, dispatch }) => {
            const smartBlockNode = resolveSmartBlockNodeFromState(state);
            if (!smartBlockNode) {
              return false;
            }
            const { node: parentNode, pos: parentPos } = smartBlockNode;
            parentNode.forEach((child, offset) => {
              if (child.type.name === PmNodeType.SMART_BLOCK_CELL) {
                const cellPos = parentPos + 1 + offset;
                tr.setNodeAttribute(cellPos, "shapeColor", color);
              }
            });
            if (dispatch) {
              dispatch(tr);
            }
            return true;
          },
          removeShapesFromAllCells: () => ({ tr, state, dispatch }) => {
            const smartBlockNode = resolveSmartBlockNodeFromState(state);
            if (!smartBlockNode) {
              return false;
            }
            const { node: parentNode, pos: parentPos } = smartBlockNode;
            parentNode.forEach((child, offset) => {
              if (child.type.name === PmNodeType.SMART_BLOCK_CELL) {
                const cellPos = parentPos + 1 + offset;
                tr.setNodeAttribute(cellPos, "shape", null);
                tr.setNodeAttribute(cellPos, "shapeColor", null);
              }
            });
            if (dispatch) {
              dispatch(tr);
            }
            return true;
          },
          removeSmartBlockCell: (cellId) => ({ tr, dispatch }) => {
            const nodeWithPos = findNodeById(tr, cellId)[0];
            if (!nodeWithPos) {
              return false;
            }
            tr.delete(nodeWithPos.pos, nodeWithPos.pos + nodeWithPos.node.nodeSize);
            if (dispatch) {
              dispatch(tr);
            }
            return true;
          },
          // Meta-only (no doc steps) so previewing a dragged color never enters
          // the undo history; the single commit on release is the one undo step.
          setSmartBlockColorPreview: (preview) => ({ tr, dispatch }) => {
            dispatch?.(tr.setMeta(SMART_BLOCK_COLOR_PREVIEW_KEY, preview));
            return true;
          },
          clearSmartBlockColorPreview: () => ({ tr, dispatch }) => {
            dispatch?.(tr.setMeta(SMART_BLOCK_COLOR_PREVIEW_KEY, null));
            return true;
          }
        };
      },
      addProseMirrorPlugins() {
        return [createSmartBlockColorPreviewPlugin(), createSmartBlockCellNormalizingPlugin()];
      }
    };
  }
});
const remapNodeIds = (json) => {
  if (json.attrs?.id) {
    json.attrs.id = generateId();
  }
  if (Array.isArray(json.content)) {
    json.content.forEach(remapNodeIds);
  }
};
const createDefaultCell = (type) => ({
  type: PmNodeType.SMART_BLOCK_CELL,
  attrs: {
    id: generateId(),
    type: type ?? null,
    label: getNewCellLabel(type ?? null)
  },
  content: [
    {
      type: "PARAGRAPH",
      attrs: {
        id: generateId()
      },
      content: []
    }
  ]
});
export {
  smartBlockExtension
};
//# sourceMappingURL=smart-block-extension.js.map
