import QueryError from "../../errors/QueryError.js";
const GRAPH_DIFF_VERSION = 'graph-diff/v1';
/** First-class graph delta result over two resolved coordinates. */
export default class GraphDiff {
    diffVersion = GRAPH_DIFF_VERSION;
    comparisonDigest;
    left;
    right;
    scope;
    changed;
    summary;
    nodes;
    edges;
    nodeProperties;
    edgeProperties;
    visiblePatchDivergence;
    constructor(fields) {
        const comparison = requireComparison(requireFields(fields).comparison);
        this.comparisonDigest = comparison.comparisonDigest;
        this.left = copySide(comparison.left);
        this.right = copySide(comparison.right);
        this.scope = comparison.scope !== undefined ? copyScope(comparison.scope) : undefined;
        this.changed = comparison.visibleState.changed;
        this.summary = copySummary(comparison.visibleState.summary);
        this.nodes = copyNodes(comparison.visibleState.nodes);
        this.edges = copyEdges(comparison.visibleState.edges);
        this.nodeProperties = copyNodeProperties(comparison.visibleState.nodeProperties);
        this.edgeProperties = copyEdgeProperties(comparison.visibleState.edgeProperties);
        this.visiblePatchDivergence = copyPatchDivergence(comparison.visiblePatchDivergence);
        Object.freeze(this);
    }
}
function requireFields(fields) {
    if (fields === null || fields === undefined) {
        throw new QueryError('GraphDiff fields must be provided', {
            code: 'E_GRAPH_DIFF',
        });
    }
    return fields;
}
function requireComparison(comparison) {
    if (comparison === null || typeof comparison !== 'object') {
        throw new QueryError('GraphDiff requires a coordinate comparison', {
            code: 'E_GRAPH_DIFF',
        });
    }
    return comparison;
}
function freezeObject(value) {
    Object.freeze(value);
    return value;
}
function copyStringArray(values) {
    return freezeObject([...values]);
}
function copySide(side) {
    const resolved = side.resolved.strand === undefined
        ? {
            ...side.resolved,
            patchFrontier: freezeObject({ ...side.resolved.patchFrontier }),
            lamportFrontier: freezeObject({ ...side.resolved.lamportFrontier }),
            summary: freezeObject({ ...side.resolved.summary }),
        }
        : {
            ...side.resolved,
            patchFrontier: freezeObject({ ...side.resolved.patchFrontier }),
            lamportFrontier: freezeObject({ ...side.resolved.lamportFrontier }),
            summary: freezeObject({ ...side.resolved.summary }),
            strand: freezeObject({
                ...side.resolved.strand,
                braid: freezeObject({
                    readOverlayCount: side.resolved.strand.braid.readOverlayCount,
                    braidedStrandIds: copyStringArray(side.resolved.strand.braid.braidedStrandIds),
                }),
            }),
        };
    return freezeObject({
        requested: freezeObject({ ...side.requested }),
        resolved: freezeObject(resolved),
    });
}
function copyScope(scope) {
    return freezeObject({
        ...(scope.nodeIdPrefixes !== undefined
            ? {
                nodeIdPrefixes: freezeObject({
                    ...(scope.nodeIdPrefixes.include !== undefined
                        ? { include: copyStringArray(scope.nodeIdPrefixes.include) }
                        : {}),
                    ...(scope.nodeIdPrefixes.exclude !== undefined
                        ? { exclude: copyStringArray(scope.nodeIdPrefixes.exclude) }
                        : {}),
                }),
            }
            : {}),
    });
}
function copySummary(summary) {
    return freezeObject({
        left: freezeObject({ ...summary.left }),
        right: freezeObject({ ...summary.right }),
        nodes: freezeObject({ ...summary.nodes }),
        edges: freezeObject({ ...summary.edges }),
        nodeProperties: freezeObject({ ...summary.nodeProperties }),
        edgeProperties: freezeObject({ ...summary.edgeProperties }),
    });
}
function copyNodes(nodes) {
    return freezeObject({
        added: copyStringArray(nodes.added),
        removed: copyStringArray(nodes.removed),
    });
}
function copyObjectArray(values) {
    return freezeObject(values.map((value) => freezeObject({ ...value })));
}
function copyEdges(edges) {
    return freezeObject({
        added: copyObjectArray(edges.added),
        removed: copyObjectArray(edges.removed),
    });
}
function copyNodeProperties(delta) {
    return freezeObject({
        added: copyObjectArray(delta.added),
        removed: copyObjectArray(delta.removed),
        changed: copyObjectArray(delta.changed),
    });
}
function copyEdgeProperties(delta) {
    return freezeObject({
        added: copyObjectArray(delta.added),
        removed: copyObjectArray(delta.removed),
        changed: copyObjectArray(delta.changed),
    });
}
function copyPatchDivergence(divergence) {
    const copied = divergence.target === undefined
        ? {
            sharedCount: divergence.sharedCount,
            leftOnlyCount: divergence.leftOnlyCount,
            rightOnlyCount: divergence.rightOnlyCount,
            leftOnlyPatchShas: copyStringArray(divergence.leftOnlyPatchShas),
            rightOnlyPatchShas: copyStringArray(divergence.rightOnlyPatchShas),
        }
        : {
            sharedCount: divergence.sharedCount,
            leftOnlyCount: divergence.leftOnlyCount,
            rightOnlyCount: divergence.rightOnlyCount,
            leftOnlyPatchShas: copyStringArray(divergence.leftOnlyPatchShas),
            rightOnlyPatchShas: copyStringArray(divergence.rightOnlyPatchShas),
            target: freezeObject({
                ...divergence.target,
                leftOnlyPatchShas: copyStringArray(divergence.target.leftOnlyPatchShas),
                rightOnlyPatchShas: copyStringArray(divergence.target.rightOnlyPatchShas),
            }),
        };
    return freezeObject(copied);
}
