import { PocketTtsOnnxTTS } from "./pocket/pocket-tts-onnx.js";
import { getLocalSpeechModelDir, DEFAULT_LOCAL_STT_MODEL, DEFAULT_LOCAL_TTS_MODEL, LocalSttModelIdSchema, LocalTtsModelIdSchema, } from "./models.js";
import { SherpaOfflineRecognizerEngine } from "./sherpa/sherpa-offline-recognizer.js";
import { SherpaOnlineRecognizerEngine } from "./sherpa/sherpa-online-recognizer.js";
import { SherpaOnnxParakeetSTT } from "./sherpa/sherpa-parakeet-stt.js";
import { SherpaParakeetRealtimeTranscriptionSession } from "./sherpa/sherpa-parakeet-realtime-session.js";
import { SherpaRealtimeTranscriptionSession } from "./sherpa/sherpa-realtime-session.js";
import { SherpaOnnxSTT } from "./sherpa/sherpa-stt.js";
import { SherpaOnnxTTS } from "./sherpa/sherpa-tts.js";
import { ensureSileroVadModel, SherpaSileroTurnDetectionProvider, } from "./sherpa/silero-vad-provider.js";
function buildModelDownloadHint(modelId) {
    return `Use 'seawork speech download --model ${modelId}' to download this model.`;
}
function resolveConfiguredLocalModels(speechConfig) {
    return {
        dictationLocalSttModel: LocalSttModelIdSchema.parse(speechConfig?.local?.models.dictationStt ?? DEFAULT_LOCAL_STT_MODEL),
        voiceLocalSttModel: LocalSttModelIdSchema.parse(speechConfig?.local?.models.voiceStt ?? DEFAULT_LOCAL_STT_MODEL),
        voiceLocalTtsModel: LocalTtsModelIdSchema.parse(speechConfig?.local?.models.voiceTts ?? DEFAULT_LOCAL_TTS_MODEL),
    };
}
export function getLocalSpeechAvailability(speechConfig) {
    const localConfig = speechConfig?.local ?? null;
    return {
        configured: Boolean(localConfig),
        modelsDir: localConfig?.modelsDir ?? null,
    };
}
function computeRequiredLocalModelIds(params) {
    const ids = new Set();
    if (params.providers.dictationStt.enabled !== false &&
        params.providers.dictationStt.provider === "local") {
        ids.add(params.models.dictationLocalSttModel);
    }
    if (params.providers.voiceStt.enabled !== false &&
        params.providers.voiceStt.provider === "local") {
        ids.add(params.models.voiceLocalSttModel);
    }
    if (params.providers.voiceTts.enabled !== false &&
        params.providers.voiceTts.provider === "local") {
        ids.add(params.models.voiceLocalTtsModel);
    }
    return Array.from(ids);
}
async function createLocalSttEngine(params) {
    const { modelId, modelsDir, logger } = params;
    if (modelId === "parakeet-tdt-0.6b-v3-int8" || modelId === "parakeet-tdt-0.6b-v2-int8") {
        const modelDir = getLocalSpeechModelDir(modelsDir, modelId);
        return {
            kind: "offline",
            engine: new SherpaOfflineRecognizerEngine({
                model: {
                    kind: "nemo_transducer",
                    encoder: `${modelDir}/encoder.int8.onnx`,
                    decoder: `${modelDir}/decoder.int8.onnx`,
                    joiner: `${modelDir}/joiner.int8.onnx`,
                    tokens: `${modelDir}/tokens.txt`,
                },
                numThreads: 2,
                debug: 0,
            }, logger),
        };
    }
    if (modelId === "paraformer-bilingual-zh-en") {
        const modelDir = getLocalSpeechModelDir(modelsDir, modelId);
        return {
            kind: "online",
            engine: new SherpaOnlineRecognizerEngine({
                model: {
                    kind: "paraformer",
                    encoder: `${modelDir}/encoder.int8.onnx`,
                    decoder: `${modelDir}/decoder.int8.onnx`,
                    tokens: `${modelDir}/tokens.txt`,
                },
                numThreads: 1,
                debug: 0,
            }, logger),
        };
    }
    if (modelId === "zipformer-bilingual-zh-en-2023-02-20") {
        const modelDir = getLocalSpeechModelDir(modelsDir, modelId);
        return {
            kind: "online",
            engine: new SherpaOnlineRecognizerEngine({
                model: {
                    kind: "transducer",
                    encoder: `${modelDir}/encoder-epoch-99-avg-1.onnx`,
                    decoder: `${modelDir}/decoder-epoch-99-avg-1.onnx`,
                    joiner: `${modelDir}/joiner-epoch-99-avg-1.onnx`,
                    tokens: `${modelDir}/tokens.txt`,
                    modelType: "zipformer",
                },
                numThreads: 1,
                debug: 0,
            }, logger),
        };
    }
    throw new Error(`Unsupported local STT model '${modelId}'`);
}
export async function initializeLocalSpeechServices(params) {
    const { providers, logger, speechConfig } = params;
    const localConfig = speechConfig?.local ?? null;
    const localModels = resolveConfiguredLocalModels(speechConfig);
    let sttService = null;
    let ttsService = null;
    let dictationSttService = null;
    let turnDetectionService = null;
    let localVoiceTtsProvider = null;
    const requiredLocalModelIds = computeRequiredLocalModelIds({
        providers,
        models: localModels,
    });
    const localSttEngines = new Map();
    const getLocalSttEngine = async (modelId) => {
        const existing = localSttEngines.get(modelId);
        if (existing) {
            return existing;
        }
        if (!localConfig) {
            return null;
        }
        try {
            const created = await createLocalSttEngine({
                modelId,
                modelsDir: localConfig.modelsDir,
                logger,
            });
            localSttEngines.set(modelId, created);
            return created;
        }
        catch (err) {
            logger.warn({
                err,
                modelsDir: localConfig.modelsDir,
                modelId,
                hint: buildModelDownloadHint(modelId),
            }, "Local STT engine unavailable");
            return null;
        }
    };
    if (providers.voiceTurnDetection.enabled !== false &&
        providers.voiceTurnDetection.provider === "local") {
        let vadModelPath;
        if (localConfig) {
            try {
                vadModelPath = await ensureSileroVadModel(localConfig.modelsDir, logger);
            }
            catch (err) {
                logger.warn({ err }, "Failed to provision Silero VAD model, falling back to bundled");
            }
        }
        turnDetectionService = new SherpaSileroTurnDetectionProvider({ modelPath: vadModelPath }, logger);
    }
    if (providers.voiceStt.enabled !== false && providers.voiceStt.provider === "local") {
        if (!localConfig) {
            logger.warn({ configured: false }, "Local STT selected for voice but local provider config is missing; STT will be unavailable");
        }
        else {
            const voiceEngine = await getLocalSttEngine(localModels.voiceLocalSttModel);
            if (voiceEngine?.kind === "offline") {
                sttService = new SherpaOnnxParakeetSTT({ engine: voiceEngine.engine }, logger);
            }
            else if (voiceEngine?.kind === "online") {
                sttService = new SherpaOnnxSTT({ engine: voiceEngine.engine }, logger);
            }
        }
    }
    if (providers.dictationStt.enabled !== false && providers.dictationStt.provider === "local") {
        if (!localConfig) {
            logger.warn({ configured: false }, "Local STT selected for dictation but local provider config is missing; dictation STT will be unavailable");
        }
        else {
            const dictationEngine = await getLocalSttEngine(localModels.dictationLocalSttModel);
            if (dictationEngine?.kind === "offline") {
                dictationSttService = {
                    id: "local",
                    createSession: () => new SherpaParakeetRealtimeTranscriptionSession({ engine: dictationEngine.engine }),
                };
            }
            else if (dictationEngine?.kind === "online") {
                dictationSttService = {
                    id: "local",
                    createSession: () => new SherpaRealtimeTranscriptionSession({ engine: dictationEngine.engine }),
                };
            }
        }
    }
    if (providers.voiceTts.enabled !== false && providers.voiceTts.provider === "local") {
        if (!localConfig) {
            logger.warn({ configured: false }, "Local TTS selected for voice but local provider config is missing; TTS will be unavailable");
        }
        else {
            try {
                if (localModels.voiceLocalTtsModel === "pocket-tts-onnx-int8") {
                    const modelDir = getLocalSpeechModelDir(localConfig.modelsDir, localModels.voiceLocalTtsModel);
                    localVoiceTtsProvider = await PocketTtsOnnxTTS.create({
                        modelDir,
                        precision: "int8",
                        targetChunkMs: 50,
                    }, logger);
                }
                else {
                    const modelDir = getLocalSpeechModelDir(localConfig.modelsDir, localModels.voiceLocalTtsModel);
                    localVoiceTtsProvider = new SherpaOnnxTTS({
                        preset: localModels.voiceLocalTtsModel,
                        modelDir,
                        speakerId: speechConfig?.local?.models.voiceTtsSpeakerId,
                        speed: speechConfig?.local?.models.voiceTtsSpeed,
                    }, logger);
                }
                ttsService = localVoiceTtsProvider;
            }
            catch (err) {
                logger.warn({
                    err,
                    modelsDir: localConfig.modelsDir,
                    modelId: localModels.voiceLocalTtsModel,
                    hint: buildModelDownloadHint(localModels.voiceLocalTtsModel),
                }, "Local TTS engine unavailable");
            }
        }
    }
    const cleanup = () => {
        const maybeFreeable = localVoiceTtsProvider;
        if (typeof maybeFreeable?.free === "function") {
            maybeFreeable.free();
        }
        for (const engine of localSttEngines.values()) {
            engine.engine.free();
        }
    };
    return {
        turnDetectionService,
        sttService,
        ttsService,
        dictationSttService,
        localVoiceTtsProvider,
        localModelConfig: localConfig
            ? {
                modelsDir: localConfig.modelsDir,
                defaultModelIds: requiredLocalModelIds,
            }
            : null,
        availability: getLocalSpeechAvailability(speechConfig),
        cleanup,
    };
}
//# sourceMappingURL=runtime.js.map