'use strict';
/**
 * Channel idle-poll backoff — Item F of error-recovery deep-dive.
 *
 * Borrowed from OpenClaw's command-poll-backoff
 * (src/agents/command-poll-backoff.ts). OpenClaw uses it to back off
 * when a long-running command produces no new output. Wall-E's adaptation:
 *
 *   When a channel poll (Slack/iMessage/Telegram) returns NO new
 *   messages, escalate the next-poll interval up to 60s. Reset to base
 *   interval on the first new message.
 *
 * Why this matters:
 *   - Slack DM channel polls every 5s — that's 17,280 calls/day even
 *     when the user is asleep
 *   - iMessage polls every 10s — same shape
 *   - Telegram polls via getUpdates with timeout — already long-poll-ish
 *
 *   For most users, 99% of polls return empty. Backing off to 60s after
 *   3-5 consecutive empty polls cuts API calls by ~10x without
 *   sacrificing responsiveness — a new message hits the next poll within
 *   60s worst-case, and most channels also push events for high-priority
 *   updates.
 *
 * Schedule: [base, base*2, base*4, base*6, capped at 60s]
 *   For Slack (base 5s):  5s → 10s → 20s → 30s → 60s (cap)
 *   For iMessage (base 10s): 10s → 20s → 40s → 60s (cap)
 *   For Telegram (base 5s): same as Slack
 */

const recoveryLogger = require('../lib/recovery-logger');

const DEFAULT_MAX_BACKOFF_MS = 60_000;
const DEFAULT_ESCALATION_MULTIPLIERS = [1, 2, 4, 6, 12]; // 1=base, escalate up to 12x

/**
 * Idle-poll tracker for a single channel. Stateful — one per channel
 * instance. Caller wires `recordPoll(hasNewMessages)` after every poll
 * and `getNextInterval()` before the next setTimeout.
 *
 * @param {Object} opts
 * @param {string} opts.channel - 'slack_dm' / 'imessage' / 'telegram' / etc
 * @param {number} opts.baseIntervalMs - the channel's normal poll interval
 * @param {number} [opts.maxBackoffMs=60000]
 * @param {number[]} [opts.escalationMultipliers=[1, 2, 4, 6, 12]]
 */
function createIdlePollTracker({
  channel,
  baseIntervalMs,
  maxBackoffMs = DEFAULT_MAX_BACKOFF_MS,
  escalationMultipliers = DEFAULT_ESCALATION_MULTIPLIERS,
}) {
  if (!channel) throw new Error('createIdlePollTracker requires channel');
  if (!Number.isFinite(baseIntervalMs) || baseIntervalMs <= 0) {
    throw new Error('createIdlePollTracker requires positive baseIntervalMs');
  }

  let consecutiveEmpty = 0;
  let lastInterval = baseIntervalMs;

  function _intervalFor(empty) {
    const idx = Math.min(empty, escalationMultipliers.length - 1);
    const candidate = baseIntervalMs * escalationMultipliers[Math.max(0, idx)];
    return Math.min(candidate, maxBackoffMs);
  }

  return {
    /**
     * Call after each poll. `hasNewMessages` should be true if the poll
     * returned at least one new message (channel-specific definition).
     */
    recordPoll(hasNewMessages) {
      if (hasNewMessages) {
        if (consecutiveEmpty > 0) {
          try {
            recoveryLogger.info('channel-poll', 'idle_backoff_reset', {
              channel,
              details: {
                prior_consecutive_empty: consecutiveEmpty,
                prior_interval_ms: lastInterval,
              },
            });
          } catch {}
        }
        consecutiveEmpty = 0;
      } else {
        consecutiveEmpty += 1;
      }
    },

    /**
     * Get the recommended next-poll delay. Should be called BEFORE
     * scheduling the next setTimeout.
     */
    getNextInterval() {
      const next = _intervalFor(consecutiveEmpty);
      if (next !== lastInterval) {
        try {
          recoveryLogger.info('channel-poll', 'idle_backoff_escalated', {
            channel,
            duration_ms: next,
            details: {
              consecutive_empty: consecutiveEmpty,
              prior_interval_ms: lastInterval,
              base_interval_ms: baseIntervalMs,
            },
          });
        } catch {}
        lastInterval = next;
      }
      return next;
    },

    /**
     * Inspect current state (mainly for tests + UI).
     */
    getStatus() {
      return {
        channel,
        consecutive_empty: consecutiveEmpty,
        base_interval_ms: baseIntervalMs,
        current_interval_ms: lastInterval,
        max_backoff_ms: maxBackoffMs,
      };
    },

    /**
     * Force-reset (e.g. after reconnect, manual user-action).
     */
    reset() {
      consecutiveEmpty = 0;
      lastInterval = baseIntervalMs;
    },
  };
}

module.exports = {
  createIdlePollTracker,
  DEFAULT_MAX_BACKOFF_MS,
  DEFAULT_ESCALATION_MULTIPLIERS,
};
