function getLexicalRoot(value) {
    if (null == value) return null;
    let parsed = null;
    if ('string' == typeof value) try {
        parsed = JSON.parse(value);
    } catch  {
        return null;
    }
    else if ('object' == typeof value) parsed = value;
    if (null == parsed) return null;
    if ('root' in parsed && null != parsed.root) return parsed.root;
    return parsed;
}
function* iterAllNodes(node) {
    if (null == node) return;
    yield node;
    if (Array.isArray(node.children)) for (const child of node.children)yield* iterAllNodes(child);
}
async function runLexicalPopulate(options) {
    const { client, readContext, visitors, values } = options;
    const pending = [];
    for (const value of values){
        const root = getLexicalRoot(value);
        if (root) for (const node of iterAllNodes(root))for (const visitor of visitors){
            const item = visitor.match(node);
            if (null != item) pending.push(item);
        }
    }
    if (0 === pending.length) return;
    const idsByCollection = new Map();
    for (const p of pending){
        let bucket = idsByCollection.get(p.collectionPath);
        if (!bucket) {
            bucket = new Set();
            idsByCollection.set(p.collectionPath, bucket);
        }
        bucket.add(p.documentId);
    }
    const fetched = new Map();
    await Promise.all(Array.from(idsByCollection.entries()).map(async ([collectionPath, idSet])=>{
        const ids = Array.from(idSet);
        const result = await client.collection(collectionPath).find({
            where: {
                id: {
                    $in: ids
                }
            },
            pageSize: ids.length,
            _readContext: readContext
        });
        const byId = new Map();
        for (const d of result.docs)if ('string' == typeof d.id) byId.set(d.id, d);
        fetched.set(collectionPath, byId);
    }));
    for (const p of pending){
        const target = fetched.get(p.collectionPath)?.get(p.documentId);
        if (target) p.apply(target);
    }
}
export { getLexicalRoot, iterAllNodes, runLexicalPopulate };
