// ---------------------------------------------------------------------------
// Date / time formatting
// ---------------------------------------------------------------------------
// All helpers pin the timezone to America/Asuncion so the output is identical
// on the server (often UTC) and the client (user's local TZ).  This prevents
// React hydration mismatches like "07:20 PM" (server) vs "04:20 PM" (client).
//
// IMPORTANT — parseDate() must be used instead of raw `new Date(str)` so that
// ISO-like strings without a timezone suffix (e.g. "2026-04-02T14:00:00") are
// always treated as UTC.  Without this, the same string produces a different
// instant on server (Node TZ) vs client (browser / DevTools TZ).
// ---------------------------------------------------------------------------

export const EVENT_TIMEZONE = 'America/Asuncion';

type Locale = 'en' | 'es';

function pickLocale(locale: Locale): string {
  return locale === 'es' ? 'es-ES' : 'en-US';
}

// Matches ISO-like strings that have NO timezone indicator (Z, +HH:MM, etc.)
const NAIVE_ISO_RE = /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}(:\d{2}(\.\d+)?)?$/;

/**
 * Parse a date string into a deterministic Date object.
 *
 * If the string looks like an ISO datetime but lacks a timezone suffix it is
 * ambiguous — `new Date()` would interpret it in the environment's local
 * timezone which differs between Node (SSR) and the browser (hydration).
 * We normalise by appending "Z" so parsing always targets UTC.
 */
export function parseDate(dateStr: string): Date {
  if (NAIVE_ISO_RE.test(dateStr)) {
    return new Date(dateStr + 'Z');
  }
  return new Date(dateStr);
}

/**
 * "Sat, Feb 14" / "sáb, 14 feb"
 */
export function formatDateShort(dateStr: string, locale: Locale = 'en'): string {
  return parseDate(dateStr).toLocaleDateString(pickLocale(locale), {
    weekday: 'short',
    month: 'short',
    day: 'numeric',
    timeZone: EVENT_TIMEZONE,
  });
}

/**
 * "Saturday, February 14, 2026" / "sábado, 14 de febrero de 2026"
 */
export function formatDateLong(dateStr: string, locale: Locale = 'en'): string {
  return parseDate(dateStr).toLocaleDateString(pickLocale(locale), {
    weekday: 'long',
    year: 'numeric',
    month: 'long',
    day: 'numeric',
    timeZone: EVENT_TIMEZONE,
  });
}

/**
 * "February 14, 2026" / "14 de febrero de 2026"  (no weekday)
 */
export function formatDateMedium(dateStr: string, locale: Locale = 'en'): string {
  return parseDate(dateStr).toLocaleDateString(pickLocale(locale), {
    year: 'numeric',
    month: 'long',
    day: 'numeric',
    timeZone: EVENT_TIMEZONE,
  });
}

/**
 * "Feb 14, 2026" / "14 feb 2026"
 */
export function formatDateCompact(dateStr: string, locale: Locale = 'en'): string {
  return parseDate(dateStr).toLocaleDateString(pickLocale(locale), {
    month: 'short',
    day: 'numeric',
    year: 'numeric',
    timeZone: EVENT_TIMEZONE,
  });
}

/**
 * "04:30 PM" / "16:30"
 */
export function formatTime(dateStr: string, locale: Locale = 'en'): string {
  return parseDate(dateStr).toLocaleTimeString(pickLocale(locale), {
    hour: '2-digit',
    minute: '2-digit',
    timeZone: EVENT_TIMEZONE,
  });
}

/**
 * "Feb 14, 2026, 04:30 PM" — compact date + time combined
 */
export function formatDateTime(dateStr: string, locale: Locale = 'en'): string {
  return parseDate(dateStr).toLocaleString(pickLocale(locale), {
    month: 'short',
    day: 'numeric',
    year: 'numeric',
    hour: '2-digit',
    minute: '2-digit',
    timeZone: EVENT_TIMEZONE,
  });
}

/**
 * "Sat, Feb 14, 04:30 PM" — short date + time combined
 */
