/** Transient SMTP / network errors worth retrying before marking recipient failed. */
export function isTransientSmtpError(err: unknown): boolean {
  if (!err || typeof err !== 'object') return false;
  const e = err as { code?: string; responseCode?: number; message?: string };
  const code = e.code;
  if (code === 'ETIMEDOUT' || code === 'ECONNRESET' || code === 'ECONNREFUSED' || code === 'ESOCKET') return true;
  const rc = e.responseCode;
  if (typeof rc === 'number' && [421, 450, 451, 452, 454].includes(rc)) return true;
  const msg = (e.message || '').toLowerCase();
  if (msg.includes('timeout') || msg.includes('temporar') || msg.includes('try again')) return true;
  return false;
}

/** Run mail send with exponential backoff (3 attempts). */
export async function withSmtpRetry<T>(fn: () => Promise<T>, label = 'smtp'): Promise<T> {
  let last: unknown;
  for (let attempt = 0; attempt < 3; attempt++) {
    try {
      return await fn();
    } catch (err) {
      last = err;
      if (!isTransientSmtpError(err) || attempt === 2) throw err;
      const ms = 400 * 2 ** attempt;
      await new Promise(r => setTimeout(r, ms));
    }
  }
  throw last;
}
