import { ValidationError } from "../core/index.js";
export class SceneMetadata {
    deterministicSeed;
    assets = [];
    materialAssignments = [];
    plans = [];
    capabilities = [];
    revisions = [];
    constructor(options = {}) {
        this.replace(options);
    }
    replace(options) {
        this.deterministicSeed = options.deterministicSeed;
        this.assets.length = 0;
        this.materialAssignments.length = 0;
        this.plans.length = 0;
        this.capabilities.length = 0;
        this.revisions.length = 0;
        for (const asset of options.assets ?? [])
            this.registerAsset(asset);
        for (const assignment of options.materialAssignments ?? [])
            this.assignMaterial(assignment);
        for (const plan of options.plans ?? [])
            this.addPlan(plan);
        for (const capability of options.capabilities ?? [])
            this.discloseCapability(capability);
        for (const revision of options.revisions ?? [])
            this.recordRevision(revision);
        return this;
    }
    clone() {
        return new SceneMetadata(this.toJSON());
    }
    get isEmpty() {
        return this.deterministicSeed === undefined
            && this.assets.length === 0
            && this.materialAssignments.length === 0
            && this.plans.length === 0
            && this.capabilities.length === 0
            && this.revisions.length === 0;
    }
    registerAsset(asset) {
        requireNonEmpty(asset.id, "asset id");
        upsertByKey(this.assets, cloneAsset(asset), (candidate) => candidate.id === asset.id);
        return this;
    }
    assignMaterial(assignment) {
        requireNonEmpty(assignment.nodeId, "material assignment nodeId");
        requireNonEmpty(assignment.materialId, "material assignment materialId");
        const cloned = cloneMaterialAssignment(assignment);
        upsertByKey(this.materialAssignments, cloned, (candidate) => candidate.nodeId === cloned.nodeId && candidate.slot === cloned.slot);
        return this;
    }
    addPlan(plan) {
        requireNonEmpty(plan.id, "plan id");
        requireNonEmpty(plan.description, "plan description");
        upsertByKey(this.plans, clonePlan(plan), (candidate) => candidate.id === plan.id);
        return this;
    }
    discloseCapability(disclosure) {
        requireNonEmpty(disclosure.feature, "capability feature");
        upsertByKey(this.capabilities, cloneCapability(disclosure), (candidate) => candidate.feature === disclosure.feature);
        return this;
    }
    discloseUnsupportedFeature(feature, reason, options = {}) {
        return this.discloseCapability({ ...options, feature, reason, status: "unsupported" });
    }
    recordRevision(revision) {
        requireNonEmpty(revision.summary, "revision summary");
        const id = revision.id ?? `revision-${this.revisions.length + 1}`;
        upsertByKey(this.revisions, cloneRevision({ ...revision, id }), (candidate) => candidate.id === id);
        return this;
    }
    unsupportedFeatures() {
        return this.capabilities.filter((capability) => capability.status === "unsupported");
    }
    toJSON() {
        return {
            ...(this.deterministicSeed !== undefined ? { deterministicSeed: this.deterministicSeed } : {}),
            ...(this.assets.length > 0 ? { assets: this.assets.map(cloneAsset) } : {}),
            ...(this.materialAssignments.length > 0 ? { materialAssignments: this.materialAssignments.map(cloneMaterialAssignment) } : {}),
            ...(this.plans.length > 0 ? { plans: this.plans.map(clonePlan) } : {}),
            ...(this.capabilities.length > 0 ? { capabilities: this.capabilities.map(cloneCapability) } : {}),
            ...(this.revisions.length > 0 ? { revisions: this.revisions.map(cloneRevision) } : {})
        };
    }
}
function requireNonEmpty(value, label) {
    if (!value || value.trim().length === 0) {
        throw new ValidationError("SCENE_METADATA", `Scene metadata ${label} must be a non-empty string.`);
    }
}
function upsertByKey(items, value, matches) {
    const index = items.findIndex(matches);
    if (index >= 0)
        items[index] = value;
    else
        items.push(value);
}
function cloneAsset(asset) {
    return {
        ...asset,
        ...(asset.nodeIds ? { nodeIds: [...asset.nodeIds] } : {}),
        ...(asset.materialIds ? { materialIds: [...asset.materialIds] } : {})
    };
}
function cloneMaterialAssignment(assignment) {
    return {
        ...assignment,
        ...(assignment.unsupportedFeatures ? { unsupportedFeatures: [...assignment.unsupportedFeatures] } : {})
    };
}
function clonePlan(plan) {
    return {
        ...plan,
        ...(plan.nodeIds ? { nodeIds: [...plan.nodeIds] } : {}),
        ...(plan.unsupportedFeatures ? { unsupportedFeatures: [...plan.unsupportedFeatures] } : {}),
        ...(plan.evidence ? { evidence: [...plan.evidence] } : {})
    };
}
function cloneCapability(capability) {
    return {
        ...capability,
        ...(capability.evidence ? { evidence: [...capability.evidence] } : {})
    };
}
function cloneRevision(revision) {
    return { ...revision, id: revision.id ?? "revision-1" };
}
//# sourceMappingURL=SceneMetadata.js.map