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

import * as fc from "fast-check";
import { parseOnlineBoardUrl, buildOnlineBoardUrl } from "@/features/online-board/url.js";
import type { OnlineBoardParams } from "@/features/online-board/url.js";
import { defineUrlParityTests } from "./harness.js";

// ---------------------------------------------------------------------------
// fast-check arbitraries for OnlineBoardParams
// ---------------------------------------------------------------------------

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

/** Single uppercase letter arbitrary */
const alphaCharArb: fc.Arbitrary<string> = fc.constantFrom(...ALPHA_CHARS);

/** 2-char uppercase carrier code (standard IATA) */
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 in a reasonable range */
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 }), // avoid invalid day-of-month
  })
  .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.
 *
 * The URL serializer pads to 4 digits on build, so the roundtrip-stable
 * representation is always 4 digits.
 */
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 }) => {
    return `${String(hour).padStart(2, "0")}${String(minute).padStart(2, "0")}`;
  });

/** Optional suffix: single uppercase letter or absent */
const suffixArb: fc.Arbitrary<string> = fc.constantFrom(...ALPHA_CHARS);

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

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

const flightArb: fc.Arbitrary<OnlineBoardParams> = fc.oneof(
  // Without suffix
  fc.record({
    type: fc.constant("flight" as const),
    carrier: carrierArb,
    flightNumber: flightNumberArb,
    date: dateArb,
  }),
  // With suffix
  fc.record({
    type: fc.constant("flight" as const),
    carrier: carrierArb,
    flightNumber: flightNumberArb,
    suffix: suffixArb,
    date: dateArb,
  }),
);

const departureArb: fc.Arbitrary<OnlineBoardParams> = fc.oneof(
  fc.record({
    type: fc.constant("departure" as const),
    station: stationArb,
    date: dateArb,
  }),
  fc.record({
    type: fc.constant("departure" as const),
    station: stationArb,
    date: dateArb,
    timeFrom: timeArb,
    timeTo: timeArb,
  }),
);

const arrivalArb: fc.Arbitrary<OnlineBoardParams> = fc.oneof(
  fc.record({
    type: fc.constant("arrival" as const),
    station: stationArb,
    date: dateArb,
  }),
  fc.record({
    type: fc.constant("arrival" as const),
    station: stationArb,
    date: dateArb,
    timeFrom: timeArb,
    timeTo: timeArb,
  }),
);

const routeArb: fc.Arbitrary<OnlineBoardParams> = fc.oneof(
  fc.record({
    type: fc.constant("route" as const),
    departure: stationArb,
    arrival: stationArb,
    date: dateArb,
  }),
  fc.record({
    type: fc.constant("route" as const),
    departure: stationArb,
    arrival: stationArb,
    date: dateArb,
    timeFrom: timeArb,
    timeTo: timeArb,
  }),
);

const detailsArb: fc.Arbitrary<OnlineBoardParams> = fc.oneof(
  fc.record({
    type: fc.constant("details" as const),
    carrier: carrierArb,
    flightNumber: flightNumberArb,
    date: dateArb,
  }),
  fc.record({
    type: fc.constant("details" as const),
    carrier: carrierArb,
    flightNumber: flightNumberArb,
    suffix: suffixArb,
    date: dateArb,
  }),
);

/** Combined arbitrary covering all 6 OnlineBoardParams discriminants */
const onlineBoardParamsArb: fc.Arbitrary<OnlineBoardParams> = fc.oneof(
  { weight: 1, arbitrary: startArb },
  { weight: 3, arbitrary: flightArb },
  { weight: 2, arbitrary: departureArb },
  { weight: 2, arbitrary: arrivalArb },
  { weight: 2, arbitrary: routeArb },
  { weight: 3, arbitrary: detailsArb },
);

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

defineUrlParityTests<OnlineBoardParams>({
  feature: "OnlineBoard",
  fixturePath: "tests/fixtures/phase-2/url-corpus/onlineboard.json",
  parse: parseOnlineBoardUrl,
  build: buildOnlineBoardUrl,
  fuzzArbitrary: onlineBoardParamsArb,
});