export function formatDateTimeShort(dateStr: string, locale: Locale = 'en'): string {
  return parseDate(dateStr).toLocaleString(pickLocale(locale), {
    weekday: 'short',
    month: 'short',
    day: 'numeric',
    hour: '2-digit',
    minute: '2-digit',
    timeZone: EVENT_TIMEZONE,
  });
}

// ---------------------------------------------------------------------------
// Price formatting
// ---------------------------------------------------------------------------

/**
 * Format price - shows decimals only if needed
 * Uses space as thousands separator (common in Paraguay)
 * Examples:
 *   45000 PYG -> "45 000 PYG" (no decimals)
 *   41.44 PYG -> "41,44 PYG" (with decimals)
 */
export function formatPrice(price: number, currency: string = 'PYG'): string {
  const hasDecimals = price % 1 !== 0;
  
  // Format the integer and decimal parts separately
  const intPart = Math.floor(Math.abs(price));
  const decPart = Math.abs(price) - intPart;
  
  // Format integer part with space as thousands separator
  const intFormatted = intPart.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
  
  // Build final string
  let result = price < 0 ? '-' : '';
  result += intFormatted;
  
  // Add decimals only if present
  if (hasDecimals) {
    const decStr = decPart.toFixed(2).substring(2); // Get just the decimal digits
    result += ',' + decStr;
  }
  
  return `${result} ${currency}`;
}

/**
 * Format currency amount (alias for formatPrice for backward compatibility)
 */
export function formatCurrency(amount: number, currency: string = 'PYG'): string {
  return formatPrice(amount, currency);
}

/**
 * Format a Paraguayan RUC for display as "base-checkdigit" (e.g. 1234567-9).
 * Legacy records stored digits only; insert the dash before the check digit.
 */
export function formatRucDisplay(ruc: string | null | undefined): string {
  if (!ruc) return '';
  if (ruc.includes('-')) return ruc;
  const digits = ruc.replace(/\D/g, '');
  if (digits.length < 2) return ruc;
  return `${digits.slice(0, -1)}-${digits.slice(-1)}`;
}

// ---------------------------------------------------------------------------
// Payment helpers
// ---------------------------------------------------------------------------

type TpagoLinkConfig = {
  tpagoLink?: string | null;
  tpagoLink2?: string | null;
  tpagoLink3?: string | null;
  tpagoLink4?: string | null;
  tpagoLink5?: string | null;
};

/**
 * Select the TPago payment link that matches the number of tickets being
 * purchased (1-5). Each link has a fixed amount baked in, so the quantity
 * determines which one to use. Falls back to the base (1-ticket) link when a
 * specific quantity link isn't configured.
 */
export function getTpagoLink(
  config: TpagoLinkConfig | null | undefined,
  ticketCount: number
): string | null {
  if (!config) return null;
  const count = Math.min(Math.max(1, Math.floor(ticketCount || 1)), 5);
  const key = (count <= 1 ? 'tpagoLink' : `tpagoLink${count}`) as keyof TpagoLinkConfig;
  return config[key] || config.tpagoLink || null;
}

/**
 * Spots left for an event, trusting the server's `availableSeats` (which uses
 * the same seat-holding formula the booking API enforces: paid + claimed
 * seats count, abandoned pending bookings don't). Falls back to deriving it
 * from the counts for older API responses.
 */
export function eventSpotsLeft(event: {
  capacity: number;
  bookedCount?: number;
  claimedCount?: number;
  availableSeats?: number;
}): number {
  if (typeof event.availableSeats === 'number') {
    return Math.max(0, event.availableSeats);
  }
  return Math.max(
    0,
    (event.capacity ?? 0) - (event.bookedCount ?? 0) - (event.claimedCount ?? 0)
  );
}

export function isEventSoldOut(event: {
  capacity: number;
  bookedCount?: number;
  claimedCount?: number;
  availableSeats?: number;
}): boolean {
  return eventSpotsLeft(event) <= 0;
}
