// src/dates/interval.ts
import { Interval as Interval2 } from "luxon";

// src/dates/format.ts
import { DateTime } from "luxon";
import { DateTime as DateTime2, Duration, Interval } from "luxon";
function parseDateTime(value, zone) {
  if (typeof value === "string") {
    const dt = DateTime.fromISO(value, { zone: zone ?? "utc" });
    if (!dt.isValid) {
      throw new RangeError(`Invalid DateTime string "${value}": ${dt.invalidReason ?? "unknown error"}`);
    }
    return dt;
  }
  if (!value.isValid) {
    throw new RangeError(`Invalid DateTime: ${value.invalidReason ?? "unknown error"}`);
  }
  return value;
}

// src/dates/interval.ts
function isDstTransition(date, zone) {
  let dt;
  try {
    dt = parseDateTime(date);
  } catch {
    return false;
  }
  const inZone = dt.setZone(zone);
  if (!inZone.isValid)
    return false;
  const startOfDay = inZone.startOf("day");
  const nextMidnight = startOfDay.plus({ days: 1 });
  return startOfDay.offset !== nextMidnight.offset;
}

// src/schedule/calendar.ts
import { z as z5 } from "zod";

// src/schedule/types.ts
import { z as z4 } from "zod";

// src/iata-types/airline.ts
import { z } from "zod";
var AirlineCodeSchema = z.string().length(2).toUpperCase();
var IcaoCodeSchema = z.string().length(4).toUpperCase();

// src/iata-types/airport.ts
import { z as z2 } from "zod";
var AirportCodeSchema = z2.string().length(3).toUpperCase();
var CountrySchema = z2.object({
  code: z2.string().length(2).toUpperCase(),
  name: z2.string().min(1)
});
var CurrencySchema = z2.object({
  code: z2.string().length(3).toUpperCase(),
  name: z2.string().min(1),
  symbol: z2.string().optional()
});

// src/iata-types/fare.ts
import { z as z3 } from "zod";
var FareClassSchema = z3.string().length(1).toUpperCase();
var BaggageAllowanceSchema = z3.discriminatedUnion("type", [
  z3.object({
    type: z3.literal("piece"),
    pieces: z3.number().int().positive(),
    description: z3.string().optional()
  }),
  z3.object({
    type: z3.literal("weight"),
    weightKg: z3.number().positive(),
    pieces: z3.number().int().positive().optional(),
    description: z3.string().optional()
  })
]);

