import { asRecord } from '../internal/utils.js';
export const RUNTIME_ROUTE_DESCRIBE_RESULT_RESPONSE_METADATA_KEY = 'x-nimi-route-describe-result';
export function normalizeRuntimeRouteSource(value) {
    return String(value || '').trim() === 'cloud' ? 'cloud' : 'local';
}
export function parseRuntimeCanonicalCapability(value) {
    const normalized = String(value || '').trim();
    if (normalized === 'text.generate'
        || normalized === 'text.embed'
        || normalized === 'image.generate'
        || normalized === 'video.generate'
        || normalized === 'world.generate'
        || normalized === 'audio.synthesize'
        || normalized === 'audio.transcribe'
        || normalized === 'music.generate'
        || normalized === 'voice_workflow.voice_clone'
        || normalized === 'voice_workflow.voice_design') {
        return normalized;
    }
    // Aliases — backward compatibility only, not canonical tokens
    if (normalized === 'music')
        return 'music.generate';
    return null;
}
export function parseRuntimeRouteMetadataKind(value) {
    const capability = parseRuntimeCanonicalCapability(value);
    if (capability === 'text.generate'
        || capability === 'audio.synthesize'
        || capability === 'audio.transcribe'
        || capability === 'voice_workflow.voice_clone'
        || capability === 'voice_workflow.voice_design') {
        return capability;
    }
    return null;
}
function parseLocalProviderAdapter(value) {
    const normalized = String(value || '').trim();
    if (normalized === 'openai_compat_adapter'
        || normalized === 'llama_native_adapter'
        || normalized === 'media_native_adapter'
        || normalized === 'speech_native_adapter'
        || normalized === 'sidecar_music_adapter') {
        return normalized;
    }
    return undefined;
}
export function parseRuntimeRouteBinding(value) {
    if (!value || typeof value !== 'object')
        return null;
    const record = asRecord(value);
    return {
        source: normalizeRuntimeRouteSource(record.source),
        connectorId: String(record.connectorId || ''),
        model: String(record.model || ''),
        modelLabel: String(record.modelLabel || '').trim() || undefined,
        modelId: String(record.modelId || '').trim() || undefined,
        provider: String(record.provider || '').trim() || undefined,
        localModelId: String(record.localModelId || '').trim() || undefined,
        engine: String(record.engine || '').trim() || undefined,
        adapter: parseLocalProviderAdapter(record.adapter),
        providerHints: record.providerHints && typeof record.providerHints === 'object' && !Array.isArray(record.providerHints)
            ? record.providerHints
            : undefined,
        endpoint: String(record.endpoint || '').trim() || undefined,
        goRuntimeLocalModelId: String(record.goRuntimeLocalModelId || '').trim() || undefined,
        goRuntimeStatus: String(record.goRuntimeStatus || '').trim() || undefined,
    };
}
function toPositiveInt(value) {
    const numeric = Number(value);
    if (!Number.isFinite(numeric))
        return undefined;
    const rounded = Math.floor(numeric);
    return rounded > 0 ? rounded : undefined;
}
function normalizeContextSource(value) {
    const normalized = String(value || '').trim();
    if (normalized === 'provider-api'
        || normalized === 'template'
        || normalized === 'default'
        || normalized === 'unknown') {
        return normalized;
    }
    return undefined;
}
function parseRuntimeRouteModelProfiles(value) {
    if (!Array.isArray(value))
        return [];
    const deduped = new Set();
    const parsed = [];
    for (const item of value) {
        const record = asRecord(item);
        const model = String(record.model || record.id || '').trim();
        if (!model)
            continue;
        const dedupeKey = model.toLowerCase();
        if (deduped.has(dedupeKey))
            continue;
        deduped.add(dedupeKey);
        const maxContextTokens = toPositiveInt(record.maxContextTokens);
        const maxOutputTokens = toPositiveInt(record.maxOutputTokens);
        const contextSource = normalizeContextSource(record.contextSource);
        parsed.push({
            model,
            ...(typeof maxContextTokens === 'number' ? { maxContextTokens } : {}),
            ...(typeof maxOutputTokens === 'number' ? { maxOutputTokens } : {}),
            ...(contextSource ? { contextSource } : {}),
        });
    }
    return parsed;
}
function parseRuntimeRouteConnectorModelCapabilities(value) {
    if (!value || typeof value !== 'object' || Array.isArray(value)) {
        return undefined;
    }
    const rawRecord = asRecord(value);
    const normalized = {};
    for (const [rawModelId, rawCapabilities] of Object.entries(rawRecord)) {
        const modelId = String(rawModelId || '').trim();
        if (!modelId)
            continue;
        const capabilities = Array.isArray(rawCapabilities)
            ? rawCapabilities.map((capability) => String(capability || '').trim()).filter(Boolean)
            : [];
        if (capabilities.length === 0)
            continue;
        normalized[modelId] = capabilities;
    }
    return Object.keys(normalized).length > 0 ? normalized : undefined;
}
function parseLocalModels(value) {
    if (!Array.isArray(value))
        return [];
    const deduped = new Set();
    const models = [];
    for (const item of value) {
        const record = asRecord(item);
        const localModelId = String(record.localModelId || record.id || '').trim();
        if (!localModelId)
            continue;
        const dedupeKey = localModelId.toLowerCase();
        if (deduped.has(dedupeKey))
            continue;
        deduped.add(dedupeKey);
        const model = String(record.model || record.name || '').trim() || localModelId;
        models.push({
            localModelId,
            label: String(record.label || '').trim() || undefined,
            engine: String(record.engine || '').trim() || undefined,
            model,
            modelId: String(record.modelId || '').trim() || undefined,
            provider: String(record.provider || '').trim() || undefined,
            adapter: parseLocalProviderAdapter(record.adapter),
            providerHints: record.providerHints && typeof record.providerHints === 'object' && !Array.isArray(record.providerHints)
                ? record.providerHints
                : undefined,
            endpoint: String(record.endpoint || '').trim() || undefined,
            status: String(record.status || '').trim() || undefined,
            goRuntimeLocalModelId: String(record.goRuntimeLocalModelId || '').trim() || undefined,
            goRuntimeStatus: String(record.goRuntimeStatus || '').trim() || undefined,
            capabilities: Array.isArray(record.capabilities)
                ? record.capabilities.map((capability) => String(capability || '').trim()).filter(Boolean)
                : undefined,
        });
    }
    return models;
}
function parseTextGenerateRouteMetadata(value) {
    const record = asRecord(value);
    const traceModeSupport = String(record.traceModeSupport || '').trim();
    if (typeof record.supportsThinking !== 'boolean'
        || typeof record.supportsImageInput !== 'boolean'
        || typeof record.supportsAudioInput !== 'boolean'
        || typeof record.supportsVideoInput !== 'boolean'
        || typeof record.supportsArtifactRefInput !== 'boolean'
        || (traceModeSupport !== 'none'
            && traceModeSupport !== 'hide'
            && traceModeSupport !== 'separate')) {
        return null;
    }
    return {
        supportsThinking: record.supportsThinking,
        traceModeSupport,
        supportsImageInput: record.supportsImageInput,
        supportsAudioInput: record.supportsAudioInput,
        supportsVideoInput: record.supportsVideoInput,
        supportsArtifactRefInput: record.supportsArtifactRefInput,
    };
}
function parseRuntimeNumericRange(value) {
    if (!value || typeof value !== 'object' || Array.isArray(value)) {
        return undefined;
    }
    const record = asRecord(value);
    const min = Number(record.min);
    const max = Number(record.max);
    if (!Number.isFinite(min) || !Number.isFinite(max) || max < min) {
        return undefined;
    }
    return { min, max };
}
function parseSpeechSynthesizeRouteMetadata(value) {
    const record = asRecord(value);
    const supportedAudioFormats = Array.isArray(record.supportedAudioFormats)
        ? record.supportedAudioFormats.map((item) => String(item || '').trim()).filter(Boolean)
        : [];
    const supportedTimingModesRaw = Array.isArray(record.supportedTimingModes)
        ? record.supportedTimingModes.map((item) => String(item || '').trim())
        : [];
    const supportedTimingModes = supportedTimingModesRaw.filter((mode) => (mode === 'none' || mode === 'word' || mode === 'char'));
    if (supportedAudioFormats.length === 0
        || supportedTimingModes.length !== supportedTimingModesRaw.length
        || supportedTimingModes.length === 0
        || typeof record.supportsLanguage !== 'boolean'
        || typeof record.supportsEmotion !== 'boolean') {
        return null;
    }
    const hintsRecord = asRecord(record.voiceRenderHints);
    const voiceRenderHints = {};
    const stability = parseRuntimeNumericRange(hintsRecord.stability);
    const similarityBoost = parseRuntimeNumericRange(hintsRecord.similarityBoost);
    const style = parseRuntimeNumericRange(hintsRecord.style);
    const speed = parseRuntimeNumericRange(hintsRecord.speed);
    if (stability) {
        voiceRenderHints.stability = stability;
    }
    if (similarityBoost) {
        voiceRenderHints.similarityBoost = similarityBoost;
    }
    if (style) {
        voiceRenderHints.style = style;
    }
    if (speed) {
        voiceRenderHints.speed = speed;
    }
    if (typeof hintsRecord.useSpeakerBoost === 'boolean') {
        voiceRenderHints.useSpeakerBoost = hintsRecord.useSpeakerBoost;
    }
    return {
        supportedAudioFormats,
        defaultAudioFormat: String(record.defaultAudioFormat || '').trim() || undefined,
        supportedTimingModes,
        supportsLanguage: record.supportsLanguage,
        supportsEmotion: record.supportsEmotion,
        ...(Object.keys(voiceRenderHints).length > 0 ? { voiceRenderHints } : {}),
        providerExtensionNamespace: String(record.providerExtensionNamespace || '').trim() || undefined,
        providerExtensionSchemaVersion: String(record.providerExtensionSchemaVersion || '').trim() || undefined,
    };
}
function parseSpeechTranscribeRouteMetadata(value) {
    const record = asRecord(value);
    const tiers = Array.isArray(record.tiers)
        ? record.tiers.map((item) => String(item || '').trim()).filter(Boolean)
        : [];
    const supportedResponseFormats = Array.isArray(record.supportedResponseFormats)
        ? record.supportedResponseFormats.map((item) => String(item || '').trim()).filter(Boolean)
        : [];
    const maxSpeakerCountNumeric = Number(record.maxSpeakerCount);
    if (tiers.length === 0
        || supportedResponseFormats.length === 0
        || typeof record.supportsLanguage !== 'boolean'
        || typeof record.supportsPrompt !== 'boolean'
        || typeof record.supportsTimestamps !== 'boolean'
        || typeof record.supportsDiarization !== 'boolean'
        || (String(record.maxSpeakerCount || '').trim() !== ''
            && (!Number.isFinite(maxSpeakerCountNumeric) || maxSpeakerCountNumeric < 0))) {
        return null;
    }
    return {
        tiers,
        supportedResponseFormats,
        supportsLanguage: record.supportsLanguage,
        supportsPrompt: record.supportsPrompt,
        supportsTimestamps: record.supportsTimestamps,
        supportsDiarization: record.supportsDiarization,
        ...(Number.isFinite(maxSpeakerCountNumeric) && maxSpeakerCountNumeric > 0 ? { maxSpeakerCount: maxSpeakerCountNumeric } : {}),
        providerExtensionNamespace: String(record.providerExtensionNamespace || '').trim() || undefined,
        providerExtensionSchemaVersion: String(record.providerExtensionSchemaVersion || '').trim() || undefined,
    };
}
function parseVoiceWorkflowVoiceCloneRouteMetadata(value) {
    const record = asRecord(value);
    const textPromptMode = String(record.textPromptMode || '').trim();
    const allowedReferenceAudioMimeTypes = Array.isArray(record.allowedReferenceAudioMimeTypes)
        ? record.allowedReferenceAudioMimeTypes.map((item) => String(item || '').trim()).filter(Boolean)
        : [];
    if (record.workflowType !== 'voice_clone'
        || (textPromptMode !== 'unsupported' && textPromptMode !== 'optional' && textPromptMode !== 'required')
        || typeof record.requiresTargetSynthesisBinding !== 'boolean'
        || typeof record.supportsLanguageHints !== 'boolean'
        || typeof record.supportsPreferredName !== 'boolean'
        || typeof record.referenceAudioUriInput !== 'boolean'
        || typeof record.referenceAudioBytesInput !== 'boolean'
        || allowedReferenceAudioMimeTypes.length === 0) {
        return null;
    }
    return {
        workflowType: 'voice_clone',
        requiresTargetSynthesisBinding: record.requiresTargetSynthesisBinding,
        textPromptMode,
        supportsLanguageHints: record.supportsLanguageHints,
        supportsPreferredName: record.supportsPreferredName,
        referenceAudioUriInput: record.referenceAudioUriInput,
        referenceAudioBytesInput: record.referenceAudioBytesInput,
        allowedReferenceAudioMimeTypes,
        providerExtensionNamespace: String(record.providerExtensionNamespace || '').trim() || undefined,
        providerExtensionSchemaVersion: String(record.providerExtensionSchemaVersion || '').trim() || undefined,
    };
}
function parseVoiceWorkflowVoiceDesignRouteMetadata(value) {
    const record = asRecord(value);
    const instructionTextMode = String(record.instructionTextMode || '').trim();
    const previewTextMode = String(record.previewTextMode || '').trim();
    if (record.workflowType !== 'voice_design'
        || typeof record.requiresTargetSynthesisBinding !== 'boolean'
        || (instructionTextMode !== 'unsupported' && instructionTextMode !== 'optional' && instructionTextMode !== 'required')
        || (previewTextMode !== 'unsupported' && previewTextMode !== 'optional' && previewTextMode !== 'required')
        || typeof record.supportsLanguage !== 'boolean'
        || typeof record.supportsPreferredName !== 'boolean') {
        return null;
    }
    return {
        workflowType: 'voice_design',
        requiresTargetSynthesisBinding: record.requiresTargetSynthesisBinding,
        instructionTextMode,
        previewTextMode,
        supportsLanguage: record.supportsLanguage,
        supportsPreferredName: record.supportsPreferredName,
        providerExtensionNamespace: String(record.providerExtensionNamespace || '').trim() || undefined,
        providerExtensionSchemaVersion: String(record.providerExtensionSchemaVersion || '').trim() || undefined,
    };
}
export function parseRuntimeRouteOptions(value, options) {
    const record = asRecord(value);
    const capability = parseRuntimeCanonicalCapability(record.capability) || undefined;
    const selected = record.selected === null
        ? null
        : (parseRuntimeRouteBinding(record.selected) || null);
    const resolvedDefault = parseRuntimeRouteBinding(record.resolvedDefault) || undefined;
    const local = asRecord(record.local);
    const localModels = parseLocalModels(local.models);
    const connectors = (Array.isArray(record.connectors) ? record.connectors : [])
        .filter((item) => item && typeof item === 'object')
        .map((item) => {
        const connector = asRecord(item);
        const modelProfiles = parseRuntimeRouteModelProfiles(connector.modelProfiles);
        return {
            id: String(connector.id || ''),
            label: String(connector.label || ''),
            vendor: String(connector.vendor || '').trim() || undefined,
            provider: String(connector.provider || '').trim() || undefined,
            models: Array.isArray(connector.models)
                ? connector.models.map((model) => String(model || '').trim()).filter(Boolean)
                : [],
            modelCapabilities: parseRuntimeRouteConnectorModelCapabilities(connector.modelCapabilities),
            ...(modelProfiles.length > 0 ? { modelProfiles } : {}),
        };
    })
        .filter((item) => item.id);
    return {
        ...(capability ? { capability } : {}),
        selected,
        ...(options?.includeResolvedDefault ? { resolvedDefault: resolvedDefault || selected || undefined } : {}),
        local: {
            models: localModels,
            defaultEndpoint: String(local.defaultEndpoint || '').trim() || undefined,
        },
        connectors,
    };
}
export function parseRuntimeRouteDescribeResult(value) {
    const record = asRecord(value);
    const capability = parseRuntimeCanonicalCapability(record.capability);
    const metadataVersion = String(record.metadataVersion || '').trim();
    const resolvedBindingRef = String(record.resolvedBindingRef || '').trim();
    const metadataKind = parseRuntimeRouteMetadataKind(record.metadataKind);
    if (!capability || metadataVersion !== 'v1' || !resolvedBindingRef || !metadataKind) {
        return null;
    }
    if (capability !== metadataKind) {
        return null;
    }
    if (capability === 'text.generate') {
        const metadata = parseTextGenerateRouteMetadata(record.metadata);
        if (!metadata)
            return null;
        return {
            capability: 'text.generate',
            metadataVersion: 'v1',
            resolvedBindingRef,
            metadataKind: 'text.generate',
            metadata,
        };
    }
    if (capability === 'audio.synthesize') {
        const metadata = parseSpeechSynthesizeRouteMetadata(record.metadata);
        if (!metadata)
            return null;
        return {
            capability: 'audio.synthesize',
            metadataVersion: 'v1',
            resolvedBindingRef,
            metadataKind: 'audio.synthesize',
            metadata,
        };
    }
    if (capability === 'audio.transcribe') {
        const metadata = parseSpeechTranscribeRouteMetadata(record.metadata);
        if (!metadata)
            return null;
        return {
            capability: 'audio.transcribe',
            metadataVersion: 'v1',
            resolvedBindingRef,
            metadataKind: 'audio.transcribe',
            metadata,
        };
    }
    if (capability === 'voice_workflow.voice_clone') {
        const metadata = parseVoiceWorkflowVoiceCloneRouteMetadata(record.metadata);
        if (!metadata)
            return null;
        return {
            capability: 'voice_workflow.voice_clone',
            metadataVersion: 'v1',
            resolvedBindingRef,
            metadataKind: 'voice_workflow.voice_clone',
            metadata,
        };
    }
    const metadata = parseVoiceWorkflowVoiceDesignRouteMetadata(record.metadata);
    if (!metadata)
        return null;
    return {
        capability: 'voice_workflow.voice_design',
        metadataVersion: 'v1',
        resolvedBindingRef,
        metadataKind: 'voice_workflow.voice_design',
        metadata,
    };
}
function createRouteDescribeDecodeError(code) {
    const error = new Error(code);
    error.name = code;
    return error;
}
function decodeBase64Text(value) {
    const normalized = String(value || '').trim();
    if (!normalized) {
        throw createRouteDescribeDecodeError('RUNTIME_ROUTE_DESCRIBE_METADATA_HEADER_MISSING');
    }
    const globalBuffer = globalThis;
    if (globalBuffer.Buffer) {
        return globalBuffer.Buffer.from(normalized, 'base64').toString('utf8');
    }
    if (typeof globalThis.atob === 'function') {
        return globalThis.atob(normalized);
    }
    throw createRouteDescribeDecodeError('RUNTIME_ROUTE_DESCRIBE_METADATA_BASE64_UNAVAILABLE');
}
export function decodeRuntimeRouteDescribeResultFromMetadata(input) {
    const encoded = String(input.metadata?.[RUNTIME_ROUTE_DESCRIBE_RESULT_RESPONSE_METADATA_KEY] || '').trim();
    if (!encoded) {
        throw createRouteDescribeDecodeError('RUNTIME_ROUTE_DESCRIBE_METADATA_HEADER_MISSING');
    }
    let parsedValue;
    try {
        parsedValue = JSON.parse(decodeBase64Text(encoded));
    }
    catch {
        throw createRouteDescribeDecodeError('RUNTIME_ROUTE_DESCRIBE_METADATA_DECODE_FAILED');
    }
    const parsed = parseRuntimeRouteDescribeResult(parsedValue);
    if (!parsed) {
        throw createRouteDescribeDecodeError('RUNTIME_ROUTE_DESCRIBE_METADATA_SCHEMA_INVALID');
    }
    if (input.expectedCapability && parsed.capability !== input.expectedCapability) {
        throw createRouteDescribeDecodeError('RUNTIME_ROUTE_DESCRIBE_METADATA_CAPABILITY_MISMATCH');
    }
    if (input.expectedResolvedBindingRef
        && parsed.resolvedBindingRef !== input.expectedResolvedBindingRef) {
        throw createRouteDescribeDecodeError('RUNTIME_ROUTE_DESCRIBE_METADATA_BINDING_REF_MISMATCH');
    }
    return parsed;
}
//# sourceMappingURL=runtime-route.js.map