/**
 * Schedule URL serializer/parser.
 *
 * Pure functions -- no side effects, no Angular imports, no Date objects.
 * Byte-exact parity with Angular's ScheduleUrlBuilderService /
 * ScheduleUrlParserService.
 *
 * @module
 */

import { parseFlightUrlParams, buildFlightUrlParams } from "../online-board/url.js";
import type { IScheduleRouteDirectionParams, IScheduleFlightId } from "./types.js";

// ---------------------------------------------------------------------------
// Public types
// ---------------------------------------------------------------------------

/** Discriminated union of all parsed schedule URL parameter shapes */
export type ScheduleParams =
  | { type: "start" }
  | { type: "route"; outbound: IScheduleRouteDirectionParams }
  | { type: "roundtrip"; outbound: IScheduleRouteDirectionParams; inbound: IScheduleRouteDirectionParams }
  | { type: "details"; flights: IScheduleFlightId[] };

// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------

const SCHEDULE_PREFIX = "schedule";

// ---------------------------------------------------------------------------
// Validation helpers
// ---------------------------------------------------------------------------

function isValidDate(date: string): boolean {
  return /^\d{8}$/.test(date);
}

function isValidTimeRange(timeRange: string): boolean {
  return /^\d{8}$/.test(timeRange);
}

// ---------------------------------------------------------------------------
// Route direction params parser/builder
// ---------------------------------------------------------------------------

/**
 * Parse a schedule route direction params string.
 * Format: {dep}-{arr}-{dateFrom}-{dateTo}[-{timeFromTo}][-C{connections}]
 *
 * Both timeRange and connections are optional. Connections starts with 'C'.
 */
export function parseScheduleRouteParams(raw: string): IScheduleRouteDirectionParams | null {
  if (!raw) return null;

  const parts = raw.split("-");
  if (parts.length < 4) return null;

  const departure = parts[0];
  const arrival = parts[1];
  const dateFrom = parts[2];
  const dateTo = parts[3];

  if (departure === undefined || arrival === undefined || dateFrom === undefined || dateTo === undefined) return null;
  if (!isValidDate(dateFrom) || !isValidDate(dateTo)) return null;

  // Remaining parts are optional: timeRange, connections (prefixed with C)
  const rest = parts.slice(4);
  const { timeFrom, timeTo, connections } = parseOptionalRouteParams(rest);

  const result: IScheduleRouteDirectionParams = {
    departure,
    arrival,
    dateFrom,
    dateTo,
  };

  if (timeFrom !== undefined && timeTo !== undefined) {
    result.timeFrom = timeFrom;
    result.timeTo = timeTo;
  }

  if (connections !== undefined) {
    result.connections = connections;
  }

  return result;
}

function parseOptionalRouteParams(parts: string[]): {
  timeFrom?: string;
  timeTo?: string;
  connections?: number;
} {
  if (parts.length === 0) return {};

  if (parts.length === 2) {
    // Both timeRange and connections
    const timeRange = parts[0];
    const connStr = parts[1];
    if (timeRange !== undefined && connStr !== undefined) {
      const time = parseTimeRange(timeRange);
      const conn = parseConnections(connStr);
      return {
        ...time,
        ...(conn !== undefined ? { connections: conn } : {}),
      };
    }
    return {};
  }

  // Single part: either connections (starts with C) or timeRange
  const part = parts[0];
  if (part === undefined) return {};

  if (part.startsWith("C") || part.startsWith("\u0421")) {
    // C (Latin) or С (Cyrillic) -- Angular spec uses Cyrillic С in one test case
    const conn = parseConnections(part);
    return conn !== undefined ? { connections: conn } : {};
  }

  const time = parseTimeRange(part);
  return time;
}

function parseTimeRange(raw: string): { timeFrom: string; timeTo: string } | Record<string, never> {
  if (!raw || !isValidTimeRange(raw)) return {};
  return {
    timeFrom: raw.slice(0, 4),
    timeTo: raw.slice(4, 8),
  };
}

function parseConnections(raw: string): number | undefined {
  if (!raw || raw.length < 2) return undefined;
  const numStr = raw.slice(1);
  const num = Number(numStr);
  return Number.isNaN(num) ? undefined : num;
}

/**
 * Build a schedule route direction params string.
 */