// src/schedule/types.ts
var DoopPatternSchema = z4.string().regex(/^[0-7]{7}$/, "DOOP must be 7 chars of 0-7, Monday=1 Sunday=7");
function dayOfWeekPattern(days) {
  const chars = "0000000".split("");
  for (const d of days) {
    if (d >= 1 && d <= 7)
      chars[d - 1] = String(d);
  }
  const pattern = chars.join("");
  return DoopPatternSchema.parse(pattern);
}
function operatesOnDay(pattern, day) {
  if (day < 1 || day > 7)
    return false;
  return pattern[day - 1] !== "0";
}
function operatingDays(pattern) {
  const days = [];
  for (let i = 0;i < 7; i++) {
    if (pattern[i] !== "0")
      days.push(i + 1);
  }
  return days;
}
var ServiceTypeSchema = z4.enum([
  "J",
  "F",
  "P",
  "C",
  "G",
  "K",
  "L",
  "M"
]);
var SeasonCodeSchema = z4.string().regex(/^[SW]\d{2}$/, "Season code: S26, W27, etc.");
var SeasonDefinitionSchema = z4.object({
  code: SeasonCodeSchema,
  startDate: z4.string().date(),
  endDate: z4.string().date(),
  name: z4.string().min(1)
});
var ScheduleEntrySchema = z4.object({
  airline: AirlineCodeSchema,
  flightNumber: z4.string().min(1),
  origin: AirportCodeSchema,
  destination: AirportCodeSchema,
  departureTimeUtc: z4.string().datetime(),
  arrivalTimeUtc: z4.string().datetime(),
  doop: DoopPatternSchema,
  serviceType: ServiceTypeSchema.default("J"),
  periodStart: z4.string().date(),
  periodEnd: z4.string().date(),
  aircraftType: z4.string().optional(),
  bookingClasses: z4.array(FareClassSchema).default([]),
  seasonCode: SeasonCodeSchema.optional()
});
var RbdAvailabilitySchema = z4.object({
  fareClass: FareClassSchema,
  seatsAvailable: z4.number().int().min(-1)
});
var AvailabilityStatusSchema = z4.enum([
  "available",
  "not-available",
  "request",
  "waitlist",
  "waitlist-closed"
]);
var FlightAvailabilitySchema = z4.object({
  airline: AirlineCodeSchema,
  flightNumber: z4.string().min(1),
  origin: AirportCodeSchema,
  destination: AirportCodeSchema,
  departureDate: z4.string().date(),
  rbdAvailability: z4.array(RbdAvailabilitySchema),
  status: AvailabilityStatusSchema.default("available")
});
var BlackoutRangeSchema = z4.object({
  startDate: z4.string().date(),
  endDate: z4.string().date(),
  description: z4.string().optional()
});
var HolidayImpactSchema = z4.enum([
  "none",
  "holiday",
  "pre-holiday",
  "post-holiday"
]);
var HolidayWarningSchema = z4.object({
  date: z4.string(),
  name: z4.string().min(1),
  impact: HolidayImpactSchema
});
var ScheduleLookupResultSchema = z4.object({
  entries: z4.array(ScheduleEntrySchema),
  blackoutDates: z4.array(BlackoutRangeSchema).default([]),
  season: SeasonDefinitionSchema.optional(),
  holidayWarnings: z4.array(HolidayWarningSchema).default([])
});

