import { compileInputAccess } from "./variable-access.js";
import { escapeForDoubleQuoteString } from "../services/codegen/escape.js";
/**
 * The functions shipped in the generated registry.js file.
 *
 * @see createRegistry()
 */
const registryFunctionNames = new Set([
    "plural",
    "number",
    "datetime",
    "relativetime",
]);
export function isRegistryFunction(name) {
    return registryFunctionNames.has(name);
}
export function registryFunctionNamesForDisplay() {
    return Array.from(registryFunctionNames).join(", ");
}
/**
 * Wraps a compiled expression value in a `registry.*` call if an
 * annotation is present.
 *
 * @example
 *   compileAnnotation("i?.count", "en", { type: "function-reference", name: "number", options: [] })
 *   >> 'registry.number("en", i?.count, {})'
 */
export function compileAnnotation(str, locale, annotation) {
    if (!annotation) {
        return str;
    }
    if (annotation.name === "relativetime") {
        validateRelativeTimeOptions(annotation);
    }
    return `registry.${annotation.name}("${locale}", ${str}, ${compileOptions(annotation.name, annotation.options)})`;
}
function compileOptions(annotationName, options) {
    if (options.length === 0) {
        return "{}";
    }
    const entries = options.map((option) => `${option.name}: ${compileOptionLiteralOrVarRef(annotationName, option.name, option.value)}`);
    const code = "{ " + entries.join(", ") + " }";
    return code;
}
const numericOptionNamesByAnnotation = {
    number: new Set([
        "minimumIntegerDigits",
        "minimumFractionDigits",
        "maximumFractionDigits",
        "minimumSignificantDigits",
        "maximumSignificantDigits",
        "roundingIncrement",
    ]),
    plural: new Set([
        "minimumIntegerDigits",
        "minimumFractionDigits",
        "maximumFractionDigits",
        "minimumSignificantDigits",
        "maximumSignificantDigits",
    ]),
    datetime: new Set(["fractionalSecondDigits"]),
};
const booleanOptionNamesByAnnotation = {
    number: new Set(["useGrouping"]),
    datetime: new Set(["hour12"]),
};
const jsNonNegativeIntegerPattern = /^(?:0|[1-9]\d*)$/;
function compileOptionLiteralOrVarRef(annotationName, optionName, value) {
    if (value.type === "variable-reference") {
        if (annotationName === "relativetime" && optionName === "unit") {
            return `/** @type {import("../registry.js").RelativeTimeFormatUnit} */ (${compileInputAccess(value.name)})`;
        }
        return compileInputAccess(value.name);
    }
    if (annotationName === "relativetime" &&
        optionName === "unit" &&
        isDollarVariableReference(value.value)) {
        return `/** @type {import("../registry.js").RelativeTimeFormatUnit} */ (${compileInputAccess(value.value.slice(1))})`;
    }
    if (shouldEmitNumberLiteral(annotationName, optionName, value.value)) {
        return value.value;
    }
    if (shouldEmitBooleanLiteral(annotationName, optionName, value.value)) {
        return value.value;
    }
    return `"${escapeForDoubleQuoteString(value.value)}"`;
}
function shouldEmitNumberLiteral(annotationName, optionName, literal) {
    return (numericOptionNamesByAnnotation[annotationName]?.has(optionName) === true &&
        jsNonNegativeIntegerPattern.test(literal));
}
function shouldEmitBooleanLiteral(annotationName, optionName, literal) {
    return (booleanOptionNamesByAnnotation[annotationName]?.has(optionName) === true &&
        (literal === "true" || literal === "false"));
}
const relativeTimeUnits = new Set([
    "year",
    "years",
    "quarter",
    "quarters",
    "month",
    "months",
    "week",
    "weeks",
    "day",
    "days",
    "hour",
    "hours",
    "minute",
    "minutes",
    "second",
    "seconds",
]);
function validateRelativeTimeOptions(annotation) {
    const unitOptions = annotation.options.filter((option) => option.name === "unit");
    if (unitOptions.length === 0) {
        throw new Error('The "relativetime" formatter requires a "unit" option.');
    }
    if (unitOptions.length > 1) {
        throw new Error('The "relativetime" formatter requires exactly one "unit" option.');
    }
    const [unitOption] = unitOptions;
    if (!unitOption || unitOption.value.type === "variable-reference") {
        return;
    }
    if (isDollarVariableReference(unitOption.value.value)) {
        return;
    }
    if (!relativeTimeUnits.has(unitOption.value.value)) {
        throw new Error(`Invalid "relativetime" unit "${unitOption.value.value}". Expected one of: ${Array.from(relativeTimeUnits).join(", ")}.`);
    }
}
function isDollarVariableReference(value) {
    return value.startsWith("$") && value.length > 1;
}
//# sourceMappingURL=compile-annotation.js.map