/**
 * FedEx shipment tracking: parse SC notes, persist, poll, post to WO rooms.
 */

import { logger } from '../utils/logger.js';
import pLimit from 'p-limit';
import { fetchWithRetry } from '../integrations/serviceChannel/client.js';
import {
  isFedExConfigured,
  isShipmentTrackingEnabled,
  trackByNumbers,
  formatFedExStatusLine,
} from '../integrations/fedex/client.js';
import webexService from './webexService.js';
import {
  getCompletedOperationsRoomId,
  parseServChanRoomTitle,
} from '../utils/servchanRoomTitle.js';

const BATCH_SIZE = 30;
const BACKFILL_CONCURRENCY = 2;
const FEDEX_TRACKING_RE = /\bFEDEX\s+([\d,\s]+)/gi;
const FEDEX_NUMBER_RE = /^\d{12,22}$/;

let bootLogged = false;

export function parseFedExTrackingNumbers(noteText) {
  if (!noteText) return [];

  const found = new Set();
  let match;
  FEDEX_TRACKING_RE.lastIndex = 0;

  while ((match = FEDEX_TRACKING_RE.exec(String(noteText))) !== null) {
    const chunk = match[1] || '';
    for (const part of chunk.split(/[,\s]+/)) {
      const num = part.trim();
      if (FEDEX_NUMBER_RE.test(num)) found.add(num);
    }
  }

  return [...found];
}

function fedExTrackUrl(trackingNumber) {
  return `https://www.fedex.com/fedextrack/?trknbr=${encodeURIComponent(trackingNumber)}`;
}

function isDelivered(statusCode, statusDescription) {
  if (String(statusCode).toUpperCase() === 'DL') return true;
  return /delivered/i.test(String(statusDescription || ''));
}

function formatStatusLabel(row) {
  return formatFedExStatusLine(row);
}

function formatStatusLabelFromResult(result) {
  return formatFedExStatusLine(result);
}

function hasShipmentStatusChanged(row, result) {
  const newLabel = formatStatusLabelFromResult(result);

  // User-visible status is the source of truth. FedEx may flip statusCode or ISO
  // timestamps between polls while the formatted line stays identical.
  if (row.lastPostedStatus) {
    return newLabel !== row.lastPostedStatus;
  }

  // Never posted to the WO room (e.g. backfill synced DB with lastPostedStatus NULL).
  return true;
}

export function formatShipmentStatusMarkdown({
  trackingNumber,
  statusDescription = null,
  statusCode = null,
  estimatedDelivery = null,
  estimatedDeliveryStart = null,
  deliveredAt = null,
  ok = true,
  error = null,
}) {
  const url = fedExTrackUrl(trackingNumber);
  if (!ok || error) {
    return `- **[${trackingNumber}](${url})** — ${error || 'Status unavailable'}`;
  }

  const summary = formatFedExStatusLine({
    statusDescription,
    statusCode,
    estimatedDelivery,
    estimatedDeliveryStart,
    deliveredAt,
  });

  return `- **[${trackingNumber}](${url})** — ${summary}`;
}

function shipmentSnapshotFromTrackResult(row, result) {
  if (!result.ok) {
    return {
      trackingNumber: row.trackingNumber,
      ok: false,
      error: result.error || 'Status unavailable',
    };
  }

  const delivered = isDelivered(result.statusCode, result.statusDescription);
  return {
    trackingNumber: row.trackingNumber,
    statusDescription: result.statusDescription,
    statusCode: result.statusCode,
    estimatedDelivery: result.estimatedDelivery,
    estimatedDeliveryStart: result.estimatedDeliveryStart,
    deliveredAt: delivered ? (result.deliveredAt || new Date().toISOString()) : null,
    ok: true,
  };
}

function collectTrackingNumbersFromNotes(notes = []) {
  const found = new Set();
  for (const note of notes) {
    for (const num of parseFedExTrackingNumbers(note?.NoteData)) {
      found.add(num);
    }
  }
  return [...found];
}

function dbGetAllMappings(db) {
  return new Promise((resolve, reject) => {
    db.all('SELECT workOrderId, roomId FROM mappings', [], (err, rows) => {
      if (err) reject(err);
      else resolve(rows || []);
    });
  });
}

async function fetchWorkOrderNotes(workOrderId) {
  const notesRes = await fetchWithRetry(`/workorders/${workOrderId}/notes`, { timeout: 30000 });
  return notesRes.data?.Notes || [];
}

