import { asRecord } from '../internal/utils.js';
function normalizeString(value) {
    return typeof value === 'string' ? value.trim() : '';
}
function normalizeNumber(value) {
    const parsed = Number(value);
    return Number.isFinite(parsed) ? parsed : undefined;
}
function normalizeAssetIntegrity(value) {
    const record = asRecord(value);
    const entries = Object.entries(record).flatMap(([key, raw]) => {
        const item = asRecord(raw);
        const sha256 = normalizeString(item.sha256).replace(/^sha256:/i, '').toLowerCase();
        const provenanceRef = normalizeString(item.provenanceRef ?? item.provenance_ref);
        const verificationState = normalizeString(item.verificationState ?? item.verification_state);
        if (!key || !/^[a-f0-9]{64}$/u.test(sha256) || !provenanceRef || verificationState !== 'digest-verified') {
            return [];
        }
        return [[key, { sha256, provenanceRef, verificationState: 'digest-verified' }]];
    });
    return entries.length > 0 ? Object.fromEntries(entries) : undefined;
}
export function normalizeWorldInspectVector(value) {
    const record = asRecord(value);
    const x = normalizeNumber(record.x);
    const y = normalizeNumber(record.y);
    const z = normalizeNumber(record.z);
    if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(z)) {
        return undefined;
    }
    return {
        x: Number(x),
        y: Number(y),
        z: Number(z),
    };
}
export function normalizeWorldInspectViewPreset(value) {
    const record = asRecord(value);
    const camera = asRecord(record.camera);
    const position = normalizeWorldInspectVector(camera.position);
    const target = normalizeWorldInspectVector(camera.target);
    const mode = normalizeString(record.mode);
    const source = normalizeString(record.source);
    if (!position || !target || mode !== 'inspect') {
        return undefined;
    }
    if (source !== 'manual' && source !== 'auto-collider' && source !== 'auto-splat') {
        return undefined;
    }
    return {
        version: Number(record.version || 1),
        mode: 'inspect',
        source,
        camera: {
            position,
            target,
        },
    };
}
export function normalizeWorldFixturePackage(value) {
    const record = asRecord(value);
    if (Object.keys(record).length === 0) {
        return null;
    }
    const spzUrlsRaw = asRecord(record.spzUrls);
    const semanticsRaw = asRecord(record.semanticsMetadata);
    const artifacts = Array.isArray(record.artifacts)
        ? record.artifacts.filter((item) => Boolean(item) && typeof item === 'object')
        : [];
    const normalized = {
        manifestPath: normalizeString(record.manifestPath),
        worldId: normalizeString(record.worldId),
        displayName: normalizeString(record.displayName),
        worldMarbleUrl: normalizeString(record.worldMarbleUrl),
        caption: normalizeString(record.caption),
        thumbnailUrl: normalizeString(record.thumbnailUrl),
        panoUrl: normalizeString(record.panoUrl),
        colliderMeshUrl: normalizeString(record.colliderMeshUrl),
        spzUrls: Object.fromEntries(Object.entries(spzUrlsRaw)
            .map(([key, url]) => [key, normalizeString(url)])
            .filter(([, url]) => Boolean(url))),
        semanticsMetadata: Object.keys(semanticsRaw).length > 0
            ? {
                ...(normalizeNumber(semanticsRaw.groundPlaneOffset ?? semanticsRaw.ground_plane_offset) != null
                    ? { groundPlaneOffset: normalizeNumber(semanticsRaw.groundPlaneOffset ?? semanticsRaw.ground_plane_offset) }
                    : {}),
                ...(normalizeNumber(semanticsRaw.metricScaleFactor ?? semanticsRaw.metric_scale_factor) != null
                    ? { metricScaleFactor: normalizeNumber(semanticsRaw.metricScaleFactor ?? semanticsRaw.metric_scale_factor) }
                    : {}),
            }
            : undefined,
        model: normalizeString(record.model),
        spzLocalPath: normalizeString(record.spzLocalPath),
        thumbnailLocalPath: normalizeString(record.thumbnailLocalPath),
        panoLocalPath: normalizeString(record.panoLocalPath),
        colliderMeshLocalPath: normalizeString(record.colliderMeshLocalPath),
        assetIntegrity: normalizeAssetIntegrity(record.assetIntegrity),
        viewerPreset: normalizeWorldInspectViewPreset(record.viewerPreset),
        artifacts,
    };
    const hasMeaningfulPayload = Boolean(normalized.manifestPath
        || normalized.worldId
        || normalized.displayName
        || normalized.worldMarbleUrl
        || normalized.caption
        || normalized.thumbnailUrl
        || normalized.panoUrl
        || normalized.colliderMeshUrl
        || normalized.model
        || normalized.spzLocalPath
        || normalized.thumbnailLocalPath
        || normalized.panoLocalPath
        || normalized.colliderMeshLocalPath
        || (normalized.assetIntegrity && Object.keys(normalized.assetIntegrity).length > 0)
        || normalized.viewerPreset
        || (normalized.spzUrls && Object.keys(normalized.spzUrls).length > 0)
        || (normalized.artifacts && normalized.artifacts.length > 0)
        || (normalized.semanticsMetadata
            && (normalized.semanticsMetadata.groundPlaneOffset != null
                || normalized.semanticsMetadata.metricScaleFactor != null)));
    return hasMeaningfulPayload ? normalized : null;
}
export function worldFixtureFromResolvedPaths(input) {
    return normalizeWorldFixturePackage({
        manifestPath: input.manifestPath,
        worldId: input.worldId,
        displayName: input.displayName,
        model: input.model,
        caption: input.caption,
        worldMarbleUrl: input.worldMarbleUrl,
        spzUrls: input.spzRemoteUrl ? { cached: input.spzRemoteUrl } : {},
        thumbnailUrl: input.thumbnailRemoteUrl,
        panoUrl: input.panoRemoteUrl,
        colliderMeshUrl: input.colliderMeshRemoteUrl,
        spzLocalPath: input.spzLocalPath,
        thumbnailLocalPath: input.thumbnailLocalPath,
        panoLocalPath: input.panoLocalPath,
        colliderMeshLocalPath: input.colliderMeshLocalPath,
        assetIntegrity: input.assetIntegrity,
        semanticsMetadata: input.semanticsMetadata,
        viewerPreset: input.viewerPreset,
    });
}
export function pickWorldFixturePreviewSpzUrl(fixture) {
    const urls = fixture?.spzUrls || {};
    for (const key of ['full_res', 'cached', 'default', 'preview']) {
        const normalized = normalizeString(urls[key]);
        if (normalized) {
            return normalized;
        }
    }
    for (const value of Object.values(urls)) {
        const normalized = normalizeString(value);
        if (normalized) {
            return normalized;
        }
    }
    return '';
}
export function resolveWorldFixtureTitle(fixture) {
    return fixture?.displayName || fixture?.worldId || fixture?.manifestPath || 'Cached world fixture';
}
export const fixture = {
    normalize: normalizeWorldFixturePackage,
    fromResolvedPaths: worldFixtureFromResolvedPaths,
    pickPreviewSpzUrl: pickWorldFixturePreviewSpzUrl,
    title: resolveWorldFixtureTitle,
};
//# sourceMappingURL=fixture.js.map