// src/schedule/calendar.ts
import Holidays from "date-holidays";
function iataSeasonDates(year) {
  const summerStart = lastSundayOfMonth(year, 2);
  const summerEnd = lastSaturdayOfMonth(year, 9);
  const winterStart = lastSundayOfMonth(year, 9);
  const winterEnd = lastSaturdayOfMonth(year + 1, 2);
  const summerCode = `S${String(year).slice(2)}`;
  const winterCode = `W${String(year).slice(2)}`;
  const summer = SeasonDefinitionSchema.parse({
    code: summerCode,
    startDate: fmtDate(summerStart),
    endDate: fmtDate(summerEnd),
    name: `Northern Summer ${year}`
  });
  const winter = SeasonDefinitionSchema.parse({
    code: winterCode,
    startDate: fmtDate(winterStart),
    endDate: fmtDate(winterEnd),
    name: `Northern Winter ${year}/${year + 1}`
  });
  return { summer, winter };
}
function lastSundayOfMonth(year, month) {
  const d = new Date(year, month + 1, 0);
  while (d.getDay() !== 0)
    d.setDate(d.getDate() - 1);
  return d;
}
function lastSaturdayOfMonth(year, month) {
  const d = new Date(year, month + 1, 0);
  while (d.getDay() !== 6)
    d.setDate(d.getDate() - 1);
  return d;
}
function fmtDate(d) {
  return d.toISOString().slice(0, 10);
}
function dstTransitionsForYear(zone, year) {
  let springForward = null;
  let fallBack = null;
  for (let d = 1;d <= 31; d++) {
    const dateStr = `${year}-03-${String(d).padStart(2, "0")}`;
    if (isDstTransition(dateStr, zone)) {
      springForward = dateStr;
      break;
    }
  }
  for (let d = 1;d <= 31; d++) {
    const dateStr = `${year}-10-${String(d).padStart(2, "0")}`;
    if (isDstTransition(dateStr, zone)) {
      fallBack = dateStr;
      break;
    }
  }
  if (!fallBack) {
    for (let d = 1;d <= 7; d++) {
      const dateStr = `${year}-11-${String(d).padStart(2, "0")}`;
      if (isDstTransition(dateStr, zone)) {
        fallBack = dateStr;
        break;
      }
    }
  }
  return { zone, springForward, fallBack };
}
var AU_HOLIDAYS = {
  region: "AU",
  holidays: (year) => [
    { date: `${year}-01-01`, name: "New Year's Day" },
    { date: `${year}-01-26`, name: "Australia Day" },
    { date: `${year}-03-29`, name: "Good Friday" },
    { date: `${year}-04-25`, name: "ANZAC Day" },
    { date: `${year}-12-25`, name: "Christmas Day" },
    { date: `${year}-12-26`, name: "Boxing Day" }
  ]
};
var US_HOLIDAYS = {
  region: "US",
  holidays: (year) => [
    { date: `${year}-01-01`, name: "New Year's Day" },
    { date: `${year}-01-20`, name: "MLK Day" },
    { date: `${year}-02-17`, name: "Presidents' Day" },
    { date: `${year}-05-26`, name: "Memorial Day" },
    { date: `${year}-07-04`, name: "Independence Day" },
    { date: `${year}-09-01`, name: "Labor Day" },
    { date: `${year}-11-27`, name: "Thanksgiving" },
    { date: `${year}-12-25`, name: "Christmas Day" }
  ]
};
var UK_HOLIDAYS = {
  region: "UK",
  holidays: (year) => [
    { date: `${year}-01-01`, name: "New Year's Day" },
    { date: `${year}-04-18`, name: "Good Friday" },
    { date: `${year}-04-21`, name: "Easter Monday" },
    { date: `${year}-05-05`, name: "Early May Bank Holiday" },
    { date: `${year}-05-26`, name: "Spring Bank Holiday" },
    { date: `${year}-08-25`, name: "Summer Bank Holiday" },
    { date: `${year}-12-25`, name: "Christmas Day" },
    { date: `${year}-12-26`, name: "Boxing Day" }
  ]
};
var HOLIDAY_PROVIDERS = {
  AU: AU_HOLIDAYS,
  US: US_HOLIDAYS,
  UK: UK_HOLIDAYS
};
function publicHolidays(region, year) {
  const provider = HOLIDAY_PROVIDERS[region.toUpperCase()];
  if (!provider)
    return [];
  return provider.holidays(year);
}
function isPublicHoliday(date, region) {
  const year = new Date(date).getFullYear();
  const holidays = publicHolidays(region, year);
  return holidays.some((h) => h.date === date);
}
function registerHolidayProvider(provider) {
  HOLIDAY_PROVIDERS[provider.region.toUpperCase()] = provider;
}

class DateHolidaysProvider {
  region;
  opts;
  constructor(country, opts = {}) {
    this.region = country.toUpperCase();
    this.opts = opts;
  }
  holidays(year) {
    const hd = new Holidays(this.region, this.opts.state ?? "", this.opts.region ?? "");
    const types = this.opts.types ?? ["public"];
    const all = hd.getHolidays(year);
    return all.filter((h) => types.includes(h.type)).map((h) => ({
      date: h.date.slice(0, 10),
      name: h.name
    }));
  }
}
function initDateHolidaysProviders() {
  registerHolidayProvider(new DateHolidaysProvider("AU"));
  registerHolidayProvider(new DateHolidaysProvider("US"));
  registerHolidayProvider(new DateHolidaysProvider("GB"));
  const ukProvider = new DateHolidaysProvider("GB");
  HOLIDAY_PROVIDERS["UK"] = {
    region: "UK",
    holidays: (year) => ukProvider.holidays(year)
  };
}
var HolidayAffectedParamsSchema = z5.object({
  date: z5.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be YYYY-MM-DD"),
  region: z5.string().min(1)
});
function isHolidayAffected(date, region) {
  HolidayAffectedParamsSchema.parse({ date, region });
  const year = new Date(date).getFullYear();
  const holidaySet = new Map;
  for (const y of [year - 1, year, year + 1]) {
    for (const h of publicHolidays(region, y)) {
      holidaySet.set(h.date, h.name);
    }
  }
  if (holidaySet.has(date)) {
    return { impact: "holiday", holidayName: holidaySet.get(date) };
  }
  const nextDay = addDays(date, 1);
  if (holidaySet.has(nextDay)) {
    return { impact: "pre-holiday", holidayName: holidaySet.get(nextDay) };
  }
  const prevDay = addDays(date, -1);
  if (holidaySet.has(prevDay)) {
    return { impact: "post-holiday", holidayName: holidaySet.get(prevDay) };
  }
  return { impact: "none", holidayName: null };
}
function addDays(date, days) {
  const d = new Date(date);
  d.setDate(d.getDate() + days);
  return d.toISOString().slice(0, 10);
}

