/**
 * Schedule API functions.
 *
 * Pure functions -- each takes an `ApiClient` as a parameter (dependency
 * injection). No React hooks, no context, no side effects.
 *
 * @module
 */

import type { ApiClient } from "@/shared/api/client.js";
import type {
  IScheduleSearchRequest,
  IScheduleResponse,
  IScheduleDetailsResponse,
  IScheduleCalendarParams,
  IScheduleDaysResponse,
} from "./types.js";

// ---------------------------------------------------------------------------
// API functions
// ---------------------------------------------------------------------------

/**
 * Search schedule flights.
 * Maps to: `GET schedule/1?departure=SVO&arrival=LED&dateFrom=2026-04-18&dateTo=2026-04-26&...`
 *
 * Mirrors Angular's ScheduleApiService.getFlights which:
 *  - uses GET with query params (not POST with JSON body)
 *  - sends dateTo = requested end + 1 day (half-open interval)
 *  - coerces `connections` to a string ('0' / '1')
 *  - drops empty timeFrom/timeTo
 */
export async function searchSchedule(
  client: ApiClient,
  params: IScheduleSearchRequest,
  signal?: AbortSignal,
): Promise<IScheduleResponse> {
  const query: Record<string, string> = {
    departure: params.departure,
    arrival: params.arrival,
    dateFrom: params.dateFrom,
    dateTo: addOneDayIso(params.dateTo),
    connections: String(params.connections ?? 1),
  };
  // TIRREDESIGN-11: the slider carries its value as four-digit 'HHMM' but
  // the endpoint only honours the colon-delimited 'HH:MM:SS' form (Angular's
  // ApiFormatterService.formatTime). '2400' collapses to '23:59:59'.
  const tf = formatApiTime(params.timeFrom);
  const tt = formatApiTime(params.timeTo);
  if (tf) query["timeFrom"] = tf;
  if (tt) query["timeTo"] = tt;
  if (params.attribute) query["attribute"] = String(params.attribute);
  return client.get<IScheduleResponse>(
    `flights/1/${client.locale}/schedule`,
    query,
    signal,
  );
}

function formatApiTime(hhmm: string | undefined): string | undefined {
  if (!hhmm || hhmm.length !== 4) return undefined;
  if (hhmm === "2400") return "23:59:59";
  return `${hhmm.slice(0, 2)}:${hhmm.slice(2, 4)}:00`;
}

/** Return the date one day after a yyyy-MM-dd string (passthrough on bad input). */
function addOneDayIso(iso: string): string {
  const m = /^(\d{4})-(\d{2})-(\d{2})(.*)$/.exec(iso);
  if (!m) return iso;
  const [, y, mm, dd, tail] = m;
  const d = new Date(Number(y), Number(mm) - 1, Number(dd));
  d.setDate(d.getDate() + 1);
  const ny = d.getFullYear().toString();
  const nm = (d.getMonth() + 1).toString().padStart(2, "0");
  const nd = d.getDate().toString().padStart(2, "0");
  return `${ny}-${nm}-${nd}${tail ?? ""}`;
}

/**
 * Get multi-flight schedule details.
 * Maps to: `GET schedule/details?flights[0]=...&dates[0]=...&departure=...&arrival=...`
 *
 * The Angular service uses indexed query params (flights[0], flights[1], etc.).
 * We build the query object to match that format.
 */
export async function getScheduleDetails(
  client: ApiClient,
  params: {
    flights: string[];
    dates: string[];
    departure: string;
    arrival: string;
  },
): Promise<IScheduleDetailsResponse> {
  // Omit blank departure/arrival so the upstream doesn't reject the
  // request. Angular's details-data-source.service falls back to
  // per-leg values when the page-level params are missing; the
  // backend accepts the request either way.
  const query: Record<string, string> = {};
  if (params.departure) query["departure"] = params.departure;
  if (params.arrival) query["arrival"] = params.arrival;

  for (let i = 0; i < params.flights.length; i++) {
    const flight = params.flights[i];
    if (flight !== undefined) query[`flights[${i}]`] = flight;
  }

  for (let i = 0; i < params.dates.length; i++) {
    const date = params.dates[i];
    if (date !== undefined) query[`dates[${i}]`] = date;
  }

  return client.get<IScheduleDetailsResponse>(`flights/v1.1/${client.locale}/schedule/details`, query);
}

/**
 * Get available calendar days for a given route.
 * Maps to: `GET days/{date}/382/{param}/schedule/v1`
 *
 * The API returns `{ days: "1111110001..." }` — a 382-char bitmask
 * where each character represents a day starting from the requested
 * base date. '1' = available, '0' = no flights.
 */
export async function getScheduleCalendarDays(
  client: ApiClient,
  params: IScheduleCalendarParams,
): Promise<string[]> {
  const routeSegment = params.connections
    ? `connections/${params.departure}-${params.arrival}-1`
    : `route/${params.departure}-${params.arrival}`;

  const path = `flights/v1/${client.locale}/days/${params.date}/382/${routeSegment}/schedule/`;

  const response = await client.get<IScheduleDaysResponse>(path);
  return parseCalendarDays(response.days, params.date);
}

// ---------------------------------------------------------------------------
// Internal helpers
// ---------------------------------------------------------------------------

/**
 * Parse the /days response into an array of yyyy-MM-dd strings.
 *
 * Handles two shapes:
 *  - legacy comma-separated ("2025-01-01,2025-01-02,…")
 *  - bitmask ("1111000…") where each position maps to a day starting from
 *    the requested base date. This is what the upstream actually returns today.
 */
function parseCalendarDays(days: string, baseDate: string): string[] {
  if (!days) return [];
  if (/^[01]+$/.test(days)) {
    return bitmaskToDates(days, baseDate);
  }
  return days.split(",").map((d) => d.trim()).filter(Boolean);
}

function bitmaskToDates(bitmask: string, baseDate: string): string[] {
  // baseDate is yyyy-MM-dd (possibly with Txx:xx:xx suffix).
  const iso = baseDate.includes("T")
    ? baseDate.split("T")[0] ?? baseDate
    : baseDate;
  const [y, m, d] = iso.split("-");
  if (!y || !m || !d) return [];
  const cursor = new Date(Number(y), Number(m) - 1, Number(d));

  const result: string[] = [];
  for (let i = 0; i < bitmask.length; i++) {
    if (bitmask[i] === "1") {
      const yy = cursor.getFullYear().toString();
      const mm = (cursor.getMonth() + 1).toString().padStart(2, "0");
      const dd = cursor.getDate().toString().padStart(2, "0");
      result.push(`${yy}-${mm}-${dd}`);
    }
    cursor.setDate(cursor.getDate() + 1);
  }
  return result;
}
