export class ConsolidationJobRegistryLimitError extends Error {
    constructor(limit) {
        super(`consolidation job registry limit reached (${String(limit)})`);
        this.name = "ConsolidationJobRegistryLimitError";
    }
}
const DEFAULT_MAX_JOBS = 32;
function isTerminal(job) {
    return (job.state === "completed" ||
        job.state === "failed" ||
        job.state === "canceled" ||
        job.state === "skipped");
}
function oldestTerminalJobId(state) {
    let candidateId;
    let candidateTs = Number.POSITIVE_INFINITY;
    for (const [jobId, record] of state.records) {
        if (!isTerminal(record.job))
            continue;
        const ts = record.job.completedAt ?? record.createdAt;
        if (ts < candidateTs) {
            candidateTs = ts;
            candidateId = jobId;
        }
    }
    return candidateId;
}
function enforceCapacity(state) {
    while (state.records.size >= state.maxJobs) {
        const evictId = oldestTerminalJobId(state);
        if (evictId === undefined) {
            throw new ConsolidationJobRegistryLimitError(state.maxJobs);
        }
        state.records.delete(evictId);
    }
}
function updateRecord(state, jobId, patch) {
    const record = state.records.get(jobId);
    if (record === undefined)
        return undefined;
    const next = { ...record, ...patch };
    state.records.set(jobId, next);
    return next;
}
function withElapsedMs(result, startedAt, completedAt) {
    return { ...result, elapsedMs: Math.max(0, completedAt - startedAt) };
}
function createRegistryState(options) {
    return {
        records: new Map(),
        maxJobs: options.maxJobs ?? DEFAULT_MAX_JOBS,
        now: options.now ?? Date.now,
    };
}
function finalizeJob(job, startedAt, completedAt, error) {
    const result = job.result;
    if (result === undefined) {
        return error === undefined ? job : { ...job, error };
    }
    return {
        ...job,
        ...(error === undefined ? {} : { error }),
        result: withElapsedMs(result, startedAt, completedAt),
    };
}
export function createConsolidationJobRegistry(options = {}) {
    const state = createRegistryState(options);
    return {
        register: (input) => {
            enforceCapacity(state);
            const record = {
                job: input.job,
                createdAt: input.createdAt,
                selection: input.selection,
                settings: input.settings,
                memoryCount: input.memoryCount,
                cancelRequested: false,
            };
            state.records.set(input.job.id, record);
            return record;
        },
        get: (jobId) => state.records.get(jobId),
        setRunning: (jobId, job) => updateRecord(state, jobId, { job }),
        complete: (jobId, job, memoryCount) => {
            const startedAt = job.startedAt ?? state.now();
            const completedAt = job.completedAt ?? state.now();
            const finalJob = finalizeJob(job, startedAt, completedAt);
            return updateRecord(state, jobId, { job: finalJob, memoryCount });
        },
        fail: (jobId, job, error, memoryCount) => {
            const startedAt = job.startedAt ?? state.now();
            const completedAt = job.completedAt ?? state.now();
            const finalJob = finalizeJob(job, startedAt, completedAt, error);
            return updateRecord(state, jobId, { job: finalJob, memoryCount });
        },
        requestCancel: (jobId) => {
            const record = state.records.get(jobId);
            if (record === undefined)
                return undefined;
            if (record.cancelRequested)
                return record;
            return updateRecord(state, jobId, { cancelRequested: true });
        },
        size: () => state.records.size,
    };
}
