export type StayRange = {
  checkIn: string;
  checkOut: string;
};

const datePattern = /^\d{4}-\d{2}-\d{2}$/;

export function assertIsoDate(value: string, label: string) {
  if (!datePattern.test(value)) {
    throw new Error(`${label} must use YYYY-MM-DD format`);
  }

  const parsed = new Date(`${value}T00:00:00.000Z`);

  if (Number.isNaN(parsed.getTime()) || parsed.toISOString().slice(0, 10) !== value) {
    throw new Error(`${label} must be a valid calendar date`);
  }
}

export function assertValidStayRange(range: StayRange) {
  assertIsoDate(range.checkIn, "checkIn");
  assertIsoDate(range.checkOut, "checkOut");

  if (range.checkIn >= range.checkOut) {
    throw new Error("checkOut must be after checkIn");
  }
}

export function nightsBetween(range: StayRange) {
  assertValidStayRange(range);

  const checkIn = new Date(`${range.checkIn}T00:00:00.000Z`);
  const checkOut = new Date(`${range.checkOut}T00:00:00.000Z`);
  const nights = (checkOut.getTime() - checkIn.getTime()) / 86_400_000;

  if (!Number.isInteger(nights) || nights < 1) {
    throw new Error("Stay must contain at least one night");
  }

  return nights;
}

export function rangesOverlap(left: StayRange, right: StayRange) {
  assertValidStayRange(left);
  assertValidStayRange(right);

  return left.checkIn < right.checkOut && right.checkIn < left.checkOut;
}

export function rangesAreAdjacent(left: StayRange, right: StayRange) {
  assertValidStayRange(left);
  assertValidStayRange(right);

  return left.checkOut === right.checkIn || right.checkOut === left.checkIn;
}

export function listNights(range: StayRange) {
  const nights = nightsBetween(range);
  const current = new Date(`${range.checkIn}T00:00:00.000Z`);

  return Array.from({ length: nights }, () => {
    const value = current.toISOString().slice(0, 10);
    current.setUTCDate(current.getUTCDate() + 1);
    return value;
  });
}
