'use strict';

(function (root, factory) {
  const api = factory();
  if (typeof module !== 'undefined' && module.exports) module.exports = api;
  if (root) root.SessionActivityUtils = api;
})(typeof globalThis !== 'undefined' ? globalThis : this, function () {
  const ACTIVE_ACTIVITY_BUCKET_MS = 10 * 60 * 1000;
  const ACTIVE_STATUS_RANK = { running: 0, resuming: 1, waiting: 2, idle: 3, exited: 4 };
  const OBSERVED_TIME_FUTURE_SKEW_MS = 60 * 1000;

  function parseTimeMs(value) {
    if (value === null || value === undefined || value === '') return 0;
    if (typeof value === 'number') return Number.isFinite(value) ? value : 0;
    if (value instanceof Date) {
      const ms = value.getTime();
      return Number.isFinite(ms) ? ms : 0;
    }
    const ms = new Date(value).getTime();
    return Number.isFinite(ms) ? ms : 0;
  }

  function normalizeObservedTimeMs(value, now = Date.now()) {
    const ms = parseTimeMs(value);
    if (!ms) return 0;
    if (ms > now + OBSERVED_TIME_FUTURE_SKEW_MS) return 0;
    return ms > now ? now : ms;
  }

  function firstPresent(values) {
    for (const value of values) {
      if (value !== null && value !== undefined && value !== '') return value;
    }
    return '';
  }

  function sessionMeta(session) {
    return (session && session.meta) ? session.meta : (session || {});
  }

  function sessionLabel(id, session) {
    const meta = sessionMeta(session);
    const label = firstPresent([meta.label, meta.title, meta.aiTitle, id]);
    return String(label || '').trim() || String(id || '');
  }

  function sessionTouchedValue(session, now = Date.now()) {
    const meta = sessionMeta(session);
    const values = [
      meta.fileModifiedAt,
      meta.modifiedAt,
      meta.lastActivity,
      meta.timestamp,
      meta.createdAt,
    ];
    for (const value of values) {
      if (normalizeObservedTimeMs(value, now)) return value;
    }
    return '';
  }

  function sessionTouchedAtMs(session, now = Date.now()) {
    const meta = sessionMeta(session);
    const times = [
      meta.fileModifiedAt,
      meta.modifiedAt,
      meta.lastActivity,
      meta.timestamp,
      meta.createdAt,
    ].map((value) => normalizeObservedTimeMs(value, now)).filter((ms) => ms > 0);
    return times.length ? Math.max(...times) : 0;
  }

  function recentSessionUpdatedValue(session, now = Date.now()) {
    const meta = sessionMeta(session);
    const durableValues = [
      meta.lastMessageAt,
      meta.lastActivityAt,
      meta.activityAt,
      meta.fileModifiedAt,
      meta.modifiedAt,
    ];
    for (const value of durableValues) {
      if (parseTimeMs(value)) return value;
    }
    return sessionTouchedValue(session, now);
  }

  function recentSessionUpdatedAtMs(session, now = Date.now()) {
    const meta = sessionMeta(session);
    const transcriptTimes = [
      meta.lastMessageAt,
      meta.lastActivityAt,
      meta.activityAt,
    ].map(parseTimeMs).filter((ms) => ms > 0);
    if (transcriptTimes.length) return transcriptTimes[0];

    const durableTimes = [
      meta.fileModifiedAt,
      meta.modifiedAt,
    ].map(parseTimeMs).filter((ms) => ms > 0);
    if (durableTimes.length) return Math.max(...durableTimes);
    return sessionTouchedAtMs(session, now);
  }

  function compareLabels(aId, aSession, bId, bSession) {
    const aLabel = sessionLabel(aId, aSession);
    const bLabel = sessionLabel(bId, bSession);
    const byLabel = aLabel.localeCompare(bLabel, undefined, { numeric: true, sensitivity: 'base' });
    if (byLabel) return byLabel;
    return String(aId || '').localeCompare(String(bId || ''), undefined, { numeric: true, sensitivity: 'base' });
  }

  function activityBucket(ts) {
    return ts > 0 ? Math.floor(ts / ACTIVE_ACTIVITY_BUCKET_MS) : -Infinity;
  }

  function compareActiveByActivity(aEntry, bEntry, now = Date.now()) {
    const [aId, aSession] = aEntry;
    const [bId, bSession] = bEntry;
    const aTouched = sessionTouchedAtMs(aSession, now);
    const bTouched = sessionTouchedAtMs(bSession, now);
    const aRecent = aTouched > 0 && (now - aTouched) < ACTIVE_ACTIVITY_BUCKET_MS;
    const bRecent = bTouched > 0 && (now - bTouched) < ACTIVE_ACTIVITY_BUCKET_MS;

    if (aRecent && bRecent) return compareLabels(aId, aSession, bId, bSession);
    if (aRecent !== bRecent) return aRecent ? -1 : 1;

    const aBucket = activityBucket(aTouched);
    const bBucket = activityBucket(bTouched);
    if (aBucket !== bBucket) return bBucket - aBucket;
    return compareLabels(aId, aSession, bId, bSession);
  }

  function compareActiveByStatusThenActivity(aEntry, bEntry, getStatus, now = Date.now()) {
    const statusFor = typeof getStatus === 'function' ? getStatus : () => '';
    const aStatus = statusFor(aEntry);
    const bStatus = statusFor(bEntry);
    const aRank = ACTIVE_STATUS_RANK[aStatus] ?? 4;
    const bRank = ACTIVE_STATUS_RANK[bStatus] ?? 4;
    if (aRank !== bRank) return aRank - bRank;
    return compareActiveByActivity(aEntry, bEntry, now);
  }

  return {
    ACTIVE_ACTIVITY_BUCKET_MS,
    ACTIVE_STATUS_RANK,
    OBSERVED_TIME_FUTURE_SKEW_MS,
    compareActiveByActivity,
    compareActiveByStatusThenActivity,
    normalizeObservedTimeMs,
    parseTimeMs,
    recentSessionUpdatedAtMs,
    recentSessionUpdatedValue,
    sessionLabel,
    sessionTouchedAtMs,
    sessionTouchedValue,
  };
});
