import { createHash } from "node:crypto";
import { existsSync, readFileSync, statSync } from "node:fs";
import { resolve } from "node:path";
const GLB_MAGIC = 0x46546c67;
const JSON_CHUNK_TYPE = 0x4e4f534a;
export function loadProductionAssetManifest(path = "fixtures/asset-corpus/manifest.json") {
    return JSON.parse(readFileSync(resolve(path), "utf8"));
}
export function inspectProductionGlb(path) {
    const data = readFileSync(resolve(path));
    if (data.byteLength < 20 || data.readUInt32LE(0) !== GLB_MAGIC) {
        throw new Error(`Not a GLB file: ${path}`);
    }
    const version = data.readUInt32LE(4);
    const firstChunkLength = data.readUInt32LE(12);
    const firstChunkType = data.readUInt32LE(16);
    if (firstChunkType !== JSON_CHUNK_TYPE) {
        throw new Error(`GLB missing JSON chunk: ${path}`);
    }
    const jsonText = data.toString("utf8", 20, 20 + firstChunkLength).trim();
    const gltf = JSON.parse(jsonText);
    const meshes = gltf.meshes ?? [];
    const materials = gltf.materials ?? [];
    const primitives = meshes.flatMap((mesh) => mesh.primitives ?? []);
    const pbrMaterialCount = materials.filter((material) => material.pbrMetallicRoughness).length;
    const texturedPbrMaterialCount = materials.filter((material) => {
        const pbr = material.pbrMetallicRoughness;
        return Boolean(pbr?.baseColorTexture || pbr?.metallicRoughnessTexture || material.normalTexture || material.occlusionTexture || material.emissiveTexture);
    }).length;
    const advancedMaterialExtensionCount = materials.filter((material) => {
        const extensionNames = Object.keys(material.extensions ?? {});
        return extensionNames.some((name) => /^KHR_materials_/.test(name));
    }).length;
    const extensionsUsed = [...new Set([...(gltf.extensionsUsed ?? []), ...materials.flatMap((material) => Object.keys(material.extensions ?? {}))])].sort();
    const morphTargetPrimitiveCount = primitives.filter((primitive) => (primitive.targets?.length ?? 0) > 0).length;
    return {
        validGlb: true,
        version,
        jsonChunkBytes: firstChunkLength,
        sceneCount: gltf.scenes?.length ?? 0,
        nodeCount: gltf.nodes?.length ?? 0,
        meshCount: meshes.length,
        primitiveCount: primitives.length,
        materialCount: materials.length,
        textureCount: gltf.textures?.length ?? 0,
        imageCount: gltf.images?.length ?? 0,
        animationCount: gltf.animations?.length ?? 0,
        skinCount: gltf.skins?.length ?? 0,
        morphTargetPrimitiveCount,
        extensionCount: extensionsUsed.length,
        extensionsUsed,
        pbrMaterialCount,
        texturedPbrMaterialCount,
        advancedMaterialExtensionCount,
        hasRealSceneComplexity: primitives.length > 0 && (materials.length > 0 || (gltf.animations?.length ?? 0) > 0 || (gltf.skins?.length ?? 0) > 0)
    };
}
export function createProductionAssetCorpusSummary(manifest = loadProductionAssetManifest()) {
    const entries = manifest.assets.map((asset) => {
        const path = resolve(asset.localPath);
        const exists = existsSync(path);
        const bytesMatch = exists && statSync(path).size === asset.bytes;
        const sha256Matches = exists && createHash("sha256").update(readFileSync(path)).digest("hex") === asset.sha256;
        const inspection = exists ? inspectProductionGlb(path) : null;
        return {
            id: asset.id,
            localPath: asset.localPath,
            exists,
            sha256Matches,
            bytesMatch,
            licenseReviewRequired: /review|non-commercial|trademark|SCEA|CC-BY-NC/i.test(asset.license),
            inspection
        };
    });
    const inspections = entries.flatMap((entry) => entry.inspection ? [entry.inspection] : []);
    const pbrTextureAssetCount = entries.filter((entry) => (entry.inspection?.texturedPbrMaterialCount ?? 0) > 0).length;
    const advancedMaterialAssetCount = entries.filter((entry) => (entry.inspection?.advancedMaterialExtensionCount ?? 0) > 0 || /clearcoat|sheen|specular/i.test(manifest.assets.find((asset) => asset.id === entry.id)?.tags.join(" ") ?? "")).length;
    const animationAssetCount = entries.filter((entry) => (entry.inspection?.animationCount ?? 0) > 0 || /animation/i.test(manifest.assets.find((asset) => asset.id === entry.id)?.tags.join(" ") ?? "")).length;
    const totalBytes = manifest.assets.reduce((total, asset) => total + asset.bytes, 0);
    const extensionCoverage = [...new Set(inspections.flatMap((inspection) => inspection.extensionsUsed))].sort();
    const primitiveOnlyRejected = inspections.every((inspection) => inspection.hasRealSceneComplexity);
    const failures = [
        ...(manifest.assets.length < manifest.requirements.minimumRealGlbAssets ? [`asset count ${manifest.assets.length} is below ${manifest.requirements.minimumRealGlbAssets}`] : []),
        ...(entries.filter((entry) => entry.exists).length < manifest.requirements.minimumRealGlbAssets ? ["too few local GLB assets exist"] : []),
        ...(entries.some((entry) => !entry.sha256Matches) ? ["one or more GLB sha256 checks failed"] : []),
        ...(entries.some((entry) => !entry.bytesMatch) ? ["one or more GLB byte-size checks failed"] : []),
        ...(inspections.length < manifest.requirements.minimumRealGlbAssets ? ["too few GLB JSON chunks parsed"] : []),
        ...(manifest.assets.filter((asset) => asset.role.startsWith("flagship")).length < manifest.requirements.minimumVisualFlagshipAssets ? ["too few flagship visual assets"] : []),
        ...(pbrTextureAssetCount < manifest.requirements.minimumPbrTextureAssets ? ["too few textured PBR assets"] : []),
        ...(advancedMaterialAssetCount < manifest.requirements.minimumAdvancedMaterialAssets ? ["too few advanced material extension assets"] : []),
        ...(animationAssetCount < manifest.requirements.minimumAnimationAssets ? ["too few animation assets"] : []),
        ...(totalBytes < manifest.requirements.minimumTotalBytes ? [`total bytes ${totalBytes} is below ${manifest.requirements.minimumTotalBytes}`] : []),
        ...(manifest.requirements.mustRejectPrimitiveOnlyProof && !primitiveOnlyRejected ? ["primitive-only or uninspectable assets are present"] : [])
    ];
    return {
        schema: "a3d-production-runtime-asset-readiness",
        pass: failures.length === 0,
        assetCount: manifest.assets.length,
        existingAssetCount: entries.filter((entry) => entry.exists).length,
        shaVerifiedAssetCount: entries.filter((entry) => entry.sha256Matches).length,
        realGlbParsedCount: inspections.length,
        visualFlagshipAssetCount: manifest.assets.filter((asset) => asset.role.startsWith("flagship")).length,
        totalBytes,
        pbrTextureAssetCount,
        advancedMaterialAssetCount,
        animationAssetCount,
        skinAssetCount: entries.filter((entry) => (entry.inspection?.skinCount ?? 0) > 0).length,
        morphAssetCount: entries.filter((entry) => (entry.inspection?.morphTargetPrimitiveCount ?? 0) > 0).length,
        classCoverage: [...new Set(manifest.assets.map((asset) => asset.class))].sort(),
        extensionCoverage,
        primitiveOnlyRejected,
        failures,
        entries
    };
}
//# sourceMappingURL=ProductionAssetCorpus.js.map