// src/schedule/availability.ts
function checkAvailability(entry, date, bookingClasses, holidayRegion, logger) {
  const classes = bookingClasses ?? entry.bookingClasses;
  let isHoliday = false;
  let holidayName = null;
  let holidayImpact = null;
  let demandMultiplier = 1;
  if (holidayRegion) {
    const affected = isHolidayAffected(date, holidayRegion);
    isHoliday = affected.impact !== "none";
    holidayImpact = affected.impact !== "none" ? affected.impact : null;
    holidayName = affected.holidayName;
    if (affected.impact === "holiday")
      demandMultiplier = 0.5;
    else if (affected.impact === "pre-holiday")
      demandMultiplier = 0.6;
    else if (affected.impact === "post-holiday")
      demandMultiplier = 0.7;
  }
  const rbdAvailability = classes.map((fc) => {
    const baseSeats = generateSeatCount(fc);
    const adjustedSeats = Math.max(0, Math.floor(baseSeats * demandMultiplier));
    return { fareClass: fc, seatsAvailable: adjustedSeats };
  });
  const flightAvail = {
    airline: entry.airline,
    flightNumber: entry.flightNumber,
    origin: entry.origin,
    destination: entry.destination,
    departureDate: date,
    rbdAvailability,
    status: "available"
  };
  FlightAvailabilitySchema.parse(flightAvail);
  const available = rbdAvailability.some((r) => r.seatsAvailable > 0);
  if (!available) {
    logger?.warn("Flight unavailable", {
      module: "schedule",
      flightNumber: entry.flightNumber,
      date
    });
  }
  logger?.debug("Availability checked", {
    module: "schedule",
    flightNumber: entry.flightNumber,
    available,
    rbdCount: rbdAvailability.length
  });
  return {
    available,
    flightAvailability: available ? flightAvail : { ...flightAvail, status: "not-available" },
    isHoliday,
    holidayName,
    holidayImpact
  };
}
function checkAvailabilityRange(entry, startDate, endDate, bookingClasses, holidayRegion) {
  const results = [];
  let current = new Date(startDate);
  const end = new Date(endDate);
  while (current <= end) {
    const dateStr = current.toISOString().slice(0, 10);
    const check = checkAvailability(entry, dateStr, bookingClasses, holidayRegion);
    results.push({
      date: dateStr,
      available: check.available,
      isHoliday: check.isHoliday,
      holidayName: check.holidayName
    });
    current = new Date(current.getTime() + 86400000);
  }
  return results;
}
function generateSeatCount(fareClass) {
  if (fareClass === "F")
    return Math.floor(Math.random() * 4) + 1;
  if (fareClass === "J" || fareClass === "C")
    return Math.floor(Math.random() * 8) + 1;
  if (fareClass === "W")
    return Math.floor(Math.random() * 12) + 2;
  const rand = Math.random();
  if (rand < 0.1)
    return 0;
  return Math.floor(Math.random() * 9) + 1;
}
// src/schedule/lookup.ts
class LruCache {
  store = new Map;
  maxSize;
  constructor(maxSize = 1000) {
    this.maxSize = maxSize;
  }
  get(key) {
    const val = this.store.get(key);
    if (val !== undefined) {
      this.store.delete(key);
      this.store.set(key, val);
    }
    return val;
  }
  set(key, value) {
    if (this.store.has(key))
      this.store.delete(key);
    if (this.store.size >= this.maxSize) {
      const firstKey = this.store.keys().next().value;
      if (firstKey !== undefined)
        this.store.delete(firstKey);
    }
    this.store.set(key, value);
  }
  clear() {
    this.store.clear();
  }
  get size() {
    return this.store.size;
  }
}
var DEFAULT_CACHE_CONFIG = {
  enabled: true,
  maxSize: 1000,
  ttlMs: 300000
};

