/**
 * Normalize user-provided lookup values to the canonical comparison format.
 */
export const normalizeLookupValue = (value) => value.trim().toLowerCase();
/**
 * Match a user-provided name/id against a metadata entity using a shared normalization strategy.
 */
export const matchesEntityNameOrId = (entity, nameOrId, options) => {
    const input = normalizeLookupValue(nameOrId);
    const entityId = normalizeLookupValue(entity.id);
    const shortId = entityId.startsWith(`${options.prefix}:`) ? entityId.slice(options.prefix.length + 1) : entityId;
    const entityName = normalizeLookupValue(entity.name);
    const extraCandidates = typeof options.extraCandidates === 'function'
        ? options.extraCandidates(entity)
        : (options.extraCandidates ?? []);
    const normalizedCandidates = [
        entityId,
        shortId,
        entityName,
        ...extraCandidates
            .filter((candidate) => typeof candidate === 'string' && candidate.length > 0)
            .map(normalizeLookupValue),
    ];
    if (normalizedCandidates.includes(input)) {
        return true;
    }
    if (!input.includes(':') && `${options.prefix}:${input}` === entityId) {
        return true;
    }
    return options.prefixedCandidates?.(input).includes(entityId) ?? false;
};
/**
 * Return a new array sorted by `entity.id` in ascending order.
 */
export const sortByEntityId = (entities) => entities.toSorted((a, b) => a.id.localeCompare(b.id));
/**
 * Return a paginated slice with safe `offset`/`limit` handling.
 */
export const paginate = (items, limit, offset) => {
    const safeOffset = Math.max(0, offset ?? 0);
    const safeLimit = limit !== undefined ? Math.max(0, limit) : undefined;
    const sliced = items.slice(safeOffset);
    if (safeLimit === undefined) {
        return sliced;
    }
    return sliced.slice(0, safeLimit);
};
/**
 * Collect layer file references from one or more entities.
 * When `layer` is provided, only references from that layer are returned.
 */
export const collectLayerReferences = (entities, layer) => {
    const unique = new Map();
    entities.forEach((entity) => {
        const layers = entity.layers ?? {};
        if (layer) {
            (layers[layer] ?? []).forEach((ref) => {
                unique.set(`${ref.layer}:${ref.path}`, ref);
            });
            return;
        }
        Object.values(layers).forEach((refs) => {
            refs.forEach((ref) => {
                unique.set(`${ref.layer}:${ref.path}`, ref);
            });
        });
    });
    return [...unique.values()];
};
/**
 * Read content for all layer files referenced by a single entity.
 * This is a convenience helper built on top of `store.readLayerFile`.
 */
export const readLayerFilesForEntity = async (store, entity, layer) => {
    const refs = collectLayerReferences([entity], layer);
    return Promise.all(refs.map(async (ref) => ({
        content: await store.readLayerFile(ref),
        ref,
    })));
};
/**
 * Check whether an entity has at least one reference for the requested layer.
 */
export const layerExistsForEntity = (entity, layer) => !!entity.layers?.[layer] && entity.layers[layer].length > 0;
/**
 * Apply response visibility options to an entity.
 * - `includeLayerRefs=false` removes `layers` from the result
 * - `includeSources=true` includes source references, otherwise returns an empty `sources` array
 */
export const mapEntityForResponse = (entity, options) => {
    const includeSources = options.includeSources ?? false;
    const includeLayerRefs = options.includeLayerRefs ?? true;
    return {
        ...entity,
        layers: includeLayerRefs ? entity.layers : undefined,
        sources: includeSources ? entity.sources : [],
    };
};