async function applyBackfillTrackResult(db, row, result) {
  const now = new Date().toISOString();

  if (!result.ok) {
    await dbUpdateShipment(db, row.trackingNumber, row.workOrderId, {
      lastCheckedAt: now,
      statusCode: row.statusCode,
      statusDescription: row.statusDescription,
      estimatedDelivery: row.estimatedDelivery,
      deliveredAt: row.deliveredAt,
      lastPostedStatus: row.lastPostedStatus,
    });
    return { delivered: false, inTransit: false, error: result.error };
  }

  const statusLabel = formatStatusLabelFromResult(result);
  const delivered = isDelivered(result.statusCode, result.statusDescription);
  const deliveredAt = delivered ? (result.deliveredAt || now) : null;

  await dbUpdateShipment(db, row.trackingNumber, row.workOrderId, {
    statusCode: result.statusCode,
    statusDescription: result.statusDescription,
    estimatedDelivery: result.estimatedDelivery,
    deliveredAt,
    lastCheckedAt: now,
    // Only mark as "already posted" for delivered packages (Option B — suppress
    // ops spam). In-transit rows keep lastPostedStatus NULL so the next cron
    // posts the current status once to the WO room.
    lastPostedStatus: delivered ? statusLabel : null,
  });

  if (delivered) {
    await dbMarkOpsDeliveredNotified(db, row.workOrderId, row.trackingNumber);
  }

  return { delivered, inTransit: !delivered, error: null };
}

function dbGetShipment(db, workOrderId, trackingNumber) {
  return new Promise((resolve, reject) => {
    db.get(
      `SELECT * FROM shipment_tracking WHERE workOrderId = ? AND trackingNumber = ?`,
      [workOrderId, trackingNumber],
      (err, row) => (err ? reject(err) : resolve(row || null))
    );
  });
}

function dbInsertShipment(db, row) {
  return new Promise((resolve, reject) => {
    db.run(
      `INSERT OR IGNORE INTO shipment_tracking
       (workOrderId, roomId, carrier, trackingNumber, detectedAt, sourceNote)
       VALUES (?, ?, 'FEDEX', ?, ?, ?)`,
      [row.workOrderId, row.roomId, row.trackingNumber, row.detectedAt, row.sourceNote ?? null],
      function onRun(err) {
        if (err) reject(err);
        else resolve(this.changes > 0);
      }
    );
  });
}

function dbUpdateShipment(db, trackingNumber, workOrderId, fields) {
  return new Promise((resolve, reject) => {
    db.run(
      `UPDATE shipment_tracking SET
         statusCode = ?,
         statusDescription = ?,
         estimatedDelivery = ?,
         deliveredAt = ?,
         lastCheckedAt = ?,
         lastPostedStatus = ?
       WHERE workOrderId = ? AND trackingNumber = ?`,
      [
        fields.statusCode ?? null,
        fields.statusDescription ?? null,
        fields.estimatedDelivery ?? null,
        fields.deliveredAt ?? null,
        fields.lastCheckedAt,
        fields.lastPostedStatus ?? null,
        workOrderId,
        trackingNumber,
      ],
      (err) => (err ? reject(err) : resolve())
    );
  });
}

function dbMarkOpsDeliveredNotified(db, workOrderId, trackingNumber) {
  return new Promise((resolve, reject) => {
    db.run(
      `UPDATE shipment_tracking SET opsDeliveredNotifiedAt = ? WHERE workOrderId = ? AND trackingNumber = ?`,
      [new Date().toISOString(), workOrderId, trackingNumber],
      (err) => (err ? reject(err) : resolve())
    );
  });
}

async function resolveSpaceHeaderLine(roomId, workOrderId) {
  try {
    const botClientMod = await import('../integrations/webex/botClient.js');
    const room = await botClientMod.default.getRoom(roomId);
    const parsed = parseServChanRoomTitle(room?.title);
    return parsed?.headerLine || room?.title || `WO ${workOrderId}`;
  } catch (err) {
    logger('shipmentTracking', `Could not resolve room title for ${roomId}: ${err.message}`, 'warn');
    return `WO ${workOrderId}`;
  }
}

