import { matchGlob } from "../../utils/matchGlob.js";
function toFilterSet(list) {
    return Array.isArray(list) && list.length > 0 ? new Set(list) : null;
}
function isKeyVisible(key, redactSet, exposeSet) {
    if (redactSet !== null && redactSet.has(key)) {
        return false;
    }
    if (exposeSet !== null && !exposeSet.has(key)) {
        return false;
    }
    return true;
}
function nodeMatches(pattern, nodeId) {
    if (typeof pattern === 'string') {
        return matchGlob(pattern, nodeId);
    }
    return pattern.some((entry) => matchGlob(entry, nodeId));
}
function filterProps(props, visibility) {
    const redactSet = toFilterSet(visibility.redact);
    const exposeSet = toFilterSet(visibility.expose);
    const filtered = {};
    for (const [key, value] of Object.entries(props)) {
        if (isKeyVisible(key, redactSet, exposeSet)) {
            filtered[key] = value;
        }
    }
    return Object.freeze(filtered);
}
function edgeEndpointVisible(edge, visibility) {
    if (edge.to !== undefined) {
        return nodeMatches(visibility.match, edge.to);
    }
    if (edge.from !== undefined) {
        return nodeMatches(visibility.match, edge.from);
    }
    return true;
}
function visibleEdges(edges, visibility) {
    const visible = [];
    for (const edge of edges) {
        if (edgeEndpointVisible(edge, visibility)) {
            visible.push(Object.freeze({ ...edge }));
        }
    }
    return Object.freeze(visible);
}
function visibleNode(node, visibility) {
    return Object.freeze({
        id: node.id,
        props: filterProps(node.props, visibility),
        edgesOut: visibleEdges(node.edgesOut, visibility),
        edgesIn: visibleEdges(node.edgesIn, visibility),
    });
}
export default class VisibleQueryReadModel {
    #source;
    #visibility;
    constructor(params) {
        this.#source = params.source;
        this.#visibility = params.visibility;
    }
    get stateHash() {
        return this.#source.stateHash;
    }
    async *nodes(request) {
        for await (const node of this.#source.nodes(request)) {
            if (this.#isVisibleNode(node.id)) {
                yield visibleNode(node, this.#visibility);
            }
        }
    }
    async *neighbors(nodeId, options) {
        if (!this.#isVisibleNode(nodeId)) {
            return;
        }
        for await (const entry of this.#source.neighbors(nodeId, options)) {
            if (this.#isVisibleNode(entry.nodeId)) {
                yield Object.freeze({ nodeId: entry.nodeId, label: entry.label });
            }
        }
    }
    async nodeProps(nodeId) {
        if (!this.#isVisibleNode(nodeId)) {
            return null;
        }
        const props = await this.#source.nodeProps(nodeId);
        if (props === null) {
            return null;
        }
        return filterProps(props, this.#visibility);
    }
    #isVisibleNode(nodeId) {
        return nodeMatches(this.#visibility.match, nodeId);
    }
}
