'use strict';

const TIMESTAMP_FIELDS = ['timestamp', 'created_at', 'createdAt', 'time', 'ts'];
const SEQUENCE_FIELDS = [
  'message_index',
  'messageIndex',
  'index',
  'seq',
  'sequence',
  '_mergeIndex',
  '_promptSourceIndex',
  '_sourceLine',
];

function firstPresentTimestamp(item) {
  if (!item || typeof item !== 'object') return item;
  for (const field of TIMESTAMP_FIELDS) {
    if (item[field] != null && item[field] !== '') return item[field];
  }
  const data = item.data && typeof item.data === 'object' ? item.data : null;
  if (data) {
    for (const field of TIMESTAMP_FIELDS) {
      if (data[field] != null && data[field] !== '') return data[field];
    }
  }
  return '';
}

function normalizeEpochMs(value) {
  if (!Number.isFinite(value)) return null;
  if (value <= 0) return null;
  // Accept Unix seconds when an upstream provider emits 10-digit epochs.
  if (value >= 1_000_000_000 && value < 10_000_000_000) return value * 1000;
  return value;
}

function timelineTimestampMs(itemOrTimestamp) {
  const raw = firstPresentTimestamp(itemOrTimestamp);
  if (typeof raw === 'number') return normalizeEpochMs(raw);
  if (typeof raw === 'bigint') {
    const value = Number(raw);
    return normalizeEpochMs(value);
  }
  const text = String(raw || '').trim();
  if (!text) return null;
  if (/^[+-]?\d+(?:\.\d+)?$/.test(text)) {
    const numeric = Number(text);
    const normalized = normalizeEpochMs(numeric);
    if (normalized != null) return normalized;
  }
  const parsed = Date.parse(text);
  return Number.isFinite(parsed) ? parsed : null;
}

function timelineSequenceValue(item) {
  if (!item || typeof item !== 'object') return null;
  for (const field of SEQUENCE_FIELDS) {
    const value = Number(item[field]);
    if (Number.isFinite(value)) return value;
  }
  const data = item.data && typeof item.data === 'object' ? item.data : null;
  if (data) {
    for (const field of SEQUENCE_FIELDS) {
      const value = Number(data[field]);
      if (Number.isFinite(value)) return value;
    }
  }
  return null;
}

function compareTimelineMessages(a, b) {
  const ams = timelineTimestampMs(a);
  const bms = timelineTimestampMs(b);
  if (ams != null && bms != null && ams !== bms) return ams - bms;
  if (ams != null && bms == null) return -1;
  if (ams == null && bms != null) return 1;

  const aMergeSource = Number(a?._mergeSourceOrder);
  const bMergeSource = Number(b?._mergeSourceOrder);
  if (Number.isFinite(aMergeSource) && Number.isFinite(bMergeSource) && aMergeSource !== bMergeSource) {
    return aMergeSource - bMergeSource;
  }

  const aSeq = timelineSequenceValue(a);
  const bSeq = timelineSequenceValue(b);
  if (aSeq != null && bSeq != null && aSeq !== bSeq) return aSeq - bSeq;
  if (aSeq != null && bSeq == null) return -1;
  if (aSeq == null && bSeq != null) return 1;

  return 0;
}

function sortTimelineMessages(messages) {
  return (Array.isArray(messages) ? messages : [])
    .map((message, index) => ({ message, index }))
    .sort((a, b) => compareTimelineMessages(a.message, b.message) || a.index - b.index)
    .map((entry) => entry.message);
}

function sortTimelineMessagesInPlace(messages) {
  if (!Array.isArray(messages) || messages.length < 2) return messages;
  const sorted = sortTimelineMessages(messages);
  messages.splice(0, messages.length, ...sorted);
  return messages;
}

function latestTimelineTimestampMs(messages) {
  let latest = 0;
  for (const message of Array.isArray(messages) ? messages : []) {
    const ms = timelineTimestampMs(message);
    if (ms != null && ms > latest) latest = ms;
  }
  return latest;
}

module.exports = {
  compareTimelineMessages,
  latestTimelineTimestampMs,
  sortTimelineMessages,
  sortTimelineMessagesInPlace,
  timelineTimestampMs,
};