async function notifyOpsOnDelivery(webex, row) {
  if (row.opsDeliveredNotifiedAt) return false;

  const opsRoomId = getCompletedOperationsRoomId();
  if (!opsRoomId) {
    logger('shipmentTracking', 'COMPLETED_OPERATIONS_ROOM_ID not set — skipping ops delivery notification', 'warn');
    return false;
  }

  const headerLine = await resolveSpaceHeaderLine(row.roomId, row.workOrderId);
  const md = `${headerLine}\nFedEx ${row.trackingNumber} — successfully delivered`;

  const sender = getSender(webex);
  try {
    await sender.sendMarkdown(opsRoomId, md);
    logger(
      'shipmentTracking',
      `Posted delivery notification to ops for ${row.trackingNumber} (WO ${row.workOrderId})`
    );
    return true;
  } catch (err) {
    logger('shipmentTracking', `Ops delivery post failed for ${row.trackingNumber}: ${err.message}`, 'warn');
    return false;
  }
}

function dbGetActiveShipments(db, workOrderId = null) {
  return new Promise((resolve, reject) => {
    const sql = workOrderId
      ? `SELECT * FROM shipment_tracking WHERE deliveredAt IS NULL AND workOrderId = ? ORDER BY detectedAt`
      : `SELECT * FROM shipment_tracking WHERE deliveredAt IS NULL ORDER BY detectedAt`;
    const params = workOrderId ? [workOrderId] : [];
    db.all(sql, params, (err, rows) => (err ? reject(err) : resolve(rows || [])));
  });
}

function dbCountActiveForWo(db, workOrderId) {
  return new Promise((resolve, reject) => {
    db.get(
      `SELECT COUNT(*) AS n FROM shipment_tracking WHERE workOrderId = ? AND deliveredAt IS NULL`,
      [workOrderId],
      (err, row) => (err ? reject(err) : resolve(row?.n ?? 0))
    );
  });
}

function getSender(webex) {
  return webex && typeof webex.sendMarkdown === 'function' ? webex : webexService;
}

export function logTrackingBootStatus() {
  if (bootLogged) return;
  bootLogged = true;
  if (!isShipmentTrackingEnabled()) {
    logger('shipmentTracking', 'Disabled (SHIPMENT_TRACKING_ENABLED or missing FedEx creds)', 'info');
    return;
  }
  if (!isFedExConfigured()) {
    logger('shipmentTracking', 'Enabled but FedEx credentials missing — tracking inactive', 'warn');
    return;
  }
  logger('shipmentTracking', 'FedEx shipment tracking active', 'info');
}

export async function registerShipmentsFromNote({
  db,
  webex,
  workOrderId,
  roomId,
  woNumber,
  noteText,
}) {
  if (!isShipmentTrackingEnabled() || !isFedExConfigured()) return { registered: 0 };
  if (!db || !roomId || !workOrderId || !noteText) return { registered: 0 };

  const numbers = parseFedExTrackingNumbers(noteText);
  if (!numbers.length) return { registered: 0 };

  const sender = getSender(webex);
  const newlyRegistered = [];

  for (const trackingNumber of numbers) {
    const existing = await dbGetShipment(db, workOrderId, trackingNumber);
    if (existing) continue;

    const inserted = await dbInsertShipment(db, {
      workOrderId,
      roomId,
      trackingNumber,
      detectedAt: new Date().toISOString(),
      sourceNote: noteText.substring(0, 500),
    });

    if (inserted) newlyRegistered.push(trackingNumber);
  }

  if (!newlyRegistered.length) return { registered: 0 };

  const lines = newlyRegistered.map(
    (n) => `- [${n}](${fedExTrackUrl(n)})`
  );
  const woLabel = woNumber ? `WO-${woNumber}` : `WO ${workOrderId}`;
  const md =
    `**FedEx shipment tracking registered** for ${woLabel}\n\n` +
    `${lines.join('\n')}\n\n` +
    `ServChan will post status updates every 2 hours (8am–6pm Eastern) while in transit.`;

  try {
    await sender.sendMarkdown(roomId, md);
  } catch (err) {
    logger('shipmentTracking', `Initial post failed for WO ${workOrderId}: ${err.message}`, 'warn');
  }

  logger(
    'shipmentTracking',
    `Registered ${newlyRegistered.length} tracking number(s) for WO ${workOrderId}: ${newlyRegistered.join(', ')}`
  );

  // Poll FedEx immediately so the WO room gets a first status post without
  // waiting for the next cron window.
  try {
    const rows = [];
    for (const trackingNumber of newlyRegistered) {
      const row = await dbGetShipment(db, workOrderId, trackingNumber);
      if (row) rows.push(row);
    }
    if (rows.length) {
      const results = await trackByNumbers(rows.map((r) => r.trackingNumber));
      for (const row of rows) {
        const result = results.get(row.trackingNumber) || {
          ok: false,
          error: 'Missing from batch response',
        };
        await applyTrackResult(db, row, result, webex, woNumber ? new Map([[workOrderId, woNumber]]) : null);
      }
    }
  } catch (err) {
    logger('shipmentTracking', `Initial poll failed for WO ${workOrderId}: ${err.message}`, 'warn');
  }

  return { registered: newlyRegistered.length, trackingNumbers: newlyRegistered };
}

