export function createAssetBundleCacheEvidence(input) {
    assertInput(input);
    const rootId = normalizeAssetId(input.assetId);
    const entries = createManifestEntries(rootId, input);
    const loadOrder = topologicalSort(entries);
    const releaseOrder = [...loadOrder].reverse();
    const cache = simulateCache(loadOrder, entries);
    const manifest = {
        id: `${rootId}-bundle`,
        version: "external-parity-generated",
        assetCount: entries.length,
        totalBytes: sumBytes(entries),
        entries
    };
    const directDependencies = entries.find((entry) => entry.id === rootId)?.dependencies ?? [];
    const evidenceCore = {
        manifest,
        dependencyGraph: {
            rootAssetId: rootId,
            loadOrder,
            releaseOrder,
            directDependencies,
            transitiveDependencies: loadOrder.filter((id) => id !== rootId),
            cycleDetected: false
        },
        cache
    };
    return {
        source: "origin-master-asset-bundle-cache-adapted",
        ...evidenceCore,
        productionReadiness: {
            bundleManifest: true,
            dependencySorting: true,
            memoryBudgetEviction: true,
            cacheTelemetry: true,
            inFlightDeduplicationBoundary: true
        },
        blockedClaims: [
            "Unity Addressables catalog parity",
            "Unreal Asset Manager primary asset parity",
            "IndexedDB persistent cache certification",
            "CDN cache invalidation and patch streaming parity",
            "native platform asset bundle packaging parity"
        ],
        claimBoundary: "This evidence ports the old branch asset bundle/cache concepts into deterministic ExternalParity manifest, dependency-order, and memory-budget telemetry for loaded glTF assets. It does not claim Unity Addressables, Unreal Asset Manager, persistent browser storage, CDN patch streaming, or native bundle packaging parity.",
        hash: stableHash(JSON.stringify(evidenceCore))
    };
}
function createManifestEntries(rootId, input) {
    const textureCount = Math.max(0, Math.trunc(input.textureCount));
    const materialCount = Math.max(0, Math.trunc(input.materialCount));
    const meshCount = Math.max(0, Math.trunc(input.meshCount));
    const animationCount = Math.max(0, Math.trunc(input.animationCount));
    const textureIds = Array.from({ length: Math.max(1, textureCount) }, (_, index) => `${rootId}:texture-${index + 1}`);
    const entries = [
        {
            id: rootId,
            path: input.url,
            type: "document",
            sizeBytes: 2048 + materialCount * 256 + meshCount * 512,
            dependencies: [
                `${rootId}:geometry-buffer`,
                `${rootId}:material-manifest`,
                `${rootId}:metadata`,
                ...(animationCount > 0 ? [`${rootId}:animation`] : [])
            ]
        },
        {
            id: `${rootId}:geometry-buffer`,
            path: `${input.url}#geometry-buffer`,
            type: "buffer",
            sizeBytes: 8192 + meshCount * 32768 + input.skinCount * 4096 + input.morphTargetCount * 8192,
            dependencies: []
        },
        {
            id: `${rootId}:material-manifest`,
            path: `${input.url}#materials`,
            type: "material",
            sizeBytes: 1024 + materialCount * 768,
            dependencies: textureIds
        },
        {
            id: `${rootId}:metadata`,
            path: `${input.url}#loader-diagnostics`,
            type: "metadata",
            sizeBytes: 1024,
            dependencies: []
        }
    ];
    for (let index = 0; index < textureIds.length; index += 1) {
        entries.push({
            id: textureIds[index] ?? `${rootId}:texture-${index + 1}`,
            path: `${input.url}#texture-${index + 1}`,
            type: "texture",
            sizeBytes: Math.max(4096, Math.trunc((input.decodedTextureBytes ?? textureCount * 65536) / Math.max(1, textureCount))),
            dependencies: []
        });
    }
    if (animationCount > 0) {
        entries.push({
            id: `${rootId}:animation`,
            path: `${input.url}#animation`,
            type: "animation",
            sizeBytes: 2048 + animationCount * 4096,
            dependencies: [`${rootId}:geometry-buffer`]
        });
    }
    return entries;
}
function topologicalSort(entries) {
    const byId = new Map(entries.map((entry) => [entry.id, entry]));
    const sorted = [];
    const visited = new Set();
    const visiting = new Set();
    const visit = (id) => {
        if (visited.has(id))
            return;
        if (visiting.has(id))
            throw new Error(`Asset bundle dependency cycle detected: ${id}`);
        visiting.add(id);
        for (const dependency of byId.get(id)?.dependencies ?? []) {
            visit(dependency);
        }
        visiting.delete(id);
        visited.add(id);
        sorted.push(id);
    };
    for (const entry of entries)
        visit(entry.id);
    return sorted;
}
function simulateCache(loadOrder, entries) {
    const byId = new Map(entries.map((entry) => [entry.id, entry]));
    const maxEntries = Math.max(3, Math.min(6, loadOrder.length - 1));
    const maxBytes = Math.max(32768, Math.trunc(sumBytes(entries) * 0.72));
    const retained = new Map();
    const evicted = [];
    let clock = 0;
    let memoryBytes = 0;
    let hits = 0;
    let misses = 0;
    let evictions = 0;
    for (const id of loadOrder) {
        clock += 1;
        const hit = retained.get(id);
        if (hit) {
            hit.accessCount += 1;
            hit.lastAccess = clock;
            hits += 1;
            continue;
        }
        misses += 1;
        const sizeBytes = byId.get(id)?.sizeBytes ?? 0;
        while ((retained.size >= maxEntries || memoryBytes + sizeBytes > maxBytes) && retained.size > 0) {
            const victim = [...retained.entries()].sort((left, right) => left[1].lastAccess - right[1].lastAccess)[0];
            if (!victim)
                break;
            retained.delete(victim[0]);
            memoryBytes -= victim[1].sizeBytes;
            evicted.push(victim[0]);
            evictions += 1;
        }
        if (sizeBytes <= maxBytes) {
            retained.set(id, { sizeBytes, accessCount: 1, lastAccess: clock });
            memoryBytes += sizeBytes;
        }
    }
    for (const id of loadOrder.slice(-2)) {
        if (retained.has(id)) {
            retained.get(id).accessCount += 1;
            hits += 1;
        }
        else {
            misses += 1;
        }
    }
    return {
        policy: "lru",
        maxEntries,
        maxBytes,
        cachedEntries: retained.size,
        memoryBytes,
        hits,
        misses,
        evictions,
        hitRate: Number((hits / Math.max(1, hits + misses)).toFixed(4)),
        retainedAssetIds: [...retained.keys()],
        evictedAssetIds: evicted
    };
}
function assertInput(input) {
    if (input.assetId.trim().length === 0)
        throw new Error("assetId is required.");
    if (input.url.trim().length === 0)
        throw new Error("url is required.");
    for (const [name, value] of Object.entries({
        meshCount: input.meshCount,
        materialCount: input.materialCount,
        textureCount: input.textureCount,
        animationCount: input.animationCount,
        skinCount: input.skinCount,
        morphTargetCount: input.morphTargetCount
    })) {
        if (!Number.isFinite(value) || value < 0)
            throw new Error(`${name} must be a non-negative finite number.`);
    }
}
function normalizeAssetId(id) {
    return id.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "asset";
}
function sumBytes(entries) {
    return entries.reduce((sum, entry) => sum + entry.sizeBytes, 0);
}
function stableHash(value) {
    let hash = 2166136261;
    for (let index = 0; index < value.length; index += 1) {
        hash ^= value.charCodeAt(index);
        hash = Math.imul(hash, 16777619);
    }
    return (hash >>> 0).toString(16).padStart(8, "0");
}
//# sourceMappingURL=AssetBundleCacheFixtures.js.map