const DEFAULT_OVERDUE_AFTER_MINUTES = 120;
const MAX_SCAN_CYCLES = 500;
function normalizeOverdueAfterMinutes(value) {
    const n = typeof value === 'number' ? value : Number.NaN;
    if (!Number.isFinite(n) || n <= 0)
        return DEFAULT_OVERDUE_AFTER_MINUTES;
    return Math.max(1, Math.floor(n));
}
function toOverdue(cycle, nowMs, thresholdMinutes) {
    if (cycle.status !== 'open')
        return null;
    const createdMs = Date.parse(cycle.createdAt);
    if (!Number.isFinite(createdMs))
        return null;
    const ageMinutes = Math.floor((nowMs - createdMs) / 60_000);
    if (ageMinutes <= thresholdMinutes)
        return null;
    return {
        id: cycle.id,
        instanceId: cycle.instanceId,
        cycleNumber: cycle.cycleNumber,
        ageMinutes,
        createdAt: cycle.createdAt,
    };
}
export class ApprenticeshipCycleSlaMonitor {
    store;
    config;
    now;
    raiseAttention;
    raisedCycleIds = new Set();
    constructor(opts) {
        this.store = opts.store;
        this.config = {
            enabled: opts.config?.enabled === true,
            overdueAfterMinutes: normalizeOverdueAfterMinutes(opts.config?.overdueAfterMinutes),
        };
        this.now = opts.now ?? (() => new Date());
        this.raiseAttention = opts.raiseAttention ?? null;
    }
    get enabled() {
        return this.config.enabled;
    }
    listOverdue(instanceId) {
        if (!this.config.enabled)
            return [];
        const nowMs = this.now().getTime();
        return this.store
            .list({ instanceId, limit: MAX_SCAN_CYCLES })
            .map((cycle) => toOverdue(cycle, nowMs, this.config.overdueAfterMinutes))
            .filter((item) => item !== null);
    }
    async tick(instanceId) {
        if (!this.config.enabled)
            return { enabled: false, overdue: [], raised: [] };
        const overdue = this.listOverdue(instanceId);
        const raised = [];
        if (!this.raiseAttention)
            return { enabled: true, overdue, raised };
        for (const cycle of overdue) {
            if (this.raisedCycleIds.has(cycle.id))
                continue;
            this.raisedCycleIds.add(cycle.id);
            raised.push(cycle.id);
            await this.raiseAttention({
                id: `apprenticeship-cycle-overdue-${cycle.id}`,
                title: 'Apprenticeship cycle overdue',
                summary: `Cycle ${cycle.cycleNumber} for ${cycle.instanceId} has been open for ` +
                    `${cycle.ageMinutes} minute(s), past the ${this.config.overdueAfterMinutes}-minute SLA.`,
                category: 'apprenticeship',
                priority: 'LOW',
                sourceContext: 'apprenticeship-cycle-sla',
            });
        }
        return { enabled: true, overdue, raised };
    }
}
//# sourceMappingURL=ApprenticeshipCycleSlaMonitor.js.map