import { ENTITY_HISTORY_DEFAULT_LIMIT, ENTITY_HISTORY_MAX_LIMIT } from './api-types.js';
import { MATCH_CONFIDENCE, structuralChangeFrom } from './types.js';
function toEntry(event) {
    const entry = {
        lineageId: event.lineageId,
        commit: event.commit,
        author: event.authorName,
        authorEmail: event.authorEmail,
        date: event.authoredAt,
        subject: event.subject,
        changeType: event.changeType,
        structuralChange: structuralChangeFrom(event.flags),
        matchReason: event.matchReason,
        confidence: event.confidence ?? MATCH_CONFIDENCE[event.matchReason],
        flags: event.flags,
    };
    if (event.previousName)
        entry.previousName = event.previousName;
    if (event.previousPath)
        entry.previousPath = event.previousPath;
    if (event.currentName)
        entry.currentName = event.currentName;
    if (event.currentPath)
        entry.currentPath = event.currentPath;
    return entry;
}
/**
 * A walk-back change, in the shape history reports.
 *
 * `structuralChange` is derived from the same flags the indexer would have
 * produced, because the classification came from `classifyEntityChange` either
 * way — the only difference is which two blobs it compared.
 */
function fromWalkBack(lineageId, change) {
    const entry = {
        lineageId,
        commit: change.commit,
        author: change.authorName,
        authorEmail: change.authorEmail,
        date: change.authoredAt,
        subject: change.subject,
        changeType: change.changeType,
        structuralChange: structuralChangeFrom(change.flags),
        matchReason: change.matchReason,
        confidence: MATCH_CONFIDENCE[change.matchReason],
        flags: change.flags,
        currentName: change.currentName,
        currentPath: change.currentPath,
    };
    if (change.previousName)
        entry.previousName = change.previousName;
    if (change.previousPath)
        entry.previousPath = change.previousPath;
    return entry;
}
export function entityHistory(entity, options, context) {
    const { entityIndex } = context;
    const diagnostics = [];
    const limit = Math.min(options.limit ?? ENTITY_HISTORY_DEFAULT_LIMIT, ENTITY_HISTORY_MAX_LIMIT);
    const lineageId = entity.ref.lineageId;
    // Events arrive in commit order; history reads newest first.
    const all = entityIndex.eventsByLineage.get(lineageId) ?? [];
    const cutoff = context.atCommitOrder;
    const chain = [...(cutoff === undefined ? all : all.filter(event => event.commitOrder <= cutoff))].reverse();
    let entries = chain.slice(0, limit).map(toEntry);
    let omitted = Math.max(0, chain.length - entries.length);
    // Only when the window holds nothing. Inside it the lineage events are the
    // richer evidence, and interleaving two independently ordered sources would
    // produce a history that is neither.
    if (entries.length === 0 && context.walkBack && context.walkBackKey) {
        const found = context.walkBack.byEntity.get(context.walkBackKey) ?? [];
        entries = found.slice(0, limit).map(change => fromWalkBack(lineageId, change));
        omitted = Math.max(0, found.length - entries.length);
        if (entries.length > 0) {
            diagnostics.push({
                code: 'commit-window-truncated',
                message: `"${entity.record.qualifiedName}" has no change inside the indexed window; these ` +
                    `${entries.length} entries come from searching ${context.walkBack.commitsScanned} commits further back.`,
                relativePath: entity.record.relativePath,
            });
        }
        else if (context.walkBack.exhausted) {
            diagnostics.push({
                code: 'commit-window-truncated',
                message: `"${entity.record.qualifiedName}" has no change in the indexed window, and none was found in ` +
                    `the ${context.walkBack.commitsScanned} commits searched before it. Its history is older still.`,
                relativePath: entity.record.relativePath,
            });
        }
    }
    if (entity.ref.provisional) {
        diagnostics.push({
            code: 'evidence-unavailable',
            message: `"${entity.record.qualifiedName}" has not been committed, so it has no history yet.`,
            relativePath: entity.record.relativePath,
        });
    }
    const head = entityIndex.state.byLineageId.get(lineageId);
    if (head?.birthAtWindowBoundary) {
        diagnostics.push({
            code: 'commit-window-truncated',
            message: `"${entity.record.qualifiedName}" already existed when the indexed window opened; earlier changes are not covered.`,
            relativePath: entity.record.relativePath,
            commit: head.birthCommit,
        });
    }
    return {
        entity: entity.ref,
        entries,
        truncated: omitted > 0,
        omitted,
        diagnostics,
    };
}
//# sourceMappingURL=history.js.map