'use strict';
/**
 * Per-credential cooldown state — Item C of error-recovery deep-dive.
 *
 * Borrowed shape from OpenClaw's auth-profiles/credential-state.ts —
 * `markFailure` puts a profile on cooldown, `markSuccess` clears it,
 * `isOnCooldown` is the gate Item B's rotation reads.
 *
 * State is persisted in kv_store under key `auth_cooldowns` as a JSON
 * map: `{ [profileId]: { until_ms, reason, consecutive_failures } }`.
 * No new schema migration — kv is already provisioned.
 *
 * Cooldown schedule mirrors the skill backoff table (Item E):
 *   1st failure → 30s
 *   2nd failure → 60s
 *   3rd failure → 5min
 *   4th failure → 15min
 *   5th+ failure → 60min
 *
 * Successful use resets consecutive_failures to 0 and clears cooldown.
 */
const { DEFAULT_BACKOFF_SCHEDULE_MS } = require('../skills/skill-dispatch-decision');
const recoveryLogger = require('../lib/recovery-logger');

const KV_KEY = 'auth_cooldowns';

function _load(brain) {
  try {
    const raw = brain.getKv(KV_KEY);
    return raw ? JSON.parse(raw) : {};
  } catch { return {}; }
}

function _save(brain, state) {
  try { brain.setKv(KV_KEY, JSON.stringify(state)); } catch {}
}

/**
 * Mark a credential profile as failed. Increments consecutive_failures
 * and sets cooldown until the corresponding schedule entry expires.
 */
function markFailure({ profileId, profileName, reason, brain, schedule }) {
  if (!profileId) return null;
  const b = brain || require('../brain');
  const sched = Array.isArray(schedule) && schedule.length > 0 ? schedule : DEFAULT_BACKOFF_SCHEDULE_MS;
  const state = _load(b);
  const prev = state[profileId] || { consecutive_failures: 0, until_ms: 0, reason: null };
  const failures = (prev.consecutive_failures || 0) + 1;
  // Schedule index: 1st failure → schedule[0] (30s); 2nd → schedule[1] (60s); etc.
  const idx = Math.min(failures - 1, sched.length - 1);
  const cooldownMs = sched[Math.max(0, idx)];
  const until = Date.now() + cooldownMs;
  state[profileId] = {
    consecutive_failures: failures,
    until_ms: until,
    reason: reason || prev.reason || 'unknown',
    last_failure_at: new Date().toISOString(),
  };
  _save(b, state);

  try {
    recoveryLogger.warn('auth-cooldown', 'credential_cooldown_applied', {
      profile: profileName || profileId,
      duration_ms: cooldownMs,
      details: {
        consecutive_failures: failures,
        until: new Date(until).toISOString(),
        reason: reason || 'unknown',
      },
    });
  } catch {}

  return { until, cooldownMs, consecutive_failures: failures };
}

/**
 * Clear cooldown after a successful use of the credential.
 */
function markSuccess({ profileId, profileName, brain }) {
  if (!profileId) return;
  const b = brain || require('../brain');
  const state = _load(b);
  if (!state[profileId]) return; // already clear
  const prev = state[profileId];
  delete state[profileId];
  _save(b, state);

  if (prev.consecutive_failures > 0) {
    try {
      recoveryLogger.info('auth-cooldown', 'credential_cooldown_cleared', {
        profile: profileName || profileId,
        details: { prior_failures: prev.consecutive_failures },
      });
    } catch {}
  }
}

/**
 * Check if a profile is currently on cooldown.
 * Returns null if eligible, or `{until_ms, remaining_ms, consecutive_failures, reason}`
 * if cooled down.
 */
function isOnCooldown({ profileId, brain, now }) {
  if (!profileId) return null;
  const b = brain || require('../brain');
  const state = _load(b);
  const entry = state[profileId];
  if (!entry) return null;
  const t = now ?? Date.now();
  if (t >= entry.until_ms) {
    // Expired — keep the consecutive_failures counter so the next failure
    // escalates further (mirrors OpenClaw's behaviour).
    return null;
  }
  return {
    until_ms: entry.until_ms,
    remaining_ms: entry.until_ms - t,
    consecutive_failures: entry.consecutive_failures,
    reason: entry.reason,
  };
}

/**
 * List all currently-cooled-down profiles. Used by the UI status panel.
 */
function listCooldowns(brain) {
  const b = brain || require('../brain');
  const state = _load(b);
  const now = Date.now();
  const active = [];
  for (const [profileId, entry] of Object.entries(state)) {
    if (entry.until_ms > now) {
      active.push({
        profile_id: profileId,
        until_ms: entry.until_ms,
        remaining_ms: entry.until_ms - now,
        consecutive_failures: entry.consecutive_failures,
        reason: entry.reason,
      });
    }
  }
  return active.sort((a, b) => a.remaining_ms - b.remaining_ms);
}

/**
 * Reset cooldowns for one or all profiles (admin/debug).
 */
function resetCooldown({ profileId, brain }) {
  const b = brain || require('../brain');
  const state = _load(b);
  if (profileId) {
    delete state[profileId];
  } else {
    Object.keys(state).forEach(k => delete state[k]);
  }
  _save(b, state);
}

module.exports = {
  markFailure,
  markSuccess,
  isOnCooldown,
  listCooldowns,
  resetCooldown,
  KV_KEY,
};
