export class ResourceLedgerPoller {
    ledger;
    breaker;
    sentinel;
    now;
    accountKey;
    // Per-source monotonic sequence — combined with ts it forms the event id, so
    // two events in the same millisecond stay distinct and a restart can't replay
    // (the seq resets to 0 on restart, but the ledger row id is source:ts:seq and
    // INSERT OR IGNORE — a genuinely new post-restart event has a new ts).
    breakerSeq = 0;
    sentinelSeq = 0;
    unsubs = [];
    started = false;
    constructor(opts) {
        this.ledger = opts.ledger;
        this.breaker = opts.breaker;
        this.sentinel = opts.rateLimitSentinel ?? null;
        this.now = opts.now ?? (() => Date.now());
        this.accountKey = opts.accountKey && opts.accountKey.trim() ? opts.accountKey.trim() : 'default';
    }
    start() {
        if (this.started)
            return;
        this.started = true;
        this.unsubs.push(this.breaker.onTrip((e) => {
            this.ledger.recordRateLimitEvent({
                ts: e.ts,
                kind: 'circuit-open',
                source: 'circuit-breaker',
                seq: this.breakerSeq++,
                accountKey: this.accountKey,
                reason: e.reason,
                detail: typeof e.retryAfterMs === 'number' ? `retryAfterMs=${e.retryAfterMs}` : undefined,
            });
        }));
        this.unsubs.push(this.breaker.onRecover((e) => {
            this.ledger.recordRateLimitEvent({
                ts: e.ts,
                kind: 'circuit-recover',
                source: 'circuit-breaker',
                seq: this.breakerSeq++,
                accountKey: this.accountKey,
            });
        }));
        if (this.sentinel) {
            const onDetected = (state) => {
                this.ledger.recordRateLimitEvent({
                    ts: this.now(),
                    kind: this.classifySentinel(state),
                    source: 'session-sentinel',
                    seq: this.sentinelSeq++,
                    accountKey: this.accountKey,
                    sessionName: typeof state?.sessionName === 'string' ? state.sessionName : undefined,
                    reason: typeof state?.reason === 'string' ? state.reason : undefined,
                });
            };
            this.sentinel.on('rate-limit:detected', onDetected);
            this.unsubs.push(() => this.sentinel?.off?.('rate-limit:detected', onDetected));
        }
    }
    /** Map a sentinel detection to a kind. Session-scoped server throttles are the
     *  common case; 529-overload is distinguished when the reason says so. */
    classifySentinel(state) {
        const reason = typeof state?.reason === 'string' ? state.reason.toLowerCase() : '';
        if (/529|overloaded/.test(reason))
            return '529';
        if (/usage|quota|weekly|session limit/.test(reason))
            return 'quota';
        return 'throttle';
    }
    stop() {
        for (const u of this.unsubs) {
            try {
                u();
            }
            catch { /* ignore */ }
        }
        this.unsubs = [];
        this.started = false;
    }
}
//# sourceMappingURL=ResourceLedgerPoller.js.map