async function applyTrackResult(db, row, result, webex, woNumberByWoId, { suppressRoomPosts = false } = {}) {
  const now = new Date().toISOString();

  if (!result.ok) {
    logger(
      'shipmentTracking',
      `Track failed for ${row.trackingNumber} (WO ${row.workOrderId}): ${result.error}`,
      'warn'
    );
    await dbUpdateShipment(db, row.trackingNumber, row.workOrderId, {
      lastCheckedAt: now,
      statusCode: row.statusCode,
      statusDescription: row.statusDescription,
      estimatedDelivery: row.estimatedDelivery,
      deliveredAt: row.deliveredAt,
      lastPostedStatus: row.lastPostedStatus,
    });
    return { updated: false, delivered: false };
  }

  const statusLabel = formatStatusLabel(result);
  const delivered = isDelivered(result.statusCode, result.statusDescription);
  const deliveredAt = delivered ? (result.deliveredAt || now) : null;
  const statusChanged = hasShipmentStatusChanged(row, result);
  const newlyDelivered = delivered && !row.deliveredAt;

  await dbUpdateShipment(db, row.trackingNumber, row.workOrderId, {
    statusCode: result.statusCode,
    statusDescription: result.statusDescription,
    estimatedDelivery: result.estimatedDelivery,
    deliveredAt,
    lastCheckedAt: now,
    lastPostedStatus: statusChanged ? statusLabel : row.lastPostedStatus,
  });

  const woNum = woNumberByWoId?.get(row.workOrderId) || row.workOrderId;
  let pendingPost = null;

  if (statusChanged) {
    pendingPost = {
      roomId: row.roomId,
      workOrderId: row.workOrderId,
      trackingNumber: row.trackingNumber,
      statusLabel,
      delivered,
      woNum,
    };

    if (!suppressRoomPosts) {
      const sender = getSender(webex);
      const md =
        `**FedEx update** · WO-${woNum}\n\n` +
        `Tracking **[${row.trackingNumber}](${fedExTrackUrl(row.trackingNumber)})**: ${statusLabel}` +
        (delivered ? '\n\nPackage delivered.' : '');

      try {
        await sender.sendMarkdown(row.roomId, md);
        logger(
          'shipmentTracking',
          `Posted status update for ${row.trackingNumber} (WO ${row.workOrderId}): ${statusLabel}`
        );
      } catch (err) {
        logger('shipmentTracking', `Update post failed for ${row.trackingNumber}: ${err.message}`, 'warn');
      }
    } else {
      logger(
        'shipmentTracking',
        `Suppressed WO post for ${row.trackingNumber} (WO ${row.workOrderId}): ${statusLabel}`,
        'info'
      );
    }
  } else {
    logger(
      'shipmentTracking',
      `No WO post for ${row.trackingNumber} (WO ${row.workOrderId}): unchanged (${statusLabel})`,
      'info'
    );
  }

  if (newlyDelivered) {
    const opsNotified = await notifyOpsOnDelivery(webex, row);
    if (opsNotified) {
      await dbMarkOpsDeliveredNotified(db, row.workOrderId, row.trackingNumber);
    }
  }

  if (delivered) {
    const remaining = await dbCountActiveForWo(db, row.workOrderId);
    if (remaining === 0 && !suppressRoomPosts) {
      const sender = getSender(webex);
      try {
        await sender.sendMarkdown(
          row.roomId,
          `**All parts shipments delivered** for WO-${woNum}.`
        );
      } catch (err) {
        logger('shipmentTracking', `All-delivered post failed for WO ${row.workOrderId}: ${err.message}`, 'warn');
      }
    }
  }

  return { updated: statusChanged, delivered, pendingPost };
}

