'use strict';

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 parsed = Date.parse(String(value));
  return Number.isFinite(parsed) ? parsed : 0;
}

function observedTimeInfo(value, now = Date.now(), opts = {}) {
  const rawMs = parseTimeMs(value);
  if (!rawMs) return { ms: 0, rawMs: 0, futureMs: 0, futureRejected: false, futureClamped: false };
  const futureSkewMs = Number.isFinite(opts.futureSkewMs)
    ? Math.max(0, opts.futureSkewMs)
    : OBSERVED_TIME_FUTURE_SKEW_MS;
  const futureMs = rawMs - now;
  if (futureMs > futureSkewMs) {
    return { ms: 0, rawMs, futureMs, futureRejected: true, futureClamped: false };
  }
  if (futureMs > 0) {
    return { ms: now, rawMs, futureMs, futureRejected: false, futureClamped: true };
  }
  return { ms: rawMs, rawMs, futureMs: 0, futureRejected: false, futureClamped: false };
}

function normalizeObservedTimeMs(value, now = Date.now(), opts = {}) {
  return observedTimeInfo(value, now, opts).ms;
}

function latestObservedTimeMs(values, now = Date.now(), opts = {}) {
  let latest = 0;
  for (const value of values || []) {
    const ms = normalizeObservedTimeMs(value, now, opts);
    if (ms > latest) latest = ms;
  }
  return latest;
}

function observedTimeIso(value, now = Date.now(), opts = {}) {
  const ms = normalizeObservedTimeMs(value, now, opts);
  return ms ? new Date(ms).toISOString() : '';
}

module.exports = {
  OBSERVED_TIME_FUTURE_SKEW_MS,
  latestObservedTimeMs,
  normalizeObservedTimeMs,
  observedTimeInfo,
  observedTimeIso,
  parseTimeMs,
};
