import { isDefined } from "ts-is-present";
import { TO_RICOS_DECORATION_DATA_FIELD } from "./../ricos-content/converters/draft/consts.js";
import { Node_Type } from "./../ricos-schema/next-schema/index.js";
import { logInvariant } from "./../_shared/log-invariant.js";
import { UNSUPPORTED_MARK_TYPE, UNSUPPORTED_NODE_TYPE } from "./consts.js";
const collectPmConverters = (plugins) => {
  const markConverters = plugins.flatMap((p) => p.pmMarkConverters).filter(isDefined);
  const marksToDecorations = (marks) => _marksToDecorations(collectFromPmMarkConverters(markConverters), marks);
  const decorationsToMarks = (decorations) => _decorationsToMarks(collectToPmMarkConverters(markConverters), decorations);
  const allNodeConverters = [
    ...plugins.flatMap((p) => p.pmNodeConverters).filter(isDefined),
    createTextConverter({ marksToDecorations, decorationsToMarks })
  ];
  return {
    ...createFromPmConverters(collectFromPmNodeConverters(allNodeConverters)),
    ...createToPmConverters(collectToPmNodeConverters(allNodeConverters)),
    marksToDecorations,
    decorationsToMarks
  };
};
const createFromPmConverters = (converters) => {
  const fromPmNodeChildren = (node) => (node.content || []).map(fromPmNode).filter(isDefined);
  const fromPmNode = (node) => {
    const convertersArr = converters[node.type] === void 0 ? [unsupportedFromPm] : converters[node.type];
    return convertersArr.map((convert) => convert(node, fromPmNodeChildren)).find(isDefined);
  };
  return {
    fromPmNodeChildren,
    fromPmNode,
    fromPm: (content) => ({
      documentStyle: content.attrs?.documentStyle,
      nodes: fromPmNodeChildren(content)
    })
  };
};
const unsupportedFromPm = (node) => {
  if (node.type !== UNSUPPORTED_NODE_TYPE) {
    logInvariant(new Error(`Missing converter for node type ${node.type}`));
    return void 0;
  }
  if (!node.attrs?.originalNode) {
    logInvariant(new Error("Unsupported node without original node"));
    return void 0;
  }
  return node.attrs.originalNode;
};
const createToPmConverters = (converters) => {
  const toPmNodeChildren = (node) => (node.nodes || []).map(toPmNode);
  const toPmNode = (node) => converters[node.type] ? converters[node.type](node, toPmNodeChildren) : {
    type: UNSUPPORTED_NODE_TYPE,
    attrs: {
      originalNode: node,
      id: node.id
    }
  };
  return {
    toPmNodeChildren,
    toPmNode,
    toPm: (content) => {
      const ricosRoot = {
        id: "root",
        type: Node_Type.UNRECOGNIZED,
        nodes: content.nodes
      };
      return {
        type: "doc",
        content: toPmNodeChildren(ricosRoot),
        attrs: {
          documentStyle: content.documentStyle || {}
        }
      };
    }
  };
};
const collectFromPmNodeConverters = (proseNodeConverters) => {
  return proseNodeConverters.reduce((acc, converter) => {
    converter.fromPm.types.forEach((type) => {
      if (!acc[type]) {
        acc[type] = [];
      }
      acc[type].push(converter.fromPm.convert);
    });
    return acc;
  }, {});
};
const collectFromPmMarkConverters = (proseMarkConverters) => Object.fromEntries(
  proseMarkConverters.map((converter) => [converter.fromPm.type, converter.fromPm.convert])
);
const collectToPmMarkConverters = (proseMarkConverter) => proseMarkConverter.reduce(
  (acc, value) => {
    acc[value.toPm.type] = acc[value.toPm.type] || [];
    acc[value.toPm.type].push(value.toPm.convert);
    return acc;
  },
  {}
);
const collectToPmNodeConverters = (proseNodeConverters) => Object.fromEntries(
  proseNodeConverters.flatMap(
    (converter) => converter.toPm.types.map((t) => [t, converter.toPm.convert])
  )
);
const _decorationsToMarks = (converters, decorations) => {
  const marks = decorations.flatMap(
    (decoration) => converters[decoration.type]?.length ? converters[decoration.type].map((converter) => converter(decoration)) : [
      {
        type: UNSUPPORTED_MARK_TYPE,
        attrs: { originalDecorationList: [decoration] }
      }
    ]
  ).filter(isDefined);
  return mergeUnsupportedMarks(marks);
};
const mergeUnsupportedMarks = (marks) => {
  return marks.reduce((acc, mark) => {
    if (mark.type !== UNSUPPORTED_MARK_TYPE) {
      acc.push(mark);
      return acc;
    }
    const existigUnsupportedMark = acc.find((m) => m.type === UNSUPPORTED_MARK_TYPE);
    if (!existigUnsupportedMark) {
      acc.push(mark);
      return acc;
    }
    existigUnsupportedMark.attrs?.originalDecorationList.push(
      ...mark.attrs?.originalDecorationList || []
    );
    return acc;
  }, []);
};
const _marksToDecorations = (converters, marks) => {
  const decorations = marks.flatMap(
    (mark) => converters[mark.type] ? converters[mark.type](mark) : convertUnsupportedMarkToDecoration(mark)
  ).filter(isDefined);
  return mergeSameTypeDecorations(decorations);
};
const mergeSameTypeDecorations = (decorations) => {
  return decorations.sort((a, b) => a.type.localeCompare(b.type)).reduce((acc, decoration) => {
    const lastDecoration = acc[acc.length - 1];
    const dataKey = TO_RICOS_DECORATION_DATA_FIELD[decoration.type];
    if (lastDecoration?.type === decoration.type) {
      lastDecoration[dataKey] = {
        ...lastDecoration[dataKey],
        ...decoration[dataKey]
      };
    } else {
      acc.push(decoration);
    }
    return acc;
  }, []);
};
const convertUnsupportedMarkToDecoration = (mark) => {
  if (!mark.attrs?.originalDecorationList) {
    logInvariant(new Error(`invalid unsupported mark ${mark.type}`));
    return [];
  }
  return mark.attrs.originalDecorationList;
};
const createTextConverter = ({
  marksToDecorations,
  decorationsToMarks
}) => ({
  toPm: {
    types: [Node_Type.TEXT],
    // @ts-expect-error $TSFixMe
    convert: (node) => {
      if (node.textData.text === "\n") {
        return {
          type: "hardBreak",
          text: "\n",
          marks: [],
          attrs: {
            id: ""
          }
        };
      }
      return {
        type: "text",
        text: node.textData.text,
        marks: decorationsToMarks(node.textData.decorations ?? []),
        attrs: {
          id: ""
        }
      };
    }
  },
  fromPm: {
    types: ["hardBreak", "text"],
    convert: (node) => {
      if (node.type === "hardBreak") {
        return {
          type: Node_Type.TEXT,
          id: "",
          nodes: [],
          textData: {
            text: "\n",
            decorations: []
          }
        };
      }
      return {
        type: Node_Type.TEXT,
        id: "",
        nodes: [],
        textData: {
          text: node.text || "",
          decorations: marksToDecorations(node.marks || [])
        }
      };
    }
  }
});
export {
  collectPmConverters
};
//# sourceMappingURL=collect.js.map
