const require_errors = require('../errors.js');
const require_IANAZone = require('../zones/IANAZone.js');
const require_fixedOffsetZone = require('../zones/fixedOffsetZone.js');
const require_digits = require('./digits.js');
const require_util = require('./util.js');
const require_formatter = require('./formatter.js');
const require_datetime = require('../datetime.js');

//#region ../../node_modules/.pnpm/luxon@3.5.0/node_modules/luxon/src/impl/tokenParser.js
const MISSING_FTP = "missing Intl.DateTimeFormat.formatToParts support";
function intUnit(regex, post = (i) => i) {
	return {
		regex,
		deser: ([s]) => post(require_digits.parseDigits(s))
	};
}
const spaceOrNBSP = `[ ${String.fromCharCode(160)}]`;
const spaceOrNBSPRegExp = new RegExp(spaceOrNBSP, "g");
function fixListRegex(s) {
	return s.replace(/\./g, "\\.?").replace(spaceOrNBSPRegExp, spaceOrNBSP);
}
function stripInsensitivities(s) {
	return s.replace(/\./g, "").replace(spaceOrNBSPRegExp, " ").toLowerCase();
}
function oneOf(strings, startIndex) {
	if (strings === null) return null;
	else return {
		regex: RegExp(strings.map(fixListRegex).join("|")),
		deser: ([s]) => strings.findIndex((i) => stripInsensitivities(s) === stripInsensitivities(i)) + startIndex
	};
}
function offset(regex, groups) {
	return {
		regex,
		deser: ([, h, m]) => require_util.signedOffset(h, m),
		groups
	};
}
function simple(regex) {
	return {
		regex,
		deser: ([s]) => s
	};
}
function escapeToken(value) {
	return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
}
/**
* @param token
* @param {Locale} loc
*/
function unitForToken(token, loc) {
	const one = require_digits.digitRegex(loc), two = require_digits.digitRegex(loc, "{2}"), three = require_digits.digitRegex(loc, "{3}"), four = require_digits.digitRegex(loc, "{4}"), six = require_digits.digitRegex(loc, "{6}"), oneOrTwo = require_digits.digitRegex(loc, "{1,2}"), oneToThree = require_digits.digitRegex(loc, "{1,3}"), oneToSix = require_digits.digitRegex(loc, "{1,6}"), oneToNine = require_digits.digitRegex(loc, "{1,9}"), twoToFour = require_digits.digitRegex(loc, "{2,4}"), fourToSix = require_digits.digitRegex(loc, "{4,6}"), literal = (t) => ({
		regex: RegExp(escapeToken(t.val)),
		deser: ([s]) => s,
		literal: true
	}), unitate = (t) => {
		if (token.literal) return literal(t);
		switch (t.val) {
			case "G": return oneOf(loc.eras("short"), 0);
			case "GG": return oneOf(loc.eras("long"), 0);
			case "y": return intUnit(oneToSix);
			case "yy": return intUnit(twoToFour, require_util.untruncateYear);
			case "yyyy": return intUnit(four);
			case "yyyyy": return intUnit(fourToSix);
			case "yyyyyy": return intUnit(six);
			case "M": return intUnit(oneOrTwo);
			case "MM": return intUnit(two);
			case "MMM": return oneOf(loc.months("short", true), 1);
			case "MMMM": return oneOf(loc.months("long", true), 1);
			case "L": return intUnit(oneOrTwo);
			case "LL": return intUnit(two);
			case "LLL": return oneOf(loc.months("short", false), 1);
			case "LLLL": return oneOf(loc.months("long", false), 1);
			case "d": return intUnit(oneOrTwo);
			case "dd": return intUnit(two);
			case "o": return intUnit(oneToThree);
			case "ooo": return intUnit(three);
			case "HH": return intUnit(two);
			case "H": return intUnit(oneOrTwo);
			case "hh": return intUnit(two);
			case "h": return intUnit(oneOrTwo);
			case "mm": return intUnit(two);
			case "m": return intUnit(oneOrTwo);
			case "q": return intUnit(oneOrTwo);
			case "qq": return intUnit(two);
			case "s": return intUnit(oneOrTwo);
			case "ss": return intUnit(two);
			case "S": return intUnit(oneToThree);
			case "SSS": return intUnit(three);
			case "u": return simple(oneToNine);
			case "uu": return simple(oneOrTwo);
			case "uuu": return intUnit(one);
			case "a": return oneOf(loc.meridiems(), 0);
			case "kkkk": return intUnit(four);
			case "kk": return intUnit(twoToFour, require_util.untruncateYear);
			case "W": return intUnit(oneOrTwo);
			case "WW": return intUnit(two);
			case "E":
			case "c": return intUnit(one);
			case "EEE": return oneOf(loc.weekdays("short", false), 1);
			case "EEEE": return oneOf(loc.weekdays("long", false), 1);
			case "ccc": return oneOf(loc.weekdays("short", true), 1);
			case "cccc": return oneOf(loc.weekdays("long", true), 1);
			case "Z":
			case "ZZ": return offset(/* @__PURE__ */ new RegExp(`([+-]${oneOrTwo.source})(?::(${two.source}))?`), 2);
			case "ZZZ": return offset(/* @__PURE__ */ new RegExp(`([+-]${oneOrTwo.source})(${two.source})?`), 2);
			case "z": return simple(/[a-z_+-/]{1,256}?/i);
			case " ": return simple(/[^\S\n\r]/);
			default: return literal(t);
		}
	};
	const unit = unitate(token) || { invalidReason: MISSING_FTP };
	unit.token = token;
	return unit;
}
const partTypeStyleToTokenVal = {
	year: {
		"2-digit": "yy",
		numeric: "yyyyy"
	},
	month: {
		numeric: "M",
		"2-digit": "MM",
		short: "MMM",
		long: "MMMM"
	},
	day: {
		numeric: "d",
		"2-digit": "dd"
	},
	weekday: {
		short: "EEE",
		long: "EEEE"
	},
	dayperiod: "a",
	dayPeriod: "a",
	hour12: {
		numeric: "h",
		"2-digit": "hh"
	},
	hour24: {
		numeric: "H",
		"2-digit": "HH"
	},
	minute: {
		numeric: "m",
		"2-digit": "mm"
	},
	second: {
		numeric: "s",
		"2-digit": "ss"
	},
	timeZoneName: {
		long: "ZZZZZ",
		short: "ZZZ"
	}
};
function tokenForPart(part, formatOpts, resolvedOpts) {
	const { type, value } = part;
	if (type === "literal") {
		const isSpace = /^\s+$/.test(value);
		return {
			literal: !isSpace,
			val: isSpace ? " " : value
		};
	}
	const style = formatOpts[type];
	let actualType = type;
	if (type === "hour") if (formatOpts.hour12 != null) actualType = formatOpts.hour12 ? "hour12" : "hour24";
	else if (formatOpts.hourCycle != null) if (formatOpts.hourCycle === "h11" || formatOpts.hourCycle === "h12") actualType = "hour12";
	else actualType = "hour24";
	else actualType = resolvedOpts.hour12 ? "hour12" : "hour24";
	let val = partTypeStyleToTokenVal[actualType];
	if (typeof val === "object") val = val[style];
	if (val) return {
		literal: false,
		val
	};
}
function buildRegex(units) {
	return [`^${units.map((u) => u.regex).reduce((f, r) => `${f}(${r.source})`, "")}$`, units];
}
function match(input, regex, handlers) {
	const matches = input.match(regex);
	if (matches) {
		const all = {};
		let matchIndex = 1;
		for (const i in handlers) if (require_util.hasOwnProperty(handlers, i)) {
			const h = handlers[i], groups = h.groups ? h.groups + 1 : 1;
			if (!h.literal && h.token) all[h.token.val[0]] = h.deser(matches.slice(matchIndex, matchIndex + groups));
			matchIndex += groups;
		}
		return [matches, all];
	} else return [matches, {}];
}
function dateTimeFromMatches(matches) {
	const toField = (token) => {
		switch (token) {
			case "S": return "millisecond";
			case "s": return "second";
			case "m": return "minute";
			case "h":
			case "H": return "hour";
			case "d": return "day";
			case "o": return "ordinal";
			case "L":
			case "M": return "month";
			case "y": return "year";
			case "E":
			case "c": return "weekday";
			case "W": return "weekNumber";
			case "k": return "weekYear";
			case "q": return "quarter";
			default: return null;
		}
	};
	let zone = null;
	let specificOffset;
	if (!require_util.isUndefined(matches.z)) zone = require_IANAZone.default.create(matches.z);
	if (!require_util.isUndefined(matches.Z)) {
		if (!zone) zone = new require_fixedOffsetZone.default(matches.Z);
		specificOffset = matches.Z;
	}
	if (!require_util.isUndefined(matches.q)) matches.M = (matches.q - 1) * 3 + 1;
	if (!require_util.isUndefined(matches.h)) {
		if (matches.h < 12 && matches.a === 1) matches.h += 12;
		else if (matches.h === 12 && matches.a === 0) matches.h = 0;
	}
	if (matches.G === 0 && matches.y) matches.y = -matches.y;
	if (!require_util.isUndefined(matches.u)) matches.S = require_util.parseMillis(matches.u);
	return [
		Object.keys(matches).reduce((r, k) => {
			const f = toField(k);
			if (f) r[f] = matches[k];
			return r;
		}, {}),
		zone,
		specificOffset
	];
}
let dummyDateTimeCache = null;
function getDummyDateTime() {
	if (!dummyDateTimeCache) dummyDateTimeCache = require_datetime.default.fromMillis(1555555555555);
	return dummyDateTimeCache;
}
function maybeExpandMacroToken(token, locale) {
	if (token.literal) return token;
	const tokens = formatOptsToTokens(require_formatter.default.macroTokenToFormatOpts(token.val), locale);
	if (tokens == null || tokens.includes(void 0)) return token;
	return tokens;
}
function expandMacroTokens(tokens, locale) {
	return Array.prototype.concat(...tokens.map((t) => maybeExpandMacroToken(t, locale)));
}
/**
* @private
*/
var TokenParser = class {
	constructor(locale, format) {
		this.locale = locale;
		this.format = format;
		this.tokens = expandMacroTokens(require_formatter.default.parseFormat(format), locale);
		this.units = this.tokens.map((t) => unitForToken(t, locale));
		this.disqualifyingUnit = this.units.find((t) => t.invalidReason);
		if (!this.disqualifyingUnit) {
			const [regexString, handlers] = buildRegex(this.units);
			this.regex = RegExp(regexString, "i");
			this.handlers = handlers;
		}
	}
	explainFromTokens(input) {
		if (!this.isValid) return {
			input,
			tokens: this.tokens,
			invalidReason: this.invalidReason
		};
		else {
			const [rawMatches, matches] = match(input, this.regex, this.handlers), [result, zone, specificOffset] = matches ? dateTimeFromMatches(matches) : [
				null,
				null,
				void 0
			];
			if (require_util.hasOwnProperty(matches, "a") && require_util.hasOwnProperty(matches, "H")) throw new require_errors.ConflictingSpecificationError("Can't include meridiem when specifying 24-hour format");
			return {
				input,
				tokens: this.tokens,
				regex: this.regex,
				rawMatches,
				matches,
				result,
				zone,
				specificOffset
			};
		}
	}
	get isValid() {
		return !this.disqualifyingUnit;
	}
	get invalidReason() {
		return this.disqualifyingUnit ? this.disqualifyingUnit.invalidReason : null;
	}
};
function explainFromTokens(locale, input, format) {
	return new TokenParser(locale, format).explainFromTokens(input);
}
function parseFromTokens(locale, input, format) {
	const { result, zone, specificOffset, invalidReason } = explainFromTokens(locale, input, format);
	return [
		result,
		zone,
		specificOffset,
		invalidReason
	];
}
function formatOptsToTokens(formatOpts, locale) {
	if (!formatOpts) return null;
	const df = require_formatter.default.create(locale, formatOpts).dtFormatter(getDummyDateTime());
	const parts = df.formatToParts();
	const resolvedOpts = df.resolvedOptions();
	return parts.map((p) => tokenForPart(p, formatOpts, resolvedOpts));
}

//#endregion
exports.TokenParser = TokenParser;
exports.expandMacroTokens = expandMacroTokens;
exports.explainFromTokens = explainFromTokens;
exports.formatOptsToTokens = formatOptsToTokens;
exports.parseFromTokens = parseFromTokens;