"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildEmptyGraphState = buildEmptyGraphState;
exports.registerLocalRef = registerLocalRef;
exports.registerCanonicalNode = registerCanonicalNode;
exports.appendPendingRelation = appendPendingRelation;
exports.applyGraphOperations = applyGraphOperations;
const RELATION_TYPES = new Set([
    'USES',
    'CONTAINS',
    'REMIXES',
]);
function buildEmptyGraphState() {
    return {
        canonicalNodeByRef: new Map(),
        localAppNodeByName: new Map(),
        localAssetNodeByName: new Map(),
        localPackNodeByNameVersion: new Map(),
        ambiguousAppNames: new Set(),
        ambiguousAssetNames: new Set(),
        ambiguousPackNameVersions: new Set(),
        pendingRelations: [],
    };
}
function registerLocalRef(map, ambiguous, key, nodeId) {
    const normalized = key.trim();
    if (!normalized) {
        return;
    }
    const existing = map.get(normalized);
    if (!existing) {
        map.set(normalized, nodeId);
        return;
    }
    if (existing !== nodeId) {
        ambiguous.add(normalized);
    }
}
function registerCanonicalNode(state, ref, nodeId) {
    const normalized = ref.trim();
    if (!normalized) {
        return;
    }
    state.canonicalNodeByRef.set(normalized, nodeId);
}
function appendPendingRelation(state, fromNodeId, type, toRef, context) {
    state.pendingRelations.push({
        fromNodeId,
        type,
        toRef,
        context,
    });
}
function parseRelationType(raw, context) {
    const normalized = raw.trim().toUpperCase();
    if (!RELATION_TYPES.has(normalized)) {
        throw new Error(`${context}: unsupported relation type "${raw}".`);
    }
    return normalized;
}
function resolveLocalRefNodeId(map, ambiguous, key, context, kindLabel) {
    const normalized = key.trim();
    if (!normalized) {
        throw new Error(`${context}: empty local ${kindLabel} ref.`);
    }
    if (ambiguous.has(normalized)) {
        throw new Error(`${context}: local ${kindLabel} ref "@${kindLabel}/${normalized}" is ambiguous in this upload run.`);
    }
    const nodeId = map.get(normalized);
    if (!nodeId) {
        throw new Error(`${context}: local ${kindLabel} ref "@${kindLabel}/${normalized}" was not produced in this upload run.`);
    }
    return nodeId;
}
function resolveRelationTargetNodeId(state, rawRef, context) {
    const ref = rawRef.trim();
    if (!ref) {
        throw new Error(`${context}: empty relation target ref.`);
    }
    if (ref.startsWith('@app/')) {
        return resolveLocalRefNodeId(state.localAppNodeByName, state.ambiguousAppNames, ref.slice(5), context, 'app');
    }
    if (ref.startsWith('@asset/')) {
        return resolveLocalRefNodeId(state.localAssetNodeByName, state.ambiguousAssetNames, ref.slice(7), context, 'asset');
    }
    if (ref.startsWith('@pack/')) {
        return resolveLocalRefNodeId(state.localPackNodeByNameVersion, state.ambiguousPackNameVersions, ref.slice(6), context, 'pack');
    }
    if (!/^(app|asset|pack):/i.test(ref)) {
        throw new Error(`${context}: invalid relation target ref "${ref}". Use canonical refs (app:/asset:/pack:) or local refs (@app/@asset/@pack).`);
    }
    const nodeId = state.canonicalNodeByRef.get(ref);
    if (!nodeId) {
        throw new Error(`${context}: canonical ref "${ref}" was not resolved in this upload run.`);
    }
    return nodeId;
}
async function applyGraphOperations(client, state) {
    if (state.pendingRelations.length === 0) {
        return;
    }
    const relations = state.pendingRelations.map((row) => ({
        fromNodeId: row.fromNodeId,
        toNodeId: resolveRelationTargetNodeId(state, row.toRef, row.context),
        type: parseRelationType(row.type, row.context),
    }));
    await client.batchUpsertGraphRelations({ relations });
}