export function buildScheduleRouteParams(params: IScheduleRouteDirectionParams): string {
  let base = `${params.departure}-${params.arrival}-${params.dateFrom}-${params.dateTo}`;

  if (params.timeFrom && params.timeTo) {
    base = `${base}-${params.timeFrom}${params.timeTo}`;
  }

  if (params.connections !== undefined) {
    base = `${base}-C${params.connections}`;
  }

  return base;
}

// ---------------------------------------------------------------------------
// Details flight segment parser
// ---------------------------------------------------------------------------

/**
 * Parse a catch-all flight details path into flight IDs.
 * Handles two formats:
 * 1. Simple: SU0012-20220501/SU0013-20220507
 * 2. With airports: SVO/SU0012-20220501/LED/LED/SU0013-20220507/SVO
 *    (3-letter airport codes are skipped)
 */
function parseFlightDetailsSegments(raw: string): IScheduleFlightId[] | null {
  if (!raw) return null;

  const segments = raw.split("/").filter(Boolean);
  if (segments.length === 0) return null;

  const flights: IScheduleFlightId[] = [];

  for (const segment of segments) {
    // Skip 3-letter airport codes (no dash, <= 3 chars)
    if (segment.length <= 3) continue;

    const parsed = parseFlightUrlParams(segment);
    if (parsed) {
      const flight: IScheduleFlightId = {
        carrier: parsed.carrier,
        flightNumber: parsed.flightNumber,
        date: parsed.date,
      };
      if (parsed.suffix !== undefined) {
        flight.suffix = parsed.suffix;
      }
      flights.push(flight);
    }
  }

  return flights.length > 0 ? flights : null;
}

// ---------------------------------------------------------------------------
// Top-level parse / build
// ---------------------------------------------------------------------------

/**
 * Parse a raw URL path into typed schedule params.
 * Returns null if the path does not match any known schedule URL shape.
 */
export function parseScheduleUrl(path: string): ScheduleParams | null {
  if (!path) return null;

  const normalized = path.startsWith("/") ? path.slice(1) : path;

  if (!normalized.startsWith(SCHEDULE_PREFIX)) return null;

  const rest = normalized.slice(SCHEDULE_PREFIX.length);

  // Start page
  if (!rest || rest === "/") return { type: "start" };

  if (!rest.startsWith("/")) return null;

  const afterPrefix = rest.slice(1);

  // Route search: route/{params}[/{returnParams}]
  if (afterPrefix.startsWith("route/")) {
    const routeContent = afterPrefix.slice("route/".length);
    if (!routeContent) return null;

    // Route params use "-" as separator, "/" separates outbound from inbound.
    // Try round-trip first (split on "/"), then fall back to one-way.
    const slashIdx = routeContent.indexOf("/");
    if (slashIdx >= 0) {
      const outboundRaw = routeContent.slice(0, slashIdx);
      const inboundRaw = routeContent.slice(slashIdx + 1);

      const parsedOutbound = parseScheduleRouteParams(outboundRaw);
      const parsedInbound = parseScheduleRouteParams(inboundRaw);

      if (parsedOutbound && parsedInbound) {
        return { type: "roundtrip", outbound: parsedOutbound, inbound: parsedInbound };
      }
    }

    // Try one-way (entire routeContent is a single route param set)
    const outbound = parseScheduleRouteParams(routeContent);
    if (outbound) {
      return { type: "route", outbound };
    }

    return null;
  }

  // Route without prefix "route/": must be details (catch-all)
  const flights = parseFlightDetailsSegments(afterPrefix);
  if (flights) {
    return { type: "details", flights };
  }

  return null;
}

/**
 * Build a URL path segment from typed schedule params.
 *
 * @returns the path segment without leading slash (e.g. "schedule/route/MOW-KUF-20220425-20220501")
 */
export function buildScheduleUrl(params: ScheduleParams): string {
  switch (params.type) {
    case "start":
      return SCHEDULE_PREFIX;

    case "route":
      return `${SCHEDULE_PREFIX}/route/${buildScheduleRouteParams(params.outbound)}`;

    case "roundtrip":
      return `${SCHEDULE_PREFIX}/route/${buildScheduleRouteParams(params.outbound)}/${buildScheduleRouteParams(params.inbound)}`;

    case "details": {
      const segments = params.flights.map((flight) => {
        return buildFlightUrlParams({
          carrier: flight.carrier,
          flightNumber: flight.flightNumber,
          date: flight.date,
          ...(flight.suffix !== undefined ? { suffix: flight.suffix } : {}),
        });
      });
      return `${SCHEDULE_PREFIX}/${segments.join("/")}`;
    }
  }
}
