/**
 * Ported from Angular's find-closest-flight utility. Picks the flight
 * whose departure (or arrival, for arrival-view) time is closest to
 * "now" on today's searches, or the first/last flight for future/past
 * days. The result gets auto-expanded and scrolled into view on the
 * search results card, matching Angular behavior.
 *
 * @module
 */

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

function firstLeg(flight: ISimpleFlight): IFlightLeg {
  if (flight.routeType === "Direct") return flight.leg;
  const first = flight.legs[0];
  if (!first) throw new Error("MultiLeg flight has no legs");
  return first;
}

function lastLeg(flight: ISimpleFlight): IFlightLeg {
  if (flight.routeType === "Direct") return flight.leg;
  const last = flight.legs[flight.legs.length - 1];
  if (!last) throw new Error("MultiLeg flight has no legs");
  return last;
}

function pickTime(flight: ISimpleFlight, isArrival: boolean): string | null {
  const leg = isArrival ? lastLeg(flight) : firstLeg(flight);
  const times = isArrival ? leg.arrival.times : leg.departure.times;
  const dep = (times as { scheduledDeparture?: { local?: string } }).scheduledDeparture;
  const arr = (times as { scheduledArrival?: { local?: string } }).scheduledArrival;
  const actualOff = (times as { actualBlockOff?: { local?: string } }).actualBlockOff;
  const actualOn = (times as { actualBlockOn?: { local?: string } }).actualBlockOn;
  const latestKey = isArrival ? "latestArrival" : "latestDeparture";
  const latest = (
    times as { latestDeparture?: { local?: string }; latestArrival?: { local?: string } }
  )[latestKey];
  if (isArrival) return actualOn?.local ?? latest?.local ?? arr?.local ?? null;
  return actualOff?.local ?? latest?.local ?? dep?.local ?? null;
}

function parseTimeMs(iso: string | null): number | null {
  if (!iso) return null;
  const ms = Date.parse(iso);
  return Number.isNaN(ms) ? null : ms;
}

/**
 * Returns `true` when `yyyymmdd` (e.g. '20260419') refers to today's
 * calendar day in the browser's local time.
 */
function isTodayYyyymmdd(yyyymmdd: string, now: Date = new Date()): boolean {
  if (yyyymmdd.length !== 8) return false;
  const todayYyyymmdd =
    String(now.getFullYear()) +
    String(now.getMonth() + 1).padStart(2, "0") +
    String(now.getDate()).padStart(2, "0");
  return yyyymmdd === todayYyyymmdd;
}

export interface ClosestOptions {
  /** 'yyyyMMdd' — the search date */
  searchDate: string;
  /** `true` when the list is an arrival view (use arrival times) */
  isArrival: boolean;
  /** Override current time for deterministic tests. */
  now?: Date;
}

/**
 * @returns the id of the flight whose time is closest to `now`, or the
 * boundary flight when the search is not for today. Returns `null`
 * when the list is empty.
 */
export function findClosestFlightId(
  flights: ISimpleFlight[],
  opts: ClosestOptions,
): string | null {
  if (!flights.length) return null;
  const now = opts.now ?? new Date();
  const nowMs = now.getTime();

  if (!isTodayYyyymmdd(opts.searchDate, now)) {
    // For future days, show the first flight; for past days, the last.
    // We've already short-circuited on `!flights.length` above so both
    // ends are defined.
    const first = flights[0];
    const last = flights[flights.length - 1];
    if (!first || !last) return null;
    const firstMs = parseTimeMs(pickTime(first, opts.isArrival));
    if (firstMs !== null && nowMs < firstMs) return first.id;
    return last.id;
  }

  let best: string | null = flights[0]?.id ?? null;
  let bestAbsDiff = Number.POSITIVE_INFINITY;
  for (const f of flights) {
    const ms = parseTimeMs(pickTime(f, opts.isArrival));
    if (ms === null) continue;
    const abs = Math.abs(ms - nowMs);
    if (abs < bestAbsDiff) {
      bestAbsDiff = abs;
      best = f.id;
    }
  }
  return best;
}
