const require_errors = require('./errors.js');
const require_formats = require('./impl/formats.js');
const require_settings = require('./settings.js');
const require_invalid = require('./impl/invalid.js');
const require_formatter = require('./impl/formatter.js');
const require_duration = require('./duration.js');
const require_datetime = require('./datetime.js');

//#region ../../node_modules/.pnpm/luxon@3.5.0/node_modules/luxon/src/interval.js
const INVALID = "Invalid Interval";
function validateStartEnd(start, end) {
	if (!start || !start.isValid) return Interval.invalid("missing or invalid start");
	else if (!end || !end.isValid) return Interval.invalid("missing or invalid end");
	else if (end < start) return Interval.invalid("end before start", `The end of an interval must be after its start, but you had start=${start.toISO()} and end=${end.toISO()}`);
	else return null;
}
/**
* An Interval object represents a half-open interval of time, where each endpoint is a {@link DateTime}. Conceptually, it's a container for those two endpoints, accompanied by methods for creating, parsing, interrogating, comparing, transforming, and formatting them.
*
* Here is a brief overview of the most commonly used methods and getters in Interval:
*
* * **Creation** To create an Interval, use {@link Interval.fromDateTimes}, {@link Interval.after}, {@link Interval.before}, or {@link Interval.fromISO}.
* * **Accessors** Use {@link Interval#start} and {@link Interval#end} to get the start and end.
* * **Interrogation** To analyze the Interval, use {@link Interval#count}, {@link Interval#length}, {@link Interval#hasSame}, {@link Interval#contains}, {@link Interval#isAfter}, or {@link Interval#isBefore}.
* * **Transformation** To create other Intervals out of this one, use {@link Interval#set}, {@link Interval#splitAt}, {@link Interval#splitBy}, {@link Interval#divideEqually}, {@link Interval.merge}, {@link Interval.xor}, {@link Interval#union}, {@link Interval#intersection}, or {@link Interval#difference}.
* * **Comparison** To compare this Interval to another one, use {@link Interval#equals}, {@link Interval#overlaps}, {@link Interval#abutsStart}, {@link Interval#abutsEnd}, {@link Interval#engulfs}
* * **Output** To convert the Interval into other representations, see {@link Interval#toString}, {@link Interval#toLocaleString}, {@link Interval#toISO}, {@link Interval#toISODate}, {@link Interval#toISOTime}, {@link Interval#toFormat}, and {@link Interval#toDuration}.
*/
var Interval = class Interval {
	/**
	* @private
	*/
	constructor(config) {
		/**
		* @access private
		*/
		this.s = config.start;
		/**
		* @access private
		*/
		this.e = config.end;
		/**
		* @access private
		*/
		this.invalid = config.invalid || null;
		/**
		* @access private
		*/
		this.isLuxonInterval = true;
	}
	/**
	* Create an invalid Interval.
	* @param {string} reason - simple string of why this Interval is invalid. Should not contain parameters or anything else data-dependent
	* @param {string} [explanation=null] - longer explanation, may include parameters and other useful debugging information
	* @return {Interval}
	*/
	static invalid(reason, explanation = null) {
		if (!reason) throw new require_errors.InvalidArgumentError("need to specify a reason the Interval is invalid");
		const invalid = reason instanceof require_invalid.default ? reason : new require_invalid.default(reason, explanation);
		if (require_settings.default.throwOnInvalid) throw new require_errors.InvalidIntervalError(invalid);
		else return new Interval({ invalid });
	}
	/**
	* Create an Interval from a start DateTime and an end DateTime. Inclusive of the start but not the end.
	* @param {DateTime|Date|Object} start
	* @param {DateTime|Date|Object} end
	* @return {Interval}
	*/
	static fromDateTimes(start, end) {
		const builtStart = require_datetime.friendlyDateTime(start), builtEnd = require_datetime.friendlyDateTime(end);
		const validateError = validateStartEnd(builtStart, builtEnd);
		if (validateError == null) return new Interval({
			start: builtStart,
			end: builtEnd
		});
		else return validateError;
	}
	/**
	* Create an Interval from a start DateTime and a Duration to extend to.
	* @param {DateTime|Date|Object} start
	* @param {Duration|Object|number} duration - the length of the Interval.
	* @return {Interval}
	*/
	static after(start, duration) {
		const dur = require_duration.default.fromDurationLike(duration), dt = require_datetime.friendlyDateTime(start);
		return Interval.fromDateTimes(dt, dt.plus(dur));
	}
	/**
	* Create an Interval from an end DateTime and a Duration to extend backwards to.
	* @param {DateTime|Date|Object} end
	* @param {Duration|Object|number} duration - the length of the Interval.
	* @return {Interval}
	*/
	static before(end, duration) {
		const dur = require_duration.default.fromDurationLike(duration), dt = require_datetime.friendlyDateTime(end);
		return Interval.fromDateTimes(dt.minus(dur), dt);
	}
	/**
	* Create an Interval from an ISO 8601 string.
	* Accepts `<start>/<end>`, `<start>/<duration>`, and `<duration>/<end>` formats.
	* @param {string} text - the ISO string to parse
	* @param {Object} [opts] - options to pass {@link DateTime#fromISO} and optionally {@link Duration#fromISO}
	* @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals
	* @return {Interval}
	*/
	static fromISO(text, opts) {
		const [s, e] = (text || "").split("/", 2);
		if (s && e) {
			let start, startIsValid;
			try {
				start = require_datetime.default.fromISO(s, opts);
				startIsValid = start.isValid;
			} catch (e$1) {
				startIsValid = false;
			}
			let end, endIsValid;
			try {
				end = require_datetime.default.fromISO(e, opts);
				endIsValid = end.isValid;
			} catch (e$1) {
				endIsValid = false;
			}
			if (startIsValid && endIsValid) return Interval.fromDateTimes(start, end);
			if (startIsValid) {
				const dur = require_duration.default.fromISO(e, opts);
				if (dur.isValid) return Interval.after(start, dur);
			} else if (endIsValid) {
				const dur = require_duration.default.fromISO(s, opts);
				if (dur.isValid) return Interval.before(end, dur);
			}
		}
		return Interval.invalid("unparsable", `the input "${text}" can't be parsed as ISO 8601`);
	}
	/**
	* Check if an object is an Interval. Works across context boundaries
	* @param {object} o
	* @return {boolean}
	*/
	static isInterval(o) {
		return o && o.isLuxonInterval || false;
	}
	/**
	* Returns the start of the Interval
	* @type {DateTime}
	*/
	get start() {
		return this.isValid ? this.s : null;
	}
	/**
	* Returns the end of the Interval
	* @type {DateTime}
	*/
	get end() {
		return this.isValid ? this.e : null;
	}
	/**
	* Returns whether this Interval's end is at least its start, meaning that the Interval isn't 'backwards'.
	* @type {boolean}
	*/
	get isValid() {
		return this.invalidReason === null;
	}
	/**
	* Returns an error code if this Interval is invalid, or null if the Interval is valid
	* @type {string}
	*/
	get invalidReason() {
		return this.invalid ? this.invalid.reason : null;
	}
	/**
	* Returns an explanation of why this Interval became invalid, or null if the Interval is valid
	* @type {string}
	*/
	get invalidExplanation() {
		return this.invalid ? this.invalid.explanation : null;
	}
	/**
	* Returns the length of the Interval in the specified unit.
	* @param {string} unit - the unit (such as 'hours' or 'days') to return the length in.
	* @return {number}
	*/
	length(unit = "milliseconds") {
		return this.isValid ? this.toDuration(...[unit]).get(unit) : NaN;
	}
	/**
	* Returns the count of minutes, hours, days, months, or years included in the Interval, even in part.
	* Unlike {@link Interval#length} this counts sections of the calendar, not periods of time, e.g. specifying 'day'
	* asks 'what dates are included in this interval?', not 'how many days long is this interval?'
	* @param {string} [unit='milliseconds'] - the unit of time to count.
	* @param {Object} opts - options
	* @param {boolean} [opts.useLocaleWeeks=false] - If true, use weeks based on the locale, i.e. use the locale-dependent start of the week; this operation will always use the locale of the start DateTime
	* @return {number}
	*/
	count(unit = "milliseconds", opts) {
		if (!this.isValid) return NaN;
		const start = this.start.startOf(unit, opts);
		let end;
		if (opts?.useLocaleWeeks) end = this.end.reconfigure({ locale: start.locale });
		else end = this.end;
		end = end.startOf(unit, opts);
		return Math.floor(end.diff(start, unit).get(unit)) + (end.valueOf() !== this.end.valueOf());
	}
	/**
	* Returns whether this Interval's start and end are both in the same unit of time
	* @param {string} unit - the unit of time to check sameness on
	* @return {boolean}
	*/
	hasSame(unit) {
		return this.isValid ? this.isEmpty() || this.e.minus(1).hasSame(this.s, unit) : false;
	}
	/**
	* Return whether this Interval has the same start and end DateTimes.
	* @return {boolean}
	*/
	isEmpty() {
		return this.s.valueOf() === this.e.valueOf();
	}
	/**
	* Return whether this Interval's start is after the specified DateTime.
	* @param {DateTime} dateTime
	* @return {boolean}
	*/
	isAfter(dateTime) {
		if (!this.isValid) return false;
		return this.s > dateTime;
	}
	/**
	* Return whether this Interval's end is before the specified DateTime.
	* @param {DateTime} dateTime
	* @return {boolean}
	*/
	isBefore(dateTime) {
		if (!this.isValid) return false;
		return this.e <= dateTime;
	}
	/**
	* Return whether this Interval contains the specified DateTime.
	* @param {DateTime} dateTime
	* @return {boolean}
	*/
	contains(dateTime) {
		if (!this.isValid) return false;
		return this.s <= dateTime && this.e > dateTime;
	}
	/**
	* "Sets" the start and/or end dates. Returns a newly-constructed Interval.
	* @param {Object} values - the values to set
	* @param {DateTime} values.start - the starting DateTime
	* @param {DateTime} values.end - the ending DateTime
	* @return {Interval}
	*/
	set({ start, end } = {}) {
		if (!this.isValid) return this;
		return Interval.fromDateTimes(start || this.s, end || this.e);
	}
	/**
	* Split this Interval at each of the specified DateTimes
	* @param {...DateTime} dateTimes - the unit of time to count.
	* @return {Array}
	*/
	splitAt(...dateTimes) {
		if (!this.isValid) return [];
		const sorted = dateTimes.map(require_datetime.friendlyDateTime).filter((d) => this.contains(d)).sort((a, b) => a.toMillis() - b.toMillis()), results = [];
		let { s } = this, i = 0;
		while (s < this.e) {
			const added = sorted[i] || this.e, next = +added > +this.e ? this.e : added;
			results.push(Interval.fromDateTimes(s, next));
			s = next;
			i += 1;
		}
		return results;
	}
	/**
	* Split this Interval into smaller Intervals, each of the specified length.
	* Left over time is grouped into a smaller interval
	* @param {Duration|Object|number} duration - The length of each resulting interval.
	* @return {Array}
	*/
	splitBy(duration) {
		const dur = require_duration.default.fromDurationLike(duration);
		if (!this.isValid || !dur.isValid || dur.as("milliseconds") === 0) return [];
		let { s } = this, idx = 1, next;
		const results = [];
		while (s < this.e) {
			const added = this.start.plus(dur.mapUnits((x) => x * idx));
			next = +added > +this.e ? this.e : added;
			results.push(Interval.fromDateTimes(s, next));
			s = next;
			idx += 1;
		}
		return results;
	}
	/**
	* Split this Interval into the specified number of smaller intervals.
	* @param {number} numberOfParts - The number of Intervals to divide the Interval into.
	* @return {Array}
	*/
	divideEqually(numberOfParts) {
		if (!this.isValid) return [];
		return this.splitBy(this.length() / numberOfParts).slice(0, numberOfParts);
	}
	/**
	* Return whether this Interval overlaps with the specified Interval
	* @param {Interval} other
	* @return {boolean}
	*/
	overlaps(other) {
		return this.e > other.s && this.s < other.e;
	}
	/**
	* Return whether this Interval's end is adjacent to the specified Interval's start.
	* @param {Interval} other
	* @return {boolean}
	*/
	abutsStart(other) {
		if (!this.isValid) return false;
		return +this.e === +other.s;
	}
	/**
	* Return whether this Interval's start is adjacent to the specified Interval's end.
	* @param {Interval} other
	* @return {boolean}
	*/
	abutsEnd(other) {
		if (!this.isValid) return false;
		return +other.e === +this.s;
	}
	/**
	* Returns true if this Interval fully contains the specified Interval, specifically if the intersect (of this Interval and the other Interval) is equal to the other Interval; false otherwise.
	* @param {Interval} other
	* @return {boolean}
	*/
	engulfs(other) {
		if (!this.isValid) return false;
		return this.s <= other.s && this.e >= other.e;
	}
	/**
	* Return whether this Interval has the same start and end as the specified Interval.
	* @param {Interval} other
	* @return {boolean}
	*/
	equals(other) {
		if (!this.isValid || !other.isValid) return false;
		return this.s.equals(other.s) && this.e.equals(other.e);
	}
	/**
	* Return an Interval representing the intersection of this Interval and the specified Interval.
	* Specifically, the resulting Interval has the maximum start time and the minimum end time of the two Intervals.
	* Returns null if the intersection is empty, meaning, the intervals don't intersect.
	* @param {Interval} other
	* @return {Interval}
	*/
	intersection(other) {
		if (!this.isValid) return this;
		const s = this.s > other.s ? this.s : other.s, e = this.e < other.e ? this.e : other.e;
		if (s >= e) return null;
		else return Interval.fromDateTimes(s, e);
	}
	/**
	* Return an Interval representing the union of this Interval and the specified Interval.
	* Specifically, the resulting Interval has the minimum start time and the maximum end time of the two Intervals.
	* @param {Interval} other
	* @return {Interval}
	*/
	union(other) {
		if (!this.isValid) return this;
		const s = this.s < other.s ? this.s : other.s, e = this.e > other.e ? this.e : other.e;
		return Interval.fromDateTimes(s, e);
	}
	/**
	* Merge an array of Intervals into a equivalent minimal set of Intervals.
	* Combines overlapping and adjacent Intervals.
	* @param {Array} intervals
	* @return {Array}
	*/
	static merge(intervals) {
		const [found, final] = intervals.sort((a, b) => a.s - b.s).reduce(([sofar, current], item) => {
			if (!current) return [sofar, item];
			else if (current.overlaps(item) || current.abutsStart(item)) return [sofar, current.union(item)];
			else return [sofar.concat([current]), item];
		}, [[], null]);
		if (final) found.push(final);
		return found;
	}
	/**
	* Return an array of Intervals representing the spans of time that only appear in one of the specified Intervals.
	* @param {Array} intervals
	* @return {Array}
	*/
	static xor(intervals) {
		let start = null, currentCount = 0;
		const results = [], ends = intervals.map((i) => [{
			time: i.s,
			type: "s"
		}, {
			time: i.e,
			type: "e"
		}]), arr = Array.prototype.concat(...ends).sort((a, b) => a.time - b.time);
		for (const i of arr) {
			currentCount += i.type === "s" ? 1 : -1;
			if (currentCount === 1) start = i.time;
			else {
				if (start && +start !== +i.time) results.push(Interval.fromDateTimes(start, i.time));
				start = null;
			}
		}
		return Interval.merge(results);
	}
	/**
	* Return an Interval representing the span of time in this Interval that doesn't overlap with any of the specified Intervals.
	* @param {...Interval} intervals
	* @return {Array}
	*/
	difference(...intervals) {
		return Interval.xor([this].concat(intervals)).map((i) => this.intersection(i)).filter((i) => i && !i.isEmpty());
	}
	/**
	* Returns a string representation of this Interval appropriate for debugging.
	* @return {string}
	*/
	toString() {
		if (!this.isValid) return INVALID;
		return `[${this.s.toISO()} – ${this.e.toISO()})`;
	}
	/**
	* Returns a string representation of this Interval appropriate for the REPL.
	* @return {string}
	*/
	[Symbol.for("nodejs.util.inspect.custom")]() {
		if (this.isValid) return `Interval { start: ${this.s.toISO()}, end: ${this.e.toISO()} }`;
		else return `Interval { Invalid, reason: ${this.invalidReason} }`;
	}
	/**
	* Returns a localized string representing this Interval. Accepts the same options as the
	* Intl.DateTimeFormat constructor and any presets defined by Luxon, such as
	* {@link DateTime.DATE_FULL} or {@link DateTime.TIME_SIMPLE}. The exact behavior of this method
	* is browser-specific, but in general it will return an appropriate representation of the
	* Interval in the assigned locale. Defaults to the system's locale if no locale has been
	* specified.
	* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat
	* @param {Object} [formatOpts=DateTime.DATE_SHORT] - Either a DateTime preset or
	* Intl.DateTimeFormat constructor options.
	* @param {Object} opts - Options to override the configuration of the start DateTime.
	* @example Interval.fromISO('2022-11-07T09:00Z/2022-11-08T09:00Z').toLocaleString(); //=> 11/7/2022 – 11/8/2022
	* @example Interval.fromISO('2022-11-07T09:00Z/2022-11-08T09:00Z').toLocaleString(DateTime.DATE_FULL); //=> November 7 – 8, 2022
	* @example Interval.fromISO('2022-11-07T09:00Z/2022-11-08T09:00Z').toLocaleString(DateTime.DATE_FULL, { locale: 'fr-FR' }); //=> 7–8 novembre 2022
	* @example Interval.fromISO('2022-11-07T17:00Z/2022-11-07T19:00Z').toLocaleString(DateTime.TIME_SIMPLE); //=> 6:00 – 8:00 PM
	* @example Interval.fromISO('2022-11-07T17:00Z/2022-11-07T19:00Z').toLocaleString({ weekday: 'short', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }); //=> Mon, Nov 07, 6:00 – 8:00 p
	* @return {string}
	*/
	toLocaleString(formatOpts = require_formats.DATE_SHORT, opts = {}) {
		return this.isValid ? require_formatter.default.create(this.s.loc.clone(opts), formatOpts).formatInterval(this) : INVALID;
	}
	/**
	* Returns an ISO 8601-compliant string representation of this Interval.
	* @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals
	* @param {Object} opts - The same options as {@link DateTime#toISO}
	* @return {string}
	*/
	toISO(opts) {
		if (!this.isValid) return INVALID;
		return `${this.s.toISO(opts)}/${this.e.toISO(opts)}`;
	}
	/**
	* Returns an ISO 8601-compliant string representation of date of this Interval.
	* The time components are ignored.
	* @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals
	* @return {string}
	*/
	toISODate() {
		if (!this.isValid) return INVALID;
		return `${this.s.toISODate()}/${this.e.toISODate()}`;
	}
	/**
	* Returns an ISO 8601-compliant string representation of time of this Interval.
	* The date components are ignored.
	* @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals
	* @param {Object} opts - The same options as {@link DateTime#toISO}
	* @return {string}
	*/
	toISOTime(opts) {
		if (!this.isValid) return INVALID;
		return `${this.s.toISOTime(opts)}/${this.e.toISOTime(opts)}`;
	}
	/**
	* Returns a string representation of this Interval formatted according to the specified format
	* string. **You may not want this.** See {@link Interval#toLocaleString} for a more flexible
	* formatting tool.
	* @param {string} dateFormat - The format string. This string formats the start and end time.
	* See {@link DateTime#toFormat} for details.
	* @param {Object} opts - Options.
	* @param {string} [opts.separator =  ' – '] - A separator to place between the start and end
	* representations.
	* @return {string}
	*/
	toFormat(dateFormat, { separator = " – " } = {}) {
		if (!this.isValid) return INVALID;
		return `${this.s.toFormat(dateFormat)}${separator}${this.e.toFormat(dateFormat)}`;
	}
	/**
	* Return a Duration representing the time spanned by this interval.
	* @param {string|string[]} [unit=['milliseconds']] - the unit or units (such as 'hours' or 'days') to include in the duration.
	* @param {Object} opts - options that affect the creation of the Duration
	* @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use
	* @example Interval.fromDateTimes(dt1, dt2).toDuration().toObject() //=> { milliseconds: 88489257 }
	* @example Interval.fromDateTimes(dt1, dt2).toDuration('days').toObject() //=> { days: 1.0241812152777778 }
	* @example Interval.fromDateTimes(dt1, dt2).toDuration(['hours', 'minutes']).toObject() //=> { hours: 24, minutes: 34.82095 }
	* @example Interval.fromDateTimes(dt1, dt2).toDuration(['hours', 'minutes', 'seconds']).toObject() //=> { hours: 24, minutes: 34, seconds: 49.257 }
	* @example Interval.fromDateTimes(dt1, dt2).toDuration('seconds').toObject() //=> { seconds: 88489.257 }
	* @return {Duration}
	*/
	toDuration(unit, opts) {
		if (!this.isValid) return require_duration.default.invalid(this.invalidReason);
		return this.e.diff(this.s, unit, opts);
	}
	/**
	* Run mapFn on the interval start and end, returning a new Interval from the resulting DateTimes
	* @param {function} mapFn
	* @return {Interval}
	* @example Interval.fromDateTimes(dt1, dt2).mapEndpoints(endpoint => endpoint.toUTC())
	* @example Interval.fromDateTimes(dt1, dt2).mapEndpoints(endpoint => endpoint.plus({ hours: 2 }))
	*/
	mapEndpoints(mapFn) {
		return Interval.fromDateTimes(mapFn(this.s), mapFn(this.e));
	}
};

//#endregion
exports.default = Interval;