async function flushConsolidatedRoomPosts(db, webex, postsByRoom) {
  const sender = getSender(webex);

  for (const [roomId, entries] of postsByRoom) {
    if (!entries.length) continue;

    const woNum = entries[0].woNum;
    const lines = entries.map((entry) => {
      const url = fedExTrackUrl(entry.trackingNumber);
      let line = `- **[${entry.trackingNumber}](${url})**: ${entry.statusLabel}`;
      if (entry.delivered) line += ' · Delivered';
      return line;
    });

    const md = `**FedEx update** · WO-${woNum}\n\n${lines.join('\n')}`;
    try {
      await sender.sendMarkdown(roomId, md);
      logger(
        'shipmentTracking',
        `Posted consolidated update for WO ${woNum} (${entries.length} shipment(s))`
      );
    } catch (err) {
      logger('shipmentTracking', `Consolidated post failed for WO ${woNum}: ${err.message}`, 'warn');
    }

    const remaining = await dbCountActiveForWo(db, entries[0].workOrderId);
    if (remaining === 0) {
      try {
        await sender.sendMarkdown(roomId, `**All parts shipments delivered** for WO-${woNum}.`);
      } catch (err) {
        logger('shipmentTracking', `All-delivered post failed for WO ${woNum}: ${err.message}`, 'warn');
      }
    }
  }
}

export async function pollActiveShipments({
  db,
  webex,
  workOrderId = null,
  suppressRoomPosts = false,
  consolidateRoomPosts = false,
} = {}) {
  if (!isShipmentTrackingEnabled() || !isFedExConfigured()) {
    return { checked: 0, updated: 0, delivered: 0, shipments: [], skipped: true };
  }
  if (!db) return { checked: 0, updated: 0, delivered: 0, shipments: [], skipped: true };

  const active = await dbGetActiveShipments(db, workOrderId);
  if (!active.length) return { checked: 0, updated: 0, delivered: 0, shipments: [] };

  let updated = 0;
  let delivered = 0;
  const shipments = [];
  const consolidatedPosts = new Map();
  const suppressPosts = suppressRoomPosts || consolidateRoomPosts;

  for (let i = 0; i < active.length; i += BATCH_SIZE) {
    const batch = active.slice(i, i + BATCH_SIZE);
    const numbers = batch.map((r) => r.trackingNumber);

    let results;
    try {
      results = await trackByNumbers(numbers);
    } catch (err) {
      logger('shipmentTracking', `Batch track failed: ${err.message}`, 'error');
      for (const row of batch) {
        shipments.push({
          trackingNumber: row.trackingNumber,
          ok: false,
          error: err.message,
        });
      }
      break;
    }

    for (const row of batch) {
      const result = results.get(row.trackingNumber) || {
        ok: false,
        error: 'Missing from batch response',
      };
      const outcome = await applyTrackResult(db, row, result, webex, null, {
        suppressRoomPosts: suppressPosts,
      });
      if (outcome.updated) updated++;
      if (outcome.delivered) delivered++;
      if (consolidateRoomPosts && outcome.pendingPost) {
        const list = consolidatedPosts.get(outcome.pendingPost.roomId) || [];
        list.push(outcome.pendingPost);
        consolidatedPosts.set(outcome.pendingPost.roomId, list);
      }
      shipments.push(shipmentSnapshotFromTrackResult(row, result));
    }
  }

  if (consolidateRoomPosts && consolidatedPosts.size) {
    await flushConsolidatedRoomPosts(db, webex, consolidatedPosts);
  }

  logger(
    'shipmentTracking',
    `Poll complete: checked=${active.length}, updated=${updated}, delivered=${delivered}` +
      (workOrderId ? ` (WO ${workOrderId})` : '')
  );

  return { checked: active.length, updated, delivered, shipments };
}

export async function pollShipmentsForWorkOrder({ db, webex, workOrderId }) {
  return pollActiveShipments({ db, webex, workOrderId, suppressRoomPosts: true });
}

/**
 * One-shot backfill: scan all mapped WO spaces for FedEx numbers in SC notes,
 * insert into shipment_tracking without WO-room registration posts, then (live
 * mode) poll FedEx once. Option B: already-delivered packages get deliveredAt +
 * opsDeliveredNotifiedAt without ops-room notification; lastPostedStatus is set
 * so cron won't flood WO rooms.
 */
