import NeighborProviderPort from "../../../ports/NeighborProviderPort.js";
function compareEdges(a, b) {
    if (a.neighborId < b.neighborId) {
        return -1;
    }
    if (a.neighborId > b.neighborId) {
        return 1;
    }
    if (a.label < b.label) {
        return -1;
    }
    if (a.label > b.label) {
        return 1;
    }
    return 0;
}
function queryDirection(direction) {
    return direction === 'out' ? 'outgoing' : 'incoming';
}
function singleQueryLabel(labels) {
    if (labels === undefined || labels.size !== 1) {
        return null;
    }
    return [...labels][0] ?? null;
}
function queryOptions(direction, labels) {
    const label = singleQueryLabel(labels);
    if (label === null) {
        return { direction };
    }
    return { direction, label };
}
function labelAllowed(entry, labels) {
    return labels === undefined || labels.has(entry.label);
}
function toNeighborEdge(entry) {
    return Object.freeze({
        neighborId: entry.nodeId,
        label: entry.label,
    });
}
function edgeKey(edge) {
    return `${edge.neighborId}\0${edge.label}`;
}
function sorted(edges) {
    return [...edges].sort(compareEdges);
}
export default class QueryReadModelNeighborProvider extends NeighborProviderPort {
    #readModel;
    constructor(readModel) {
        super();
        this.#readModel = readModel;
    }
    async getNeighbors(nodeId, direction, options) {
        if (direction === 'both') {
            return await this.#bothNeighbors(nodeId, options?.labels);
        }
        return await this.#directionalNeighbors(nodeId, queryDirection(direction), options?.labels);
    }
    async hasNode(nodeId) {
        return await this.#readModel.nodeProps(nodeId) !== null;
    }
    async #bothNeighbors(nodeId, labels) {
        const deduped = new Map();
        for (const edge of await this.#directionalNeighbors(nodeId, 'outgoing', labels)) {
            deduped.set(edgeKey(edge), edge);
        }
        for (const edge of await this.#directionalNeighbors(nodeId, 'incoming', labels)) {
            if (!deduped.has(edgeKey(edge))) {
                deduped.set(edgeKey(edge), edge);
            }
        }
        return sorted([...deduped.values()]);
    }
    async #directionalNeighbors(nodeId, direction, labels) {
        const edges = [];
        for await (const entry of this.#readModel.neighbors(nodeId, queryOptions(direction, labels))) {
            if (labelAllowed(entry, labels)) {
                edges.push(toNeighborEdge(entry));
            }
        }
        return sorted(edges);
    }
}
