/**
 * Day-change badge computation per TZ §4.1.17.
 *
 * Returns the day-offset between a time's local date and the query
 * (user's requested) date. Returns 0 when the time falls on the query
 * date (no badge shown).
 *
 * Each time type (scheduled / expected / actual) should call this
 * independently — badges for different time types of the same flight
 * may differ (e.g. scheduled +0 but actual +1 due to delay crossing
 * midnight).
 *
 * The dayChange value is supplied by the API on each ITimesSet.dayChange.value
 * field; this helper provides client-side computation for cases where a
 * query-date baseline is available (e.g. when API value is absent or
 * verification is needed).
 */

const ISO_DATE_RE = /^(\d{4})-(\d{2})-(\d{2})/;

/**
 * Extract the calendar date components from an ISO local string without
 * TZ reprojection. Handles both bare ("2026-04-15T23:30:00") and
 * offset-aware ("2026-04-15T23:30:00+03:00") forms.
 *
 * Returns [year, month (0-based), day] or null on failure.
 */
function extractLocalDateParts(
  iso: string,
): [number, number, number] | null {
  const m = ISO_DATE_RE.exec(iso);
  if (!m || !m[1] || !m[2] || !m[3]) return null;
  return [parseInt(m[1], 10), parseInt(m[2], 10) - 1, parseInt(m[3], 10)];
}

/**
 * Compute the day-change badge value per TZ §4.1.17.
 *
 * @param timeLocalIso  The local time string from the API (may have TZ offset
 *                      suffix — interpreted as the *airport-local* wall-clock).
 * @param queryDateYyyymmdd  The user's requested search date ("YYYY-MM-DD"),
 *                           which is the baseline "A" in TZ §4.1.17.
 * @returns Integer day offset (B - A). 0 means same day → no badge shown.
 *          Positive = later date, negative = earlier date.
 */
export function computeDayChange(
  timeLocalIso: string | null | undefined,
  queryDateYyyymmdd: string,
): number {
  if (!timeLocalIso) return 0;

  const timeParts = extractLocalDateParts(timeLocalIso);
  const queryParts = extractLocalDateParts(queryDateYyyymmdd);
  if (!timeParts || !queryParts) return 0;

  // Compare calendar dates as UTC midnight to avoid DST edge cases.
  const timeMs = Date.UTC(timeParts[0], timeParts[1], timeParts[2]);
  const queryMs = Date.UTC(queryParts[0], queryParts[1], queryParts[2]);
  if (Number.isNaN(timeMs) || Number.isNaN(queryMs)) return 0;

  return Math.round((timeMs - queryMs) / (24 * 60 * 60 * 1000));
}

/**
 * Build the tooltip text for a day-change badge per TZ §4.1.17 rules 5–6:
 * - ±1        → "День" (Russian: «День»)
 * - ±2 and up → the relevant date in DD.MM.YYYY format, computed from the
 *               base ISO string's local date shifted by `dayChange` days.
 *
 * Uses string-based date extraction (no TZ reprojection) for correctness
 * on SSR and across viewer time zones.
 *
 * @param baseLocalIso  The time string whose local calendar date is the base.
 * @param dayChange     The badge value (non-zero; caller must guard against 0).
 */
export function dayChangeBadgeTooltip(
  baseLocalIso: string,
  dayChange: number,
): string {
  const abs = Math.abs(dayChange);
  if (abs === 0) return "";
  if (abs === 1) return "День";

  // Shift the local date by dayChange days using UTC arithmetic.
  const parts = extractLocalDateParts(baseLocalIso);
  if (!parts) return "";

  const shiftedMs = Date.UTC(parts[0], parts[1], parts[2] + dayChange);
  const shifted = new Date(shiftedMs);
  const dd = String(shifted.getUTCDate()).padStart(2, "0");
  const mm = String(shifted.getUTCMonth() + 1).padStart(2, "0");
  const yyyy = shifted.getUTCFullYear();
  return `${dd}.${mm}.${yyyy}`;
}

/**
 * Format a day-change numeric value as a badge label string.
 * Returns "" for 0, "+1" for 1, "-1" for -1, etc.
 */
export function formatDayChangeBadge(value: number): string {
  if (value === 0) return "";
  return value > 0 ? `+${value}` : String(value);
}
