/**
 * Shared repo-asset listing.
 *
 * Builds the list of assets owned by entries in a single repo, merging the two
 * storage schemes (legacy `{type}/{id}/{filename}` blobs surfaced by the
 * backend's listAll, and immutable-keyed assets tracked only in the ownership
 * index). Extracted from the asset-api route so the HTTP endpoints and the MCP
 * `list_assets` tool return an identical shape — including a render-safe
 * `embed` string callers can paste straight into an entry body.
 */
import { publicUrlFor, publicUrlForAssetId } from './assets/index.js';
import { assetEmbed } from '../core/embeds.js';
import { readFileSync } from 'fs';
/**
 * Read the `name:` field from a markdown file's YAML frontmatter without a
 * full YAML parse. Returns null if the file can't be read or has no name.
 * Lets the asset list label assets by entity name without paying the cost of
 * a full markdownService.parseFile() per entity.
 */
function extractEntityName(mdPath) {
    try {
        const raw = readFileSync(mdPath, 'utf-8');
        // Frontmatter is always at the top; don't read past ~2KB.
        const head = raw.slice(0, 2048);
        if (!head.startsWith('---'))
            return null;
        const end = head.indexOf('\n---', 3);
        const block = end === -1 ? head : head.slice(0, end);
        // Match `name: value` or `name: "value"` or `name: 'value'`.
        const match = block.match(/^name:\s*(?:'([^']*)'|"([^"]*)"|(.+?))\s*$/m);
        if (!match)
            return null;
        return (match[1] ?? match[2] ?? match[3]).trim() || null;
    }
    catch {
        return null;
    }
}
/**
 * Build the asset list for a single repo. Returns [] if the repo's graph can't
 * be loaded (right for the bulk endpoint; single-repo callers should check
 * repo existence themselves first if they want a 404).
 *
 * Algorithm (works for both local and R2 backends):
 *   1. One listAllAssets() call — single filesystem walk or one paginated S3
 *      ListObjectsV2 request. No HEAD round-trips, no markdown parsing.
 *   2. Resolve each asset's entity markdown path via the graph's file index
 *      (not a `{plural}/{id}.md` guess, which misses root/nested entries).
 *   3. Merge in immutable-keyed assets from the ownership index — these live
 *      under an `{assetId}/` prefix the backend's listAll can't see.
 */
export async function listRepoAssets(repo, workspaceManager) {
    let graph;
    try {
        graph = workspaceManager.getGraph(repo);
    }
    catch {
        return [];
    }
    let allAssets;
    try {
        allAssets = await graph.listAllAssets();
    }
    catch {
        return [];
    }
    const nameCache = new Map();
    const result = [];
    for (const asset of allAssets) {
        const mdPath = graph.getEntityFilePath(asset.entityType, asset.entityId);
        if (!mdPath)
            continue;
        const key = `${asset.entityType}/${asset.entityId}`;
        let entityName = nameCache.get(key);
        if (entityName === undefined) {
            entityName = extractEntityName(mdPath) ?? asset.entityId;
            nameCache.set(key, entityName);
        }
        const url = publicUrlFor(asset, asset.entityType, asset.entityId);
        result.push({
            filename: asset.filename,
            size: asset.size,
            contentType: asset.contentType,
            url,
            embed: assetEmbed(asset.filename, url),
            assetId: null,
            entityType: asset.entityType,
            entityId: asset.entityId,
            entityName,
            mediaType: asset.mediaType,
        });
    }
    // Immutable-keyed assets live in the ownership index, not under a
    // {type}/{id} key prefix, so backend.listAll() can't see them. Merge them in
    // from assets.db. (During the migration cutover — after migrate, before
    // old-blob cleanup — an asset can appear via both paths; that transient
    // duplicate clears when the legacy blobs are deleted.)
    const index = workspaceManager.getAssetIndex?.();
    if (index) {
        for (const row of index.listByRepo(repo)) {
            const mdPath = graph.getEntityFilePath(row.ownerType, row.ownerId);
            const key = `${row.ownerType}/${row.ownerId}`;
            let entityName = nameCache.get(key);
            if (entityName === undefined) {
                entityName = (mdPath ? extractEntityName(mdPath) : null) ?? row.ownerId;
                nameCache.set(key, entityName);
            }
            const url = publicUrlForAssetId(row.assetId, row.filename);
            result.push({
                filename: row.filename,
                size: 0, // size isn't tracked in the index; a backend HEAD would cost a round-trip
                contentType: row.contentType,
                url,
                embed: assetEmbed(row.filename, url),
                assetId: row.assetId,
                entityType: row.ownerType,
                entityId: row.ownerId,
                entityName,
                mediaType: row.mediaType,
            });
        }
    }
    return result;
}
//# sourceMappingURL=asset-listing.js.map