/**
 * Online Board 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 {
  IBoardResponse,
  IDaysResponse,
  FlightRequestType,
} from "./types.js";

// ---------------------------------------------------------------------------
// Parameter types
// ---------------------------------------------------------------------------

export interface SearchFlightsParams {
  /** Flight number search: e.g. "SU100" */
  flightNumber?: string;
  /** Date range start — yyyyMMdd format (used by the API as `dateFrom`) */
  dateFrom: string;
  /** Date range end — yyyyMMdd format (used by the API as `dateTo`) */
  dateTo: string;
  /** Departure airport IATA code */
  departure?: string;
  /** Arrival airport IATA code */
  arrival?: string;
  /** Time range start — HHmm format */
  timeFrom?: string;
  /** Time range end — HHmm format */
  timeTo?: string;
}

export interface FlightDetailsParams {
  /** Carrier code + flight number, e.g. "SU100" */
  flights: string;
  /** Date in yyyy-MM-dd format */
  dates: string;
}

export interface CalendarParams {
  /** Base date — yyyy-MM-dd format */
  date: string;
  /** Search type discriminator */
  searchType: FlightRequestType;
  /** Flight number (for "flight" type), e.g. "SU100" */
  flightNumber?: string;
  /** Departure airport IATA code (for "departure" or "route" type) */
  departure?: string;
  /** Arrival airport IATA code (for "arrival" or "route" type) */
  arrival?: string;
}

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

/**
 * Search flights on the online board.
 * Maps to: `GET /board?flightNumber=...&dateFrom=...&dateTo=...&...`
 */
export async function searchFlights(
  client: ApiClient,
  params: SearchFlightsParams,
): Promise<IBoardResponse> {
  const query: Record<string, string> = {
    dateFrom: params.dateFrom,
    dateTo: params.dateTo,
  };

  if (params.flightNumber) query["flightNumber"] = params.flightNumber;
  if (params.departure) query["departure"] = params.departure;
  if (params.arrival) query["arrival"] = params.arrival;
  if (params.timeFrom) query["timeFrom"] = params.timeFrom;
  if (params.timeTo) query["timeTo"] = params.timeTo;

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

/**
 * Get flight details.
 * Maps to: `GET /onlineboard/details?flights=SU100&dates=2025-01-15`
 */
export async function getFlightDetails(
  client: ApiClient,
  params: FlightDetailsParams,
): Promise<IBoardResponse> {
  return client.get<IBoardResponse>(`flights/v1.1/${client.locale}/onlineboard/details`, {
    flights: params.flights,
    dates: params.dates,
  });
}

/**
 * Get available calendar days for a given search context.
 * Maps to: `GET /v1/days/{date}/31/{searchType}/{searchParams}/board/`
 *
 * The API returns `{ days: "1110101..." }` — a 31-char bitmask where each
 * character represents a day starting from (baseDate - 1). '1' = flights
 * available, '0' = no flights. This function converts enabled positions
 * into yyyyMMdd date strings.
 */
export async function getCalendarDays(
  client: ApiClient,
  params: CalendarParams,
): Promise<string[]> {
  const searchSegment = buildCalendarSearchSegment(params);
  const path = `flights/v1/${client.locale}/days/${params.date}/31/${searchSegment}/board/`;

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

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

function buildCalendarSearchSegment(params: CalendarParams): string {
  switch (params.searchType) {
    case "flight":
      return `flight/${params.flightNumber ?? ""}`;
    case "departure":
      return `departure/${params.departure ?? ""}`;
    case "arrival":
      return `arrival/${params.arrival ?? ""}`;
    case "route": {
      const dep = params.departure ?? "";
      const arr = params.arrival ?? "";
      return `route/${dep}-${arr}`;
    }
  }
}

/**
 * Parse a calendar days bitmask into an array of yyyyMMdd date strings.
 *
 * The API returns a 31-char string of '1' and '0'. Each position maps to
 * a day starting from (baseDate - 1 day). Positions with '1' are enabled.
 *
 * Matches Angular's search-page-base.component.ts logic:
 *   date.setDate(date.getDate() - 1);
 *   for (var i = 0; i < res.days.length; i++) { ... date.setDate(date.getDate() + 1); }
 */
function parseCalendarDays(days: string, baseDate: string): string[] {
  if (!days) return [];

  // If it looks like a bitmask (only 0s and 1s), convert to dates
  if (/^[01]+$/.test(days)) {
    return bitmaskToDates(days, baseDate);
  }

  // Fallback: comma-separated date list (legacy format)
  return days.split(",").map((d) => d.trim()).filter(Boolean);
}

/**
 * Convert a bitmask string to yyyyMMdd date strings.
 * Base date is the search date; iteration starts from (baseDate - 1 day).
 */
function bitmaskToDates(bitmask: string, baseDate: string): string[] {
  // Parse baseDate — could be "yyyy-MM-ddT00:00:00" or "yyyyMMdd"
  let year: number, month: number, day: number;
  if (baseDate.includes("-")) {
    const parts = baseDate.split("T")[0]!.split("-");
    year = parseInt(parts[0]!, 10);
    month = parseInt(parts[1]!, 10) - 1;
    day = parseInt(parts[2]!, 10);
  } else {
    year = parseInt(baseDate.slice(0, 4), 10);
    month = parseInt(baseDate.slice(4, 6), 10) - 1;
    day = parseInt(baseDate.slice(6, 8), 10);
  }

  // Start from baseDate - 1 day (matching Angular)
  const cursor = new Date(year, month, day);
  cursor.setDate(cursor.getDate() - 1);

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

  return result;
}
