/**
 * Schedule URL parity tests.
 *
 * Registers the Schedule URL serializer against the generic URL parity
 * harness. Tests fixture corpus + fast-check fuzz roundtrip.
 *
 * @module
 */

import * as fc from "fast-check";
import { parseScheduleUrl, buildScheduleUrl } from "@/features/schedule/url.js";
import type { ScheduleParams } from "@/features/schedule/url.js";
import { defineUrlParityTests } from "./harness.js";

// ---------------------------------------------------------------------------
// fast-check arbitraries for ScheduleParams
// ---------------------------------------------------------------------------

const ALPHA_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("") as [string, ...string[]];

const alphaCharArb: fc.Arbitrary<string> = fc.constantFrom(...ALPHA_CHARS);

/** 2-char uppercase carrier code */
const carrierArb: fc.Arbitrary<string> = fc
  .tuple(alphaCharArb, alphaCharArb)
  .map(([a, b]) => `${a}${b}`);

/** 3-char uppercase IATA station code */
const stationArb: fc.Arbitrary<string> = fc
  .tuple(alphaCharArb, alphaCharArb, alphaCharArb)
  .map(([a, b, c]) => `${a}${b}${c}`);

/** Valid yyyyMMdd date string */
const dateArb = fc
  .record({
    year: fc.integer({ min: 2020, max: 2030 }),
    month: fc.integer({ min: 1, max: 12 }),
    day: fc.integer({ min: 1, max: 28 }),
  })
  .map(({ year, month, day }) => {
    const m = String(month).padStart(2, "0");
    const d = String(day).padStart(2, "0");
    return `${year}${m}${d}`;
  });

/** 4-digit zero-padded flight number */
const flightNumberArb = fc
  .integer({ min: 1, max: 9999 })
  .map((n) => String(n).padStart(4, "0"));

/** Optional time range: 4-digit HHmm strings */
const timeArb = fc
  .record({
    hour: fc.integer({ min: 0, max: 23 }),
    minute: fc.integer({ min: 0, max: 59 }),
  })
  .map(({ hour, minute }) =>
    `${String(hour).padStart(2, "0")}${String(minute).padStart(2, "0")}`,
  );

// ---------------------------------------------------------------------------
// Route direction params arbitrary
// ---------------------------------------------------------------------------

import type { IScheduleRouteDirectionParams } from "@/features/schedule/types.js";

const directionBaseArb: fc.Arbitrary<IScheduleRouteDirectionParams> = fc.record({
  departure: stationArb,
  arrival: stationArb,
  dateFrom: dateArb,
  dateTo: dateArb,
});

const directionWithTimeArb: fc.Arbitrary<IScheduleRouteDirectionParams> = fc.record({
  departure: stationArb,
  arrival: stationArb,
  dateFrom: dateArb,
  dateTo: dateArb,
  timeFrom: timeArb,
  timeTo: timeArb,
});

const directionWithConnArb: fc.Arbitrary<IScheduleRouteDirectionParams> = fc.record({
  departure: stationArb,
  arrival: stationArb,
  dateFrom: dateArb,
  dateTo: dateArb,
  connections: fc.integer({ min: 0, max: 5 }),
});

const directionWithAllArb: fc.Arbitrary<IScheduleRouteDirectionParams> = fc.record({
  departure: stationArb,
  arrival: stationArb,
  dateFrom: dateArb,
  dateTo: dateArb,
  timeFrom: timeArb,
  timeTo: timeArb,
  connections: fc.integer({ min: 0, max: 5 }),
});

const directionArb: fc.Arbitrary<IScheduleRouteDirectionParams> = fc.oneof(
  directionBaseArb,
  directionWithTimeArb,
  directionWithConnArb,
  directionWithAllArb,
);

// ---------------------------------------------------------------------------
// Discriminated union arbitraries
// ---------------------------------------------------------------------------

const startArb: fc.Arbitrary<ScheduleParams> = fc.constant({ type: "start" as const });

const routeArb: fc.Arbitrary<ScheduleParams> = directionArb.map((d) => ({
  type: "route" as const,
  outbound: d,
}));

const roundtripArb: fc.Arbitrary<ScheduleParams> = fc
  .tuple(directionArb, directionArb)
  .map(([outbound, inbound]) => ({
    type: "roundtrip" as const,
    outbound,
    inbound,
  }));

import type { IScheduleFlightId } from "@/features/schedule/types.js";

const flightIdArb: fc.Arbitrary<IScheduleFlightId> = fc.record({
  carrier: carrierArb,
  flightNumber: flightNumberArb,
  date: dateArb,
});

const detailsArb: fc.Arbitrary<ScheduleParams> = fc
  .array(flightIdArb, { minLength: 1, maxLength: 4 })
  .map((flights) => ({
    type: "details" as const,
    flights,
  }));

/** Combined arbitrary covering all ScheduleParams discriminants */
const scheduleParamsArb: fc.Arbitrary<ScheduleParams> = fc.oneof(
  { weight: 1, arbitrary: startArb },
  { weight: 3, arbitrary: routeArb },
  { weight: 3, arbitrary: roundtripArb },
  { weight: 3, arbitrary: detailsArb },
);

// ---------------------------------------------------------------------------
// Register against harness
// ---------------------------------------------------------------------------

defineUrlParityTests<ScheduleParams>({
  feature: "Schedule",
  fixturePath: "tests/fixtures/phase-3/url-corpus/schedule.json",
  parse: parseScheduleUrl,
  build: buildScheduleUrl,
  fuzzArbitrary: scheduleParamsArb,
});
