import { normalizeSREMathSpeechOptions, } from "./tts/math-speech.js";
const VALID_TTS_LAYOUT_MODES = new Set([
    "reserved-row",
    "expanding-row",
    "floating-overlay",
    "left-aligned",
]);
const toRecord = (value) => value && typeof value === "object" ? value : {};
const isServerBackend = (backend) => backend === "polly" || backend === "google" || backend === "server";
const withDefault = (value, fallback) => value === undefined ? fallback : value;
export const normalizeTTSLayoutMode = (value, fallback = "left-aligned") => typeof value === "string" &&
    VALID_TTS_LAYOUT_MODES.has(value)
    ? value
    : fallback;
/** Default inline TTS speed button multipliers (excluding 1.0×). */
export const DEFAULT_TTS_SPEED_OPTIONS = Object.freeze([0.8, 1.25]);
const normalizeSpeedRate = (entry) => {
    if (typeof entry !== "number" || !Number.isFinite(entry) || entry <= 0) {
        return undefined;
    }
    const rounded = Math.round(entry * 100) / 100;
    return rounded === 1 ? undefined : rounded;
};
const trimOptionalText = (value) => {
    if (typeof value !== "string")
        return undefined;
    const trimmed = value.trim();
    return trimmed.length ? trimmed : undefined;
};
const formatSpeedLabel = (rate) => `${rate}x`;
const formatSpeedAriaLabel = (label, usedDefaultLabel) => usedDefaultLabel ? `Speed ${label}` : `${label} speed`;
const normalizeSpeedAriaLabel = (label, ariaLabel, usedDefaultLabel) => {
    if (!ariaLabel)
        return formatSpeedAriaLabel(label, usedDefaultLabel);
    if (ariaLabel.toLowerCase().includes(label.toLowerCase()))
        return ariaLabel;
    return `${label} ${ariaLabel}`;
};
const normalizeTTSSpeedOptionConfig = (entry) => {
    if (typeof entry === "number")
        return normalizeSpeedRate(entry);
    if (!entry || typeof entry !== "object" || Array.isArray(entry))
        return undefined;
    const record = entry;
    const rate = normalizeSpeedRate(record.rate);
    if (rate === undefined)
        return undefined;
    const label = trimOptionalText(record.label);
    const ariaLabel = trimOptionalText(record.ariaLabel);
    return {
        rate,
        ...(label ? { label } : {}),
        ...(ariaLabel ? { ariaLabel } : {}),
    };
};
export const normalizeTTSSpeedOptionConfigs = (value) => {
    if (!Array.isArray(value))
        return [...DEFAULT_TTS_SPEED_OPTIONS];
    if (value.length === 0)
        return [];
    const dedupedRates = new Set();
    const normalized = [];
    for (const entry of value) {
        const option = normalizeTTSSpeedOptionConfig(entry);
        if (option === undefined)
            continue;
        const rate = typeof option === "number" ? option : option.rate;
        if (dedupedRates.has(rate))
            continue;
        dedupedRates.add(rate);
        normalized.push(option);
    }
    return normalized.length ? normalized : [...DEFAULT_TTS_SPEED_OPTIONS];
};
/**
 * Resolves inline toolbar speed options from host config.
 * - Omitted/non-array: default speed buttons.
 * - Empty array: hide speed buttons.
 * - Arrays that sanitize to no valid values: default speed buttons.
 * - 1.0× is never shown as a discrete speed button.
 */
export const normalizeTTSSpeedOptions = (value) => {
    if (!Array.isArray(value))
        return [...DEFAULT_TTS_SPEED_OPTIONS];
    if (value.length === 0)
        return [];
    return normalizeTTSSpeedOptionConfigs(value).map((option) => typeof option === "number" ? option : option.rate);
};
export const normalizeTTSSpeedControlOptions = (value) => normalizeTTSSpeedOptionConfigs(value).map((option) => {
    if (typeof option === "number") {
        const label = formatSpeedLabel(option);
        return {
            rate: option,
            label,
            ariaLabel: formatSpeedAriaLabel(label, true),
        };
    }
    const defaultLabel = formatSpeedLabel(option.rate);
    const label = option.label || defaultLabel;
    return {
        rate: option.rate,
        label,
        ariaLabel: normalizeSpeedAriaLabel(label, option.ariaLabel, label === defaultLabel),
    };
});
export const formatTTSSpeedOptionsAsText = (values) => values.join(", ");
/**
 * Parse comma/semicolon-separated multipliers from settings UI text.
 * - Empty or whitespace-only string → hide speed buttons (`[]`).
 * - Non-empty text with no parseable finite numbers → same as invalid-only array config: defaults.
 */
