/** Formatting helpers for numbers, bytes, durations and timestamps. */

export function formatInt(value: number | null | undefined): string {
  if (value === null || value === undefined || Number.isNaN(value)) return "—";
  return value.toLocaleString("en-US");
}

export function formatBytes(value: number | null | undefined): string {
  if (value === null || value === undefined || Number.isNaN(value)) return "—";
  if (value < 1024) return `${value} B`;
  const units = ["KiB", "MiB", "GiB", "TiB"];
  let size = value;
  let unit = "B";
  for (const next of units) {
    if (size < 1024) break;
    size /= 1024;
    unit = next;
  }
  return `${size >= 100 ? size.toFixed(0) : size.toFixed(1)} ${unit}`;
}

export function formatDurationMs(value: number | null | undefined): string {
  if (value === null || value === undefined || Number.isNaN(value)) return "—";
  if (value < 1000) return `${Math.round(value)} ms`;
  const totalSeconds = Math.floor(value / 1000);
  const hours = Math.floor(totalSeconds / 3600);
  const minutes = Math.floor((totalSeconds % 3600) / 60);
  const seconds = totalSeconds % 60;
  if (hours > 0) return `${hours}h ${String(minutes).padStart(2, "0")}m`;
  if (minutes > 0) return `${minutes}m ${String(seconds).padStart(2, "0")}s`;
  const fraction = value / 1000;
  return `${fraction >= 10 ? fraction.toFixed(0) : fraction.toFixed(1)}s`;
}

/** Parse an ISO/RFC3339 timestamp; returns null when unparseable. */
export function parseTime(value: string | null | undefined): Date | null {
  if (!value) return null;
  const date = new Date(value);
  return Number.isNaN(date.getTime()) ? null : date;
}

/** "2026-07-17 01:46:55 UTC" — stable, timezone-explicit rendering. */
export function formatUtc(value: string | null | undefined): string {
  const date = parseTime(value);
  if (!date) return value ? String(value) : "—";
  const pad = (n: number) => String(n).padStart(2, "0");
  return (
    `${date.getUTCFullYear()}-${pad(date.getUTCMonth() + 1)}-${pad(date.getUTCDate())} ` +
    `${pad(date.getUTCHours())}:${pad(date.getUTCMinutes())}:${pad(date.getUTCSeconds())} UTC`
  );
}

export function formatRelative(value: string | null | undefined): string {
  const date = parseTime(value);
  if (!date) return "—";
  const deltaMs = Date.now() - date.getTime();
  const future = deltaMs < 0;
  const abs = Math.abs(deltaMs);
  const minutes = Math.floor(abs / 60_000);
  const hours = Math.floor(abs / 3_600_000);
  const days = Math.floor(abs / 86_400_000);
  let text: string;
  if (abs < 60_000) text = "just now";
  else if (minutes < 60) text = `${minutes}m`;
  else if (hours < 24) text = `${hours}h`;
  else text = `${days}d`;
  if (text === "just now") return text;
  return future ? `in ${text}` : `${text} ago`;
}

export function formatPercent(part: number, total: number): string {
  if (!total) return "—";
  const pct = (part / total) * 100;
  // One decimal is misleading at the extremes: a non-zero share rounding to
  // "0.0%" looks empty, and 99.95% rounding to "100.0%" looks complete.
  if (pct > 0 && pct < 0.1) return "<0.1%";
  if (pct < 100 && pct > 99.9) return ">99.9%";
  return `${pct.toFixed(1)}%`;
}

/** Truncate a long identifier for display, keeping head and tail. */
export function truncateMiddle(value: string, max = 32): string {
  if (value.length <= max) return value;
  const head = Math.ceil((max - 1) / 2);
  const tail = Math.floor((max - 1) / 2);
  return `${value.slice(0, head)}…${value.slice(value.length - tail)}`;
}

/** Human label for an object type code (accepts both report-stream and projection vocabularies). */
export function objectTypeLabel(type: string | null | undefined): string {
  switch ((type ?? "").toLowerCase()) {
    case "roa":
      return "ROA";
    case "mft":
    case "manifest":
      return "Manifest";
    case "crl":
      return "CRL";
    case "cer":
    case "certificate":
      return "Certificate";
    case "asa":
    case "aspa":
      return "ASPA";
    case "gbr":
      return "Ghostbusters";
    case "ee":
      return "EE Certificate";
    case "router_certificate":
    case "router_cert":
      return "Router certificate";
    case "other":
      return "Other";
    default:
      return type ? type.toUpperCase() : "—";
  }
}
