/**
 * Date formatting utility.
 *
 * All timestamps stored and transmitted by the backend are in UTC (ISO 8601
 * strings).  This module provides helper functions that convert them to the
 * IANA timezone configured during the BanGUI setup wizard and format them
 * for display.
 *
 * Usage:
 * ```ts
 * import { formatDate, formatDateShort } from "../utils/formatDate";
 *
 * // Full date + time
 * formatDate("2024-06-15T13:45:00Z", "Europe/Berlin");
 * // → "15/06/2024, 15:45:00"
 *
 * // Date only
 * formatDateShort("2024-06-15T13:45:00Z", "Europe/Berlin");
 * // → "15/06/2024"
 * ```
 */

/** Fallback timezone used when no timezone is provided. */
const DEFAULT_TIMEZONE = "UTC";

/**
 * Validate that *tz* is a recognised IANA timezone identifier.
 *
 * Returns the validated string or ``DEFAULT_TIMEZONE`` on failure so that
 * callers always receive a safe value rather than an exception.
 *
 * @param tz - IANA timezone string to validate.
 * @returns The original *tz* if valid, otherwise ``"UTC"``.
 */
function safeTimezone(tz: string | undefined | null): string {
  if (!tz) return DEFAULT_TIMEZONE;
  try {
    // Intl.DateTimeFormat throws a RangeError for unknown timezone identifiers.
    Intl.DateTimeFormat(undefined, { timeZone: tz });
    return tz;
  } catch {
    return DEFAULT_TIMEZONE;
  }
}

/**
 * Format an ISO 8601 UTC timestamp as a localised date-and-time string.
 *
 * The exact format depends on the user's browser locale in combination with
 * the provided timezone (e.g. ``"15/06/2024, 15:45:00"`` for ``en-GB``).
 *
 * @param isoUtc  - UTC timestamp string, e.g. ``"2024-06-15T13:45:00Z"``.
 * @param timezone - IANA timezone identifier, e.g. ``"Europe/Berlin"``.
 *   Defaults to ``"UTC"``.
 * @returns A human-readable date-time string in the specified timezone.
 */
export function formatDate(
  isoUtc: string | null | undefined,
  timezone: string | null | undefined = DEFAULT_TIMEZONE,
): string {
  if (!isoUtc) return "—";
  try {
    return new Intl.DateTimeFormat(undefined, {
      timeZone: safeTimezone(timezone),
      year: "numeric",
      month: "2-digit",
      day: "2-digit",
      hour: "2-digit",
      minute: "2-digit",
      second: "2-digit",
    }).format(new Date(isoUtc));
  } catch {
    return isoUtc;
  }
}

/**
 * Format an ISO 8601 UTC timestamp as a localised date-only string.
 *
 * @param isoUtc  - UTC timestamp string.
 * @param timezone - IANA timezone identifier.  Defaults to ``"UTC"``.
 * @returns A date-only string, e.g. ``"15/06/2024"``.
 */
export function formatDateShort(
  isoUtc: string | null | undefined,
  timezone: string | null | undefined = DEFAULT_TIMEZONE,
): string {
  if (!isoUtc) return "—";
  try {
    return new Intl.DateTimeFormat(undefined, {
      timeZone: safeTimezone(timezone),
      year: "numeric",
      month: "2-digit",
      day: "2-digit",
    }).format(new Date(isoUtc));
  } catch {
    return isoUtc;
  }
}

/**
 * Format an ISO 8601 UTC timestamp as a relative time string (e.g. "3 minutes
 * ago") using the browser's `Intl.RelativeTimeFormat`.
 *
 * Falls back to {@link formatDate} when the difference cannot be computed.
 *
 * @param isoUtc  - UTC timestamp string.
 * @param timezone - IANA timezone identifier used for the fallback format.
 * @returns A relative time string or a full date-time string as fallback.
 */
export function formatRelative(
  isoUtc: string | null | undefined,
  timezone: string | null | undefined = DEFAULT_TIMEZONE,
): string {
  if (!isoUtc) return "—";
  try {
    const then = new Date(isoUtc).getTime();
    const now = Date.now();
    const diffSeconds = Math.round((then - now) / 1000);
    const absDiff = Math.abs(diffSeconds);

    const rtf = new Intl.RelativeTimeFormat(undefined, { numeric: "auto" });

    if (absDiff < 60) return rtf.format(diffSeconds, "second");
    if (absDiff < 3600) return rtf.format(Math.round(diffSeconds / 60), "minute");
    if (absDiff < 86400) return rtf.format(Math.round(diffSeconds / 3600), "hour");
    if (absDiff < 86400 * 30) return rtf.format(Math.round(diffSeconds / 86400), "day");
    return formatDate(isoUtc, timezone);
  } catch {
    return formatDate(isoUtc, timezone);
  }
}

/**
 * Format an ISO 8601 timestamp for display with local browser timezone.
 *
 * Keeps parity with existing code paths in the UI that render full date+time
 * strings inside table rows.
 */
export function formatTimestamp(iso: string): string {
  try {
    return new Date(iso).toLocaleString(undefined, {
      year: "numeric",
      month: "2-digit",
      day: "2-digit",
      hour: "2-digit",
      minute: "2-digit",
      second: "2-digit",
    });
  } catch {
    return iso;
  }
}

/**
 * Format a duration in seconds to a compact text representation.
 */
export function formatSeconds(seconds: number): string {
  if (seconds < 0) return "permanent";
  if (seconds < 60) return `${String(seconds)} s`;
  if (seconds < 3600) return `${String(Math.round(seconds / 60))} min`;
  return `${String(Math.round(seconds / 3600))} h`;
}
