/**
 * Default sort for Online-Board results per TZ §4.1.13.2.
 *
 * Rules:
 * - Flight-number / Route / Departure modes: sort by departure time
 *   (first leg for MultiLeg), ascending.
 * - Arrival mode: sort by arrival time (last leg for MultiLeg), ascending.
 *
 * Day-ordering (yesterday < today < tomorrow) emerges naturally because the
 * API returns absolute ISO timestamps (`ITimesSet.local` is a local ISO
 * string) and comparing their millisecond values directly gives the correct
 * ordering across midnight boundaries.
 *
 * Flights whose relevant timestamp is missing (null / undefined) are pushed
 * to the end of the list.
 *
 * @module
 */

import type { ISimpleFlight } from "./types.js";

/** Which time field drives the sort. */
export type SortMode = "flight-number" | "route" | "departure" | "arrival";

/**
 * Resolve the ISO timestamp string used for sorting a single flight.
 *
 * - Departure modes: first leg's `scheduledDeparture.local`.
 * - Arrival mode: last leg's `scheduledArrival.local`.
 *
 * Returns `null` when the timestamp is absent.
 */
function resolveIso(flight: ISimpleFlight, useArrival: boolean): string | null {
  if (flight.routeType === "MultiLeg") {
    const legs = flight.legs;
    const leg = useArrival
      ? legs[legs.length - 1]
      : legs[0];
    if (!leg) return null;
    if (useArrival) {
      return leg.arrival?.times?.scheduledArrival?.local ?? null;
    }
    return leg.departure?.times?.scheduledDeparture?.local ?? null;
  }

  // Direct flight
  const leg = flight.leg;
  if (useArrival) {
    return leg?.arrival?.times?.scheduledArrival?.local ?? null;
  }
  return leg?.departure?.times?.scheduledDeparture?.local ?? null;
}

/**
 * Returns a comparator for `Array.sort` that orders flights per §4.1.13.2.
 */
export function compareFlightsForMode(
  mode: SortMode,
): (a: ISimpleFlight, b: ISimpleFlight) => number {
  const useArrival = mode === "arrival";
  return (a, b) => {
    const isoA = resolveIso(a, useArrival);
    const isoB = resolveIso(b, useArrival);
    if (isoA === null && isoB === null) return 0;
    if (isoA === null) return 1;  // push missing to end
    if (isoB === null) return -1;
    const tA = Date.parse(isoA);
    const tB = Date.parse(isoB);
    if (Number.isNaN(tA) && Number.isNaN(tB)) return 0;
    if (Number.isNaN(tA)) return 1;
    if (Number.isNaN(tB)) return -1;
    return tA - tB;
  };
}

/**
 * Return a sorted copy of `flights` per TZ §4.1.13.2.
 * Does not mutate the input array.
 */
export function sortFlights(
  flights: ISimpleFlight[],
  mode: SortMode,
): ISimpleFlight[] {
  return [...flights].sort(compareFlightsForMode(mode));
}
