/**
 * Timeline time-calculation algorithm per TZ §4.1.15.7.
 *
 * Rules (R94–R98):
 *   R94 — Total minutes = arrTime (actual ?? estimated ?? scheduled) UTC
 *          − depTime (actual) UTC
 *   R95 — Elapsed minutes «В пути» = now UTC − depTime UTC
 *   R96 — Remaining minutes «До прилета» = arrTime UTC − now UTC
 *   R97 — Aircraft position % = clamp(elapsed / total, 0, 1) × 100
 *   R98 — Display format: omit zero leading units
 *          «45мин.» / «2ч. 15мин.» / «1д. 3ч. 5мин.»
 *
 * @module
 */

// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------

/** UTC ISO timestamp (or any string parseable by Date.parse) */
type ISOString = string;

/**
 * Time inputs for a single flight leg, all in UTC.
 * Names match the API field names used in IArrivalStationTimes.
 */
export interface TimelineInputs {
  /** UTC timestamp of actual block-off (wheels-up). Required for in-flight calc. */
  depActualUtc: ISOString | null | undefined;
  /** UTC timestamp of scheduled arrival. Always present. */
  arrScheduledUtc: ISOString;
  /** UTC timestamp of estimated (expected) arrival. Present during flight. */
  arrEstimatedUtc?: ISOString | null;
  /** UTC timestamp of actual arrival (block-on). Present after landing. */
  arrActualUtc?: ISOString | null;
}

/** Result of the §4.1.15.7 algorithm for one leg. */
export interface TimelineCalc {
  /**
   * Total scheduled flight duration in minutes.
   * Uses actual ?? estimated ?? scheduled arrival per R94.
   * 0 if departure actual is missing or timestamps are invalid.
   */
  totalMinutes: number;
  /**
   * Minutes elapsed since departure (В пути) per R95.
   * Clamped to [0, totalMinutes].
   */
  elapsedMinutes: number;
  /**
   * Minutes remaining until arrival (До прилета) per R96.
   * Clamped to [0, ∞).
   */
  remainingMinutes: number;
  /**
   * Aircraft position on the timeline as a percentage [0, 100] per R97.
   */
  positionPercent: number;
}

// ---------------------------------------------------------------------------
// Display format (R98)
// ---------------------------------------------------------------------------

const RU_UNITS = { d: "д.", h: "ч.", m: "мин." };
const EN_UNITS = { d: "d", h: "h", m: "m" };

/**
 * Format total minutes into a human-readable duration per TZ §4.1.15.7 R98.
 *
 * Zero leading units are omitted:
 *   - 45 min → «45мин.»
 *   - 90 min → «1ч. 30мин.»
 *   - 1505 min (1d 1h 5m) → «1д. 1ч. 5мин.»
 *
 * Minutes are always included (even if 0), matching the TZ format examples.
 *
 * @param minutes - Total duration in minutes (non-negative integer)
 * @param locale  - Any locale string; strings starting with "ru" use Cyrillic units
 */
export function formatTimelineDuration(minutes: number, locale = "en"): string {
  const units = locale.toLowerCase().startsWith("ru") ? RU_UNITS : EN_UNITS;

  const totalMins = Math.max(0, Math.floor(minutes));
  const days = Math.floor(totalMins / (60 * 24));
  const hours = Math.floor((totalMins % (60 * 24)) / 60);
  const mins = totalMins % 60;

  const parts: string[] = [];
  if (days > 0) parts.push(`${days}${units.d}`);
  if (hours > 0 || days > 0) parts.push(`${hours}${units.h}`);
  parts.push(`${mins}${units.m}`);

  return parts.join(" ");
}

// ---------------------------------------------------------------------------
// Core algorithm
// ---------------------------------------------------------------------------

/**
 * Pick the best arrival UTC timestamp for the total-minutes calculation.
 *
 * Per R94: use actual if available, then estimated, then scheduled.
 */
function bestArrivalUtc(inputs: TimelineInputs): ISOString {
  return inputs.arrActualUtc || inputs.arrEstimatedUtc || inputs.arrScheduledUtc;
}

/**
 * Compute timeline values for a single leg per TZ §4.1.15.7.
 *
 * Pass `nowMs` (milliseconds since epoch) to enable deterministic tests;
 * defaults to `Date.now()`.
 *
 * Returns zeros for all fields when:
 *  - `depActualUtc` is absent (flight not yet departed),
 *  - any timestamp is invalid / unparseable.
 */
export function computeTimelineCalc(
  inputs: TimelineInputs,
  nowMs: number = Date.now(),
): TimelineCalc {
  const zero: TimelineCalc = {
    totalMinutes: 0,
    elapsedMinutes: 0,
    remainingMinutes: 0,
    positionPercent: 0,
  };

  if (!inputs.depActualUtc) return zero;

  const depMs = Date.parse(inputs.depActualUtc);
  const arrMs = Date.parse(bestArrivalUtc(inputs));

  if (Number.isNaN(depMs) || Number.isNaN(arrMs)) return zero;
  if (arrMs <= depMs) return zero;

  // R94: total duration
  const totalMs = arrMs - depMs;
  const totalMinutes = Math.round(totalMs / 60_000);

  // R95: elapsed (В пути)
  const elapsedMs = Math.max(0, Math.min(totalMs, nowMs - depMs));
  const elapsedMinutes = Math.round(elapsedMs / 60_000);

  // R96: remaining (До прилета)
  const remainingMs = Math.max(0, arrMs - nowMs);
  const remainingMinutes = Math.round(remainingMs / 60_000);

  // R97: position %
  const positionPercent = Math.round((elapsedMs / totalMs) * 100);

  return { totalMinutes, elapsedMinutes, remainingMinutes, positionPercent };
}