export const parseTTSSpeedOptionsFromText = (text) => {
    const trimmed = text.trim();
    if (!trimmed)
        return [];
    const parts = trimmed
        .split(/[,;]+/)
        .map((p) => p.trim())
        .filter(Boolean);
    const candidates = parts
        .map((p) => Number.parseFloat(p))
        .filter((n) => Number.isFinite(n));
    if (!candidates.length)
        return [...DEFAULT_TTS_SPEED_OPTIONS];
    return normalizeTTSSpeedOptions(candidates);
};
const applyRuntimeDefaults = (config) => {
    const withLayoutDefaults = {
        ...config,
        layoutMode: normalizeTTSLayoutMode(config.layoutMode),
    };
    const backend = config.backend || "browser";
    if (!isServerBackend(backend))
        return withLayoutDefaults;
    const withServerDefaults = {
        ...withLayoutDefaults,
        apiEndpoint: withDefault(withLayoutDefaults.apiEndpoint, "/api/tts"),
        transportMode: withDefault(withLayoutDefaults.transportMode, "pie"),
        endpointValidationMode: withDefault(withLayoutDefaults.endpointValidationMode, "voices"),
        validateEndpoint: withDefault(withLayoutDefaults.validateEndpoint, true),
        includeAuthOnAssetFetch: withDefault(withLayoutDefaults.includeAuthOnAssetFetch, false),
        rate: withDefault(withLayoutDefaults.rate, 1.0),
        language: withDefault(withLayoutDefaults.language, "en-US"),
    };
    if (backend === "polly") {
        return {
            ...withServerDefaults,
            defaultVoice: withDefault(withServerDefaults.defaultVoice, "Joanna"),
            engine: withDefault(withServerDefaults.engine, "neural"),
            format: withDefault(withServerDefaults.format, "mp3"),
            speechMarksMode: withDefault(withServerDefaults.speechMarksMode, "word+sentence"),
        };
    }
    if (backend === "google") {
        return {
            ...withServerDefaults,
            defaultVoice: withDefault(withServerDefaults.defaultVoice, "en-US-Wavenet-A"),
        };
    }
    return withServerDefaults;
};
export const resolveTTSRuntimeSettings = (config) => {
    const configRecord = toRecord(config);
    const settingsRecord = toRecord(configRecord.settings);
    return applyRuntimeDefaults({
        ...configRecord,
        ...settingsRecord,
    });
};
export const resolveTTSBackend = (config) => config.backend || "browser";
export const resolveRuntimeProvider = (config, backend) => {
    if (backend === "polly" || backend === "google")
        return backend;
    if (backend === "server") {
        return config.serverProvider || config.provider;
    }
    return config.serverProvider || config.provider;
};
export const resolveTransportMode = (config, runtimeProvider) => config.transportMode || (runtimeProvider === "custom" ? "custom" : "pie");
export const buildRuntimeTTSConfig = (config) => {
    const backend = resolveTTSBackend(config);
    const runtimeProvider = resolveRuntimeProvider(config, backend);
    const transportMode = resolveTransportMode(config, runtimeProvider);
    const mathSpeech = normalizeSREMathSpeechOptions(config.mathSpeech);
    return {
        voice: config.defaultVoice,
        rate: config.rate,
        pitch: config.pitch,
        providerOptions: {
            ...(config.language ? { locale: config.language } : {}),
            ...(backend === "polly" && config.engine
                ? { engine: config.engine }
                : {}),
            ...(backend === "polly" && typeof config.sampleRate === "number"
                ? { sampleRate: config.sampleRate }
                : {}),
            ...(backend === "polly" && config.format
                ? { format: config.format }
                : {}),
            ...(backend === "polly"
                ? {
                    speechMarkTypes: config.speechMarksMode === "word+sentence"
                        ? ["word", "sentence"]
                        : ["word"],
                }
                : {}),
            ...(transportMode === "custom" && typeof config.cache === "boolean"
                ? { cache: config.cache }
                : {}),
            ...(transportMode === "custom" && config.speedRate
                ? { speedRate: config.speedRate }
                : {}),
            ...(transportMode === "custom" && config.lang_id
                ? { lang_id: config.lang_id }
                : {}),
            ...(mathSpeech ? { mathSpeech } : {}),
        },
        apiEndpoint: config.apiEndpoint,
        provider: runtimeProvider,
        language: config.language,
        transportMode,
        endpointMode: config.endpointMode,
        endpointValidationMode: config.endpointValidationMode,
        includeAuthOnAssetFetch: config.includeAuthOnAssetFetch,
        validateEndpoint: config.validateEndpoint,
        // Toolkit-level highlight setting carried through the config channel
        // (like apiEndpoint/transportMode); consumed by the highlight pipeline,
        // ignored by providers. Only forwarded when set so the pipeline default
        // (per-token enabled) applies otherwise.
        ...(typeof config.mathTokenHighlighting === "boolean"
            ? { mathTokenHighlighting: config.mathTokenHighlighting }
            : {}),
    };
};
export const resolveTTSLayoutMode = (config) => normalizeTTSLayoutMode(config.layoutMode);
export const resolveTTSHostToolbarLayout = (config) => {
    const layoutMode = resolveTTSLayoutMode(config);
    switch (layoutMode) {
        case "reserved-row":
            return {
                mount: "before-buttons",
                controlsRow: {
                    reserveSpace: true,
                    expandWhenToolActive: false,
                },
            };
        case "expanding-row":
            return {
                mount: "before-buttons",
                controlsRow: {
                    reserveSpace: false,
                    expandWhenToolActive: true,
                },
            };
        case "floating-overlay":
        case "left-aligned":
        default:
            return {
                mount: "before-buttons",
                controlsRow: {
                    reserveSpace: false,
                    expandWhenToolActive: false,
                },
            };
    }
};
//# sourceMappingURL=tts-runtime-config.js.map