// engine/hooks/handlers/on-status-change.ts — on-status-change hook handler (SPEC-137)
// Listens for spec:status-change events and regenerates the spec dashboard.
// Rate-limited to 10 seconds per project to avoid excessive regeneration.
// ---------------------------------------------------------------------------
// Rate limiter (per project, 10 second cooldown)
// ---------------------------------------------------------------------------
const RATE_LIMIT_MS = 10_000; // 10 seconds
const lastRegenTimestamps = new Map();
/**
 * Check if dashboard regen for the project is within rate limit.
 * Returns true if rate-limited (should skip), false if should proceed.
 */
export function isDashboardRateLimited(projectId, nowMs = Date.now()) {
    const last = lastRegenTimestamps.get(projectId);
    if (last === undefined) {
        return false;
    }
    return nowMs - last < RATE_LIMIT_MS;
}
/**
 * Record regen timestamp for a project.
 */
export function recordRegenTimestamp(projectId, nowMs = Date.now()) {
    lastRegenTimestamps.set(projectId, nowMs);
}
/**
 * Clear all regen timestamps (for testing).
 */
export function clearRegenTimestamps() {
    lastRegenTimestamps.clear();
}
// ---------------------------------------------------------------------------
// Payload extraction
// ---------------------------------------------------------------------------
/**
 * Extract and validate SpecStatusChangePayload from an EventBusEvent.
 * Returns null if payload is invalid or missing required fields.
 */
export function extractStatusChangePayload(event) {
    const { data } = event;
    if (typeof data.eventName !== 'string' || data.eventName !== 'spec:status-change') {
        return null;
    }
    if (typeof data.specId !== 'string' ||
        typeof data.oldStatus !== 'string' ||
        typeof data.newStatus !== 'string' ||
        typeof data.projectId !== 'string') {
        return null;
    }
    if (!data.specId || !data.projectId) {
        return null;
    }
    return {
        specId: data.specId,
        oldStatus: data.oldStatus,
        newStatus: data.newStatus,
        projectId: data.projectId,
    };
}
// ---------------------------------------------------------------------------
// Core handler logic
// ---------------------------------------------------------------------------
/**
 * Handle a spec:status-change event.
 * Regenerates the spec dashboard, rate-limited per project.
 */
export async function handleStatusChange(event, regenFn) {
    const payload = extractStatusChangePayload(event);
    if (!payload) {
        return null;
    }
    const now = Date.now();
    if (isDashboardRateLimited(payload.projectId, now)) {
        return {
            specId: payload.specId,
            dashboardRegenerated: false,
            reason: 'rate-limited',
        };
    }
    recordRegenTimestamp(payload.projectId, now);
    try {
        const regenerated = await regenFn(payload.projectId);
        if (regenerated) {
            console.warn(`[hooks] Dashboard regenerated for project ${payload.projectId} after ${payload.specId} changed to ${payload.newStatus}`);
        }
        return {
            specId: payload.specId,
            dashboardRegenerated: regenerated,
        };
    }
    catch (err) {
        console.warn(`[hooks] on-status-change dashboard regen error:`, err);
        return {
            specId: payload.specId,
            dashboardRegenerated: false,
            reason: 'regen-error',
        };
    }
}
// ---------------------------------------------------------------------------
// EventBus integration
// ---------------------------------------------------------------------------
/**
 * Emit a spec:status-change event on the event bus.
 */
export function emitSpecStatusChange(bus, payload) {
    return bus.emitEvent('hook:complete', {
        eventName: 'spec:status-change',
        specId: payload.specId,
        oldStatus: payload.oldStatus,
        newStatus: payload.newStatus,
        projectId: payload.projectId,
    });
}
/**
 * Subscribe to spec:status-change events on the event bus.
 * Returns an unsubscribe function.
 */
export function subscribeStatusChangeHandler(bus, regenFn) {
    const listener = (event) => {
        void handleStatusChange(event, regenFn).catch((err) => {
            console.error('[planu] on-status-change handleStatusChange failed:', err);
        });
    };
    bus.onEvent('hook:complete', listener);
    return () => {
        bus.off('hook:complete', listener);
    };
}
//# sourceMappingURL=on-status-change.js.map