import { ConfirmationStore, InvalidTransitionError } from './ConfirmationStore.js';
import { NotificationDispatcher } from './NotificationDispatcher.js';
const CHECK_EXPIRED_INTERVAL_MS = 60_000;
const MAX_PENDING = 10;
const DEFAULT_TIMEOUT_S = 300;
export class ConfirmationManager {
    store;
    dispatcher;
    eventBus;
    pending = new Map();
    expiryCheckId = null;
    timeoutSeconds;
    constructor(deps) {
        this.store = new ConfirmationStore(deps.storage);
        this.dispatcher = new NotificationDispatcher(deps.channels);
        this.eventBus = deps.eventBus;
        this.timeoutSeconds = deps.timeoutSeconds ?? DEFAULT_TIMEOUT_S;
    }
    async requestConfirmation(action, riskAssessment) {
        this.enforceMaxPending();
        const input = {
            correlationId: action.correlationId ?? `confirm-${Date.now()}`,
            actionType: action.actionType,
            actionSummary: `${action.actionType} on ${action.target}`,
            riskLevel: riskAssessment.riskLevel,
            riskScore: riskAssessment.score,
            riskFactors: riskAssessment.factors,
            timeoutSeconds: this.timeoutSeconds,
            idempotencyKey: `${action.actionType}:${action.target}:${riskAssessment.riskLevel}`,
        };
        const confirmation = this.store.create(input);
        if (confirmation.status !== 'pending') {
            return {
                id: confirmation.id,
                status: confirmation.status,
                response: confirmation.ownerResponse ?? undefined,
            };
        }
        const notifyResult = await this.dispatcher.notify(confirmation);
        if (notifyResult.success && notifyResult.channel) {
            this.store.updateChannel(confirmation.id, notifyResult.channel);
        }
        if (!notifyResult.success) {
            const expired = this.store.resolve(confirmation.id, 'expired', 'All notification channels failed');
            this.emitResolved(expired);
            return { id: expired.id, status: 'expired', response: expired.ownerResponse ?? undefined };
        }
        this.emitRequested(confirmation);
        return this.waitForResponse(confirmation);
    }
    handleResponse(confirmationId, approved, response) {
        const status = approved ? 'approved' : 'rejected';
        const resolved = this.store.resolve(confirmationId, status, response);
        this.emitResolved(resolved);
        this.resolvePending(confirmationId, {
            id: resolved.id,
            status: resolved.status,
            response: resolved.ownerResponse ?? undefined,
        });
        return {
            id: resolved.id,
            status: resolved.status,
            response: resolved.ownerResponse ?? undefined,
        };
    }
    checkExpired() {
        const expired = this.store.getExpired();
        let count = 0;
        for (const row of expired) {
            try {
                const resolved = this.store.resolve(row.id, 'expired', 'Confirmation timeout');
                this.emitResolved(resolved);
                this.resolvePending(row.id, { id: row.id, status: 'expired', response: 'Confirmation timeout' });
                count++;
            }
            catch (err) {
                if (!(err instanceof InvalidTransitionError))
                    throw err;
            }
        }
        return count;
    }
    loadPendingOnStartup() {
        const pendingRows = this.store.getPending();
        const now = Date.now();
        for (const row of pendingRows) {
            const expiresAt = new Date(row.expiresAt).getTime();
            if (expiresAt <= now) {
                try {
                    this.store.resolve(row.id, 'expired', 'Expired during restart');
                }
                catch {
                    // Already transitioned
                }
            }
        }
    }
    startExpiryCheck() {
        this.expiryCheckId = setInterval(() => {
            this.checkExpired();
        }, CHECK_EXPIRED_INTERVAL_MS);
    }
    stopExpiryCheck() {
        if (this.expiryCheckId) {
            clearInterval(this.expiryCheckId);
            this.expiryCheckId = null;
        }
        for (const [id, entry] of this.pending) {
            clearTimeout(entry.timerId);
            entry.resolve({ id, status: 'cancelled' });
        }
        this.pending.clear();
    }
    getStore() {
        return this.store;
    }
    getPendingCount() {
        return this.store.getPendingCount();
    }
    waitForResponse(confirmation) {
        return new Promise((resolve) => {
            const remaining = new Date(confirmation.expiresAt).getTime() - Date.now();
            const timerId = setTimeout(() => {
                this.pending.delete(confirmation.id);
                try {
                    const expired = this.store.resolve(confirmation.id, 'expired', 'Confirmation timeout');
                    this.emitResolved(expired);
                    resolve({ id: expired.id, status: 'expired', response: 'Confirmation timeout' });
                }
                catch {
                    const current = this.store.getById(confirmation.id);
                    resolve({
                        id: confirmation.id,
                        status: (current?.status ?? 'expired'),
                    });
                }
            }, Math.max(remaining, 0));
            this.pending.set(confirmation.id, { resolve, timerId });
        });
    }
    resolvePending(id, result) {
        const entry = this.pending.get(id);
        if (entry) {
            clearTimeout(entry.timerId);
            this.pending.delete(id);
            entry.resolve(result);
        }
    }
    enforceMaxPending() {
        const pendingCount = this.store.getPendingCount();
        if (pendingCount >= MAX_PENDING) {
            const expired = this.store.getExpired();
            if (expired.length === 0) {
                const oldest = this.store.getPending();
                if (oldest.length > 0) {
                    try {
                        this.store.resolve(oldest[0].id, 'expired', 'Max pending limit exceeded');
                    }
                    catch {
                        // Already transitioned
                    }
                }
            }
        }
    }
    emitRequested(confirmation) {
        try {
            this.eventBus.emit('confirmation_requested', {
                actionId: confirmation.id,
                channel: confirmation.channel ?? 'unknown',
                status: 'PENDING_APPROVAL',
                expiresAt: confirmation.expiresAt,
            });
        }
        catch {
            // Non-critical
        }
    }
    emitResolved(confirmation) {
        try {
            const statusMap = {
                approved: 'APPROVED',
                rejected: 'REJECTED',
                expired: 'EXPIRED',
                cancelled: 'EXPIRED',
            };
            this.eventBus.emit('confirmation_resolved', {
                actionId: confirmation.id,
                channel: confirmation.channel ?? 'unknown',
                status: (statusMap[confirmation.status] ?? 'EXPIRED'),
                ownerResponse: confirmation.ownerResponse ?? undefined,
                expiresAt: confirmation.expiresAt,
            });
        }
        catch {
            // Non-critical
        }
    }
}
//# sourceMappingURL=ConfirmationManager.js.map