/**
 * EdgeAdd — adds a directed edge to the graph with a causal dot.
 */
import PatchError from "../../errors/PatchError.js";
import { Dot } from "../../crdt/Dot.js";
import Op from "./Op.js";
import { OP_SCOPE_BOTH } from "./OpScope.js";
import { assertNonEmptyString, assertNoReservedBytes } from "./validate.js";
import { compareEventIds } from "../../utils/EventId.js";
import { encodeEdgeKey } from "../../services/KeyCodec.js";
import ReceiptBuilder from "../../services/ReceiptBuilder.js";
export default class EdgeAdd extends Op {
    receiptName = 'EdgeAdd';
    from;
    to;
    label;
    dot;
    constructor({ from, to, label, dot }) {
        super('EdgeAdd', OP_SCOPE_BOTH);
        assertNonEmptyString(from, 'EdgeAdd', 'from');
        assertNonEmptyString(to, 'EdgeAdd', 'to');
        assertNonEmptyString(label, 'EdgeAdd', 'label');
        assertNoReservedBytes(from, 'EdgeAdd', 'from');
        assertNoReservedBytes(to, 'EdgeAdd', 'to');
        assertNoReservedBytes(label, 'EdgeAdd', 'label');
        if (!(dot instanceof Dot)) {
            throw new PatchError('EdgeAdd requires dot to be a Dot instance');
        }
        this.from = from;
        this.to = to;
        this.label = label;
        this.dot = dot;
        Object.freeze(this);
    }
    validate() { }
    mutate(state, eventId) {
        const edgeKey = encodeEdgeKey(this.from, this.to, this.label);
        state.edgeAlive.add(edgeKey, this.dot);
        const prev = state.edgeBirthEvent.get(edgeKey);
        if (prev === undefined || compareEventIds(eventId, prev) > 0) {
            state.edgeBirthEvent.set(edgeKey, eventId);
        }
    }
    outcome(state) {
        const edgeKey = encodeEdgeKey(this.from, this.to, this.label);
        return ReceiptBuilder.edgeAddOutcome(state.edgeAlive, { dot: this.dot }, edgeKey);
    }
    snapshot(state) {
        const ek = encodeEdgeKey(this.from, this.to, this.label);
        return { edgeWasAlive: state.edgeAlive.contains(ek), edgeKey: ek };
    }
    accumulate(diff, state, before) {
        if (before.edgeWasAlive !== true && before.edgeKey !== undefined && state.edgeAlive.contains(before.edgeKey)) {
            diff.edgesAdded.push({ from: this.from, to: this.to, label: this.label });
        }
    }
}
