/**
 * Emerge Mail — Gmail bounce forwarder.
 *
 * Forwards Gmail delivery-failure (DSN) messages to your site's Emerge Mail
 * endpoint, which records them in the Activity Log. Your secret token is
 * already baked into the URL below.
 *
 * Setup (one time): open https://script.google.com → New project, paste this
 * whole file, Save, then pick `emergeSetup` and click Run and approve the Gmail
 * permission. It installs its own recurring trigger — nothing else to do.
 */

var EMERGE_WEBHOOK_URL       = '__EMERGE_WEBHOOK_URL__';
var EMERGE_TRIGGER_MINUTES   = 15;                          // allowed: 1, 5, 10, 15, 30
var EMERGE_LOOKBACK_INTERVAL = EMERGE_TRIGGER_MINUTES + 5;  // minutes scanned per run (+5 buffer)
var EMERGE_LABEL             = 'EmergeMailProcessed';       // marks handled threads
var EMERGE_MAX_THREADS       = 50;                          // per run

// Run once. (Re)installs the trigger and does a first pass. Safe to re-run.
function emergeSetup() {
  ScriptApp.getProjectTriggers().forEach(function (trigger) {
    if (trigger.getHandlerFunction() === 'emergeForwardBounces') {
      ScriptApp.deleteTrigger(trigger);
    }
  });

  ScriptApp.newTrigger('emergeForwardBounces')
    .timeBased()
    .everyMinutes(EMERGE_TRIGGER_MINUTES)
    .create();

  emergeForwardBounces();
}

function emergeForwardBounces() {
  var label = GmailApp.getUserLabelByName(EMERGE_LABEL) || GmailApp.createLabel(EMERGE_LABEL);
  // after: takes a Unix timestamp (newer_than: is only day-granular).
  var cutoff = Math.floor((Date.now() - EMERGE_LOOKBACK_INTERVAL * 60 * 1000) / 1000);
  var query = 'from:mailer-daemon after:' + cutoff + ' -label:' + EMERGE_LABEL;
  var threads = GmailApp.search(query, 0, EMERGE_MAX_THREADS);
  if (!threads.length) { return; }

  var mailbox = Session.getActiveUser().getEmail();

  // One event per recipient per run: Gmail retries a soft bounce and files each
  // DSN in the same thread, so this avoids logging the same address repeatedly.
  var byEmail = {};

  threads.forEach(function (thread) {
    if (emergeThreadHasLabel_(thread, EMERGE_LABEL)) { return; } // search index lags; check directly
    thread.getMessages().forEach(function (msg) {
      var parsed = emergeParseBounce_(msg);
      if (parsed) { emergeCollectByEmail_(byEmail, parsed); }
    });
    thread.addLabel(label);
  });

  var events = Object.keys(byEmail).map(function (key) { return byEmail[key]; });
  if (!events.length) { return; }

  UrlFetchApp.fetch(EMERGE_WEBHOOK_URL, {
    method: 'post',
    contentType: 'application/json',
    muteHttpExceptions: true,
    payload: JSON.stringify({ emerge_source: 'gmail', mailbox: mailbox, events: events })
  });
}

// Keep one bounce per recipient; a hard bounce wins over a soft one.
function emergeCollectByEmail_(byEmail, parsed) {
  var key = String(parsed.email || '').toLowerCase();
  if (!key) { return; }
  var existing = byEmail[key];
  if (!existing) { byEmail[key] = parsed; return; }
  if (existing.disposition !== 'hard' && parsed.disposition === 'hard') {
    byEmail[key] = parsed;
  }
}

function emergeThreadHasLabel_(thread, name) {
  var labels = thread.getLabels();
  for (var i = 0; i < labels.length; i++) {
    if (labels[i].getName() === name) { return true; }
  }
  return false;
}

// Pull the failed recipient + status out of an RFC 3464 delivery-status report.
function emergeParseBounce_(msg) {
  var raw = msg.getRawContent();

  var action = (raw.match(/Action:\s*(\w+)/i) || [])[1] || '';
  if (action.toLowerCase() !== 'failed') { return null; }

  var rcpt = (raw.match(/Final-Recipient:\s*[^;]+;\s*([^\s]+)/i) || [])[1] || '';
  if (!rcpt) { return null; }

  var status = (raw.match(/Status:\s*([245]\.\d+\.\d+)/i) || [])[1] || '';
  var diag   = (raw.match(/Diagnostic-Code:\s*(.+)/i) || [])[1] || '';

  // The DSN quotes the original headers after the status part, so the LAST
  // Subject:/Message-ID: are the original's (matching what Emerge Mail stamped).
  var subjects = raw.match(/^Subject:\s*(.+)$/gim) || [];
  var subject  = subjects.length ? subjects[subjects.length - 1].replace(/^Subject:\s*/i, '').trim() : '';

  var ids       = raw.match(/^Message-ID:\s*(<[^>]+>)/gim) || [];
  var messageId = ids.length ? ids[ids.length - 1].replace(/^Message-ID:\s*/i, '').trim() : '';

  return {
    email:       rcpt.replace(/[<>]/g, '').trim(),
    disposition: status.charAt(0) === '5' ? 'hard' : 'soft',
    message_id:  messageId,
    subject:     subject,
    reason:      (diag || status).trim()
  };
}
