/**
 * Extracts metadata from a ProseMirror document node
 *
 * This function traverses the entire document tree and collects information about
 * special elements like images and links.
 *
 * @param doc - The ProseMirror document node to extract metadata from
 * @returns A metadata object containing arrays of images and links found in the document
 */
export function getMetadataFromDoc(doc) {
    const metadata = { images: [], links: [] };
    doc.descendants((node) => {
        if (isImageNode(node)) {
            metadata.images.push(extractImageMetadata(node));
        }
        else if (isTextNodeWithMarks(node)) {
            for (const link of extractLinkMetadata(node))
                metadata.links.push(link);
        }
        return true;
    });
    return metadata;
}
function isImageNode(node) {
    return node.type.name === 'image' && !!node.attrs;
}
function extractImageMetadata(node) {
    return {
        src: node.attrs.src,
        state: node.attrs.state,
        fileInfoId: node.attrs.fileInfoId,
    };
}
function isTextNodeWithMarks(node) {
    var _a;
    return node.isText && ((_a = node.marks) === null || _a === void 0 ? void 0 : _a.length) > 0;
}
function extractLinkMetadata(node) {
    return node.marks
        .filter((mark) => mark.type.name === 'link' && mark.attrs)
        .map((mark) => ({
        href: mark.attrs.href,
        text: node.text,
    }));
}
/**
 * Determines if metadata has changed between two states
 * Handles duplicates correctly but is order-insensitive
 *
 * @param oldMetadata - The previous metadata state to compare against
 * @param newMetadata - The current metadata state
 * @returns True if there are any differences between the metadata objects, false otherwise
 */
export function hasMetadataChanged(oldMetadata, newMetadata) {
    return (hasDifferentLengths(oldMetadata, newMetadata) ||
        hasDifferentLinks(oldMetadata.links, newMetadata.links) ||
        hasDifferentImages(oldMetadata.images, newMetadata.images));
}
function hasDifferentLengths(oldMetadata, newMetadata) {
    return (oldMetadata.images.length !== newMetadata.images.length ||
        oldMetadata.links.length !== newMetadata.links.length);
}
function hasDifferentLinks(oldLinks, newLinks) {
    const oldLinkCounts = getLinkFrequencyMap(oldLinks);
    const newLinkCounts = getLinkFrequencyMap(newLinks);
    return !areFrequencyMapsEqual(oldLinkCounts, newLinkCounts);
}
function hasDifferentImages(oldImages, newImages) {
    const oldImageCounts = getImageFrequencyMap(oldImages);
    const newImageCounts = getImageFrequencyMap(newImages);
    return !areFrequencyMapsEqual(oldImageCounts, newImageCounts);
}
/**
 * Creates a frequency map for images based on their key properties
 * @param images
 */
function getImageFrequencyMap(images) {
    const countMap = new Map();
    for (const image of images) {
        const key = `${image.fileInfoId}|${image.state}|${image.src}`;
        countMap.set(key, (countMap.get(key) || 0) + 1);
    }
    return countMap;
}
/**
 * Creates a frequency map for links based on their key properties
 * @param links
 */
function getLinkFrequencyMap(links) {
    const countMap = new Map();
    for (const link of links) {
        const key = `${link.href}|${link.text}`;
        countMap.set(key, (countMap.get(key) || 0) + 1);
    }
    return countMap;
}
/**
 * Compares two frequency maps for equality
 * @param map1
 * @param map2
 */
function areFrequencyMapsEqual(map1, map2) {
    if (map1.size !== map2.size) {
        return false;
    }
    for (const [key, count] of map1.entries()) {
        if (map2.get(key) !== count) {
            return false;
        }
    }
    return true;
}