class ScheduleLookup {
  cache;
  cacheConfig;
  store;
  logger;
  constructor(store, cacheConfig, logger) {
    this.store = store;
    this.cacheConfig = { ...DEFAULT_CACHE_CONFIG, ...cacheConfig };
    this.cache = new LruCache(this.cacheConfig.maxSize);
    this.logger = logger;
  }
  lookupSchedule(origin, destination, date) {
    const cacheKey = `${origin}-${destination}-${date}`;
    if (this.cacheConfig.enabled) {
      const cached = this.cache.get(cacheKey);
      if (cached && Date.now() < cached.expiresAt) {
        return cached.result;
      }
    }
    const entries = this.store.lookup(origin, destination, date);
    if (entries.length === 0) {
      this.logger?.warn("No schedule entries found", { origin, destination, date });
    }
    const holidayWarnings = [];
    if (this.cacheConfig.holidayRegion) {
      const { impact, holidayName } = isHolidayAffected(date, this.cacheConfig.holidayRegion);
      if (impact !== "none" && holidayName) {
        holidayWarnings.push({ date, name: holidayName, impact });
      }
    }
    const result = {
      entries,
      blackoutDates: [],
      holidayWarnings
    };
    try {
      ScheduleLookupResultSchema.parse(result);
    } catch (err) {
      this.logger?.error("Schedule lookup result validation failed", {
        origin,
        destination,
        date,
        error: String(err)
      });
      throw err;
    }
    if (this.cacheConfig.enabled) {
      this.cache.set(cacheKey, {
        result,
        expiresAt: Date.now() + this.cacheConfig.ttlMs
      });
    }
    return result;
  }
  clearCache() {
    this.cache.clear();
  }
}

class InMemoryScheduleStore {
  entries = [];
  addEntry(entry) {
    const parsed = ScheduleEntrySchema.parse(entry);
    this.entries.push(parsed);
  }
  addEntries(entries) {
    for (const entry of entries)
      this.addEntry(entry);
  }
  lookup(origin, destination, date) {
    const dateDay = parseDateTime(date).weekday;
    return this.entries.filter((e) => {
      if (e.origin !== origin.toUpperCase())
        return false;
      if (e.destination !== destination.toUpperCase())
        return false;
      if (date < e.periodStart || date > e.periodEnd)
        return false;
      if (!operatesOnDate(e.doop, dateDay))
        return false;
      return true;
    });
  }
}
function operatesOnDate(doop, weekday) {
  if (weekday < 1 || weekday > 7)
    return false;
  return doop[weekday - 1] !== "0";
}
export {
  registerHolidayProvider,
  publicHolidays,
  operatingDays,
  operatesOnDay,
  isPublicHoliday,
  isHolidayAffected,
  initDateHolidaysProviders,
  iataSeasonDates,
  dstTransitionsForYear,
  dayOfWeekPattern,
  checkAvailabilityRange,
  checkAvailability,
  ServiceTypeSchema,
  SeasonDefinitionSchema,
  SeasonCodeSchema,
  ScheduleLookupResultSchema,
  ScheduleLookup,
  ScheduleEntrySchema,
  RbdAvailabilitySchema,
  InMemoryScheduleStore,
  HolidayWarningSchema,
  HolidayImpactSchema,
  FlightAvailabilitySchema,
  DoopPatternSchema,
  DateHolidaysProvider,
  BlackoutRangeSchema,
  AvailabilityStatusSchema
};
