/**
 * Flights Map 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 {
  FlightsMapSearchParams,
  FlightsMapCalendarParams,
  IDestinationsResponse,
  IFlightsMapDaysResponse,
} from "./types.js";

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

/**
 * Search flight destinations/routes on the map.
 * Maps to: `GET destinations/1?departure=...&arrival=...&dateFrom=...&dateTo=...&connections=0`
 */
export async function searchDestinations(
  client: ApiClient,
  params: FlightsMapSearchParams,
): Promise<IDestinationsResponse> {
  // Upstream expects yyyy-MM-DD with dashes on /destinations; internal callers
  // pass the compact yyyyMMdd we use everywhere else in the app. Matches
  // Angular's ApiFormatterService.formatDateOnly output.
  const query: Record<string, string> = {
    departure: params.departure,
    dateFrom: toDashedDate(params.dateFrom),
    dateTo: toDashedDate(params.dateTo),
    connections: String(params.connections ?? 0),
  };

  if (params.arrival) {
    query["arrival"] = params.arrival;
  }

  return client.get<IDestinationsResponse>(`flights/1/${client.locale}/destinations`, query);
}

function toDashedDate(yyyymmdd: string): string {
  if (/^\d{8}$/.test(yyyymmdd)) {
    return `${yyyymmdd.slice(0, 4)}-${yyyymmdd.slice(4, 6)}-${yyyymmdd.slice(6, 8)}`;
  }
  // Already dashed (or something else) — pass through.
  return yyyymmdd;
}

/**
 * Get available calendar days for a flights-map route.
 * Maps to: `GET days/{date}/200/{routeSegment}/flights-map/v1`
 *
 * The `days` response field is a binary string ("0110...") where each
 * character represents one day starting from `date`. We parse it into
 * an array of available date strings.
 */
export async function getFlightsMapCalendar(
  client: ApiClient,
  params: FlightsMapCalendarParams,
): Promise<string[]> {
  const routeSegment = buildRouteSegment(params);
  if (!routeSegment) return [];

  // Upstream expects yyyy-MM-DD with dashes on the /days path too.
  const path = `flights/v1/${client.locale}/days/${toDashedDate(params.date)}/200/${routeSegment}/flights-map/`;
  const response = await client.get<IFlightsMapDaysResponse>(path);
  return parseBinaryDays(response.days, params.date);
}

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

function buildRouteSegment(params: FlightsMapCalendarParams): string | null {
  if (params.departure && params.arrival) {
    return params.connections
      ? `connections/${params.departure}-${params.arrival}-1`
      : `route/${params.departure}-${params.arrival}`;
  }

  if (params.departure) {
    return `departure/${params.departure}`;
  }

  return null;
}

/**
 * Parse a binary days string ("01101...") into an array of ISO date strings
 * representing the available (=1) days, starting from `startDate`.
 */
function parseBinaryDays(days: string, startDate: string): string[] {
  if (!days) return [];

  const result: string[] = [];
  const start = parseYyyymmdd(startDate);
  if (!start) return [];

  for (let i = 0; i < days.length; i++) {
    if (days[i] === "1") {
      const d = new Date(start);
      d.setDate(d.getDate() + i);
      result.push(formatYyyymmdd(d));
    }
  }

  return result;
}

function parseYyyymmdd(s: string): Date | null {
  if (s.length !== 8) return null;
  const year = Number(s.slice(0, 4));
  const month = Number(s.slice(4, 6)) - 1;
  const day = Number(s.slice(6, 8));
  const d = new Date(year, month, day);
  return Number.isNaN(d.getTime()) ? null : d;
}

function formatYyyymmdd(d: Date): string {
  const y = d.getFullYear().toString();
  const m = (d.getMonth() + 1).toString().padStart(2, "0");
  const day = d.getDate().toString().padStart(2, "0");
  return `${y}${m}${day}`;
}