export async function backfillShipmentsFromAllMappings({ db, dryRun = true } = {}) {
  const mode = dryRun ? 'DRY RUN' : 'LIVE';

  if (!db) {
    return { error: 'Database not available', dryRun, mode };
  }
  if (!isShipmentTrackingEnabled() || !isFedExConfigured()) {
    return {
      error: 'Shipment tracking disabled or FedEx credentials missing',
      dryRun,
      mode,
    };
  }

  const mappings = await dbGetAllMappings(db);
  const limit = pLimit(BACKFILL_CONCURRENCY);
  const results = [];
  const newlyInsertedRows = [];

  const summary = {
    mappingsScanned: mappings.length,
    mappingsWithTracking: 0,
    trackingNumbersFound: 0,
    registered: 0,
    alreadyTracked: 0,
    fedExChecked: 0,
    alreadyDelivered: 0,
    inTransit: 0,
    trackErrors: 0,
    noteFetchErrors: 0,
  };

  await Promise.all(
    mappings.map((mapping) =>
      limit(async () => {
        const { workOrderId, roomId } = mapping;
        const rowResult = {
          workOrderId,
          roomId,
          trackingNumbers: [],
          registered: [],
          alreadyTracked: [],
        };

        try {
          const notes = await fetchWorkOrderNotes(workOrderId);
          const numbers = collectTrackingNumbersFromNotes(notes);
          rowResult.trackingNumbers = numbers;
          summary.trackingNumbersFound += numbers.length;
          if (numbers.length) summary.mappingsWithTracking++;

          for (const trackingNumber of numbers) {
            const existing = await dbGetShipment(db, workOrderId, trackingNumber);
            if (existing) {
              rowResult.alreadyTracked.push(trackingNumber);
              summary.alreadyTracked++;
              continue;
            }

            if (dryRun) {
              rowResult.registered.push(trackingNumber);
              summary.registered++;
              continue;
            }

            const inserted = await dbInsertShipment(db, {
              workOrderId,
              roomId,
              trackingNumber,
              detectedAt: new Date().toISOString(),
              sourceNote: 'backfill',
            });

            if (inserted) {
              rowResult.registered.push(trackingNumber);
              summary.registered++;
              const row = await dbGetShipment(db, workOrderId, trackingNumber);
              if (row) newlyInsertedRows.push(row);
            }
          }
        } catch (err) {
          rowResult.error = err.message;
          summary.noteFetchErrors++;
          logger(
            'shipmentTracking',
            `Backfill note fetch failed for WO ${workOrderId}: ${err.message}`,
            'warn'
          );
        }

        if (rowResult.trackingNumbers.length || rowResult.error) {
          results.push(rowResult);
        }
      })
    )
  );

  results.sort((a, b) => String(a.workOrderId).localeCompare(String(b.workOrderId)));

  if (!dryRun && newlyInsertedRows.length) {
    for (let i = 0; i < newlyInsertedRows.length; i += BATCH_SIZE) {
      const batch = newlyInsertedRows.slice(i, i + BATCH_SIZE);
      const numbers = batch.map((r) => r.trackingNumber);

      let trackResults;
      try {
        trackResults = await trackByNumbers(numbers);
      } catch (err) {
        logger('shipmentTracking', `Backfill FedEx batch failed: ${err.message}`, 'error');
        summary.trackErrors += batch.length;
        continue;
      }

      for (const row of batch) {
        summary.fedExChecked++;
        const result = trackResults.get(row.trackingNumber) || {
          ok: false,
          error: 'Missing from batch response',
        };
        const outcome = await applyBackfillTrackResult(db, row, result);
        if (outcome.error) summary.trackErrors++;
        else if (outcome.delivered) summary.alreadyDelivered++;
        else if (outcome.inTransit) summary.inTransit++;
      }
    }
  }

  logger('shipmentTracking', `Backfill complete dryRun=${dryRun} ${JSON.stringify(summary)}`);

  return { dryRun, mode, summary, results };
}

export default {
  parseFedExTrackingNumbers,
  logTrackingBootStatus,
  registerShipmentsFromNote,
  pollActiveShipments,
  pollShipmentsForWorkOrder,
  backfillShipmentsFromAllMappings,
  formatShipmentStatusMarkdown,
};
