// asr-worker-handler（Phase 1 引擎抽象与 Worker 化）。
// 纯处理器：接收协议信封 → 绑定 session → 分发到共享 OnnxWhisperAudioTranscriber
// （模型管理/可用性/dump）或 AsrSessionController（推流/暂停/指标/停止）。
// 不依赖全局 self / chrome，便于 Node 测试注入 fake 端点和真实类。
// 旧 session（sessionId 不匹配）的消息直接丢弃；事件携带 worker 侧单调 sequence。
import { InferenceScheduler } from "./inference-scheduler.js";
import { OnnxWhisperAudioTranscriber } from "./onnx-transcriber.js";
import { ProviderError } from "./providers.js";
import { AsrSessionController } from "./asr-controller.js";
import { WhisperAsrEngine } from "./whisper-asr-engine.js";
import { selectAsrEngineKind } from "./asr-router.js";
import { SHERPA_EN_STREAMING_20M, SHERPA_JA_REAZONSPEECH, WHISPER_BASE_WEBGPU_FP16, selectAsrCapability } from "./model-capabilities.js";
import { CapabilityAsrModelBundleController, CapabilityAsrModelController } from "./capability-asr-model-controller.js";
import { SherpaBrowserRuntime } from "./sherpa-browser-runtime.js";
import { SherpaStreamingEngine } from "./sherpa-streaming-engine.js";
import { SherpaSegmentedEngine } from "./sherpa-segmented-engine.js";
import { WhisperWebGpuEngine, hasWebGpu } from "./whisper-webgpu-engine.js";
import { AutoLanguageEngine } from "./auto-language-engine.js";
import { encodeWorkerEvent, encodeWorkerResponse, isWorkerRequestMessage } from "./worker-protocol.js";
export function createAsrWorkerHandler(deps) {
    const scheduler = new InferenceScheduler();
    const transcriber = deps.transcriber ?? new OnnxWhisperAudioTranscriber(undefined, undefined, (priority, task) => scheduler.run(priority, task));
    let capabilityModels = deps.capabilityModels;
    let autoCapabilities;
    let sherpaRuntime = deps.sherpaRuntime;
    const capabilities = () => (capabilityModels ??= new CapabilityAsrModelController());
    const automaticModels = () => (autoCapabilities ??= new CapabilityAsrModelBundleController([SHERPA_EN_STREAMING_20M, SHERPA_JA_REAZONSPEECH]));
    const sherpa = () => (sherpaRuntime ??= new SherpaBrowserRuntime());
    let sessionId;
    let lastRequestSequence = 0;
    let eventSequence = 0;
    const postEvent = (event) => {
        if (!sessionId)
            return;
        deps.post(encodeWorkerEvent(sessionId, ++eventSequence, event));
    };
    const controller = deps.controller ?? new AsrSessionController({
        createEngine: (kind) => createEngine(kind, transcriber, sherpa, (engineEvent) => {
            if (engineEvent.type === "diagnostic")
                postEvent({ type: "diagnostic", line: engineEvent.line });
        }),
        emit: (event) => postEvent(event),
    });
    async function handle(message) {
        if (!isWorkerRequestMessage(message))
            return;
        // 首条消息绑定 session；后续不匹配的 session 直接丢弃。
        if (sessionId === undefined)
            sessionId = message.sessionId;
        else if (message.sessionId !== sessionId)
            return;
        if (message.sequence <= lastRequestSequence)
            return;
        lastRequestSequence = message.sequence;
        try {
            const request = message.request;
            switch (request.type) {
                case "model.status":
                    reply(message, !routed(request) ? await transcriber.modelStatus() : selectAsrModelRoute(request) === "automatic-realtime" ? await automaticModels().status() : await capabilityStatus(capabilities(), capabilityLanguage(request), request.profile));
                    break;
                case "model.inspect":
                    reply(message, !routed(request) ? await transcriber.inspectModel() : selectAsrModelRoute(request) === "automatic-realtime" ? await automaticModels().inspect() : await capabilityInspect(capabilities(), capabilityLanguage(request), request.profile));
                    break;
                case "model.download":
                    reply(message, { ok: !routed(request) ? transcriber.beginModelDownload() : selectAsrModelRoute(request) === "automatic-realtime" ? automaticModels().beginDownload() : capabilities().beginDownload(capabilityFor(capabilityLanguage(request), request.profile)) });
                    break;
                case "model.cancel":
                    if (!routed(request))
                        await transcriber.cancelDownload();
                    else if (selectAsrModelRoute(request) === "automatic-realtime")
                        await automaticModels().cancel();
                    else
                        await capabilities().cancel(capabilityFor(capabilityLanguage(request), request.profile));
                    reply(message, { ok: true });
                    break;
                case "model.remove":
                    if (!routed(request))
                        await transcriber.removeModel();
                    else if (selectAsrModelRoute(request) === "automatic-realtime")
                        await automaticModels().clear();
                    else
                        await capabilities().remove(capabilityFor(capabilityLanguage(request), request.profile));
                    reply(message, { ok: true });
                    break;
                case "models.clear":
                    await automaticModels().clear();
                    await capabilities().remove(WHISPER_BASE_WEBGPU_FP16);
                    await transcriber.removeModel().catch(() => undefined);
                    reply(message, { ok: true });
                    break;
                case "availability":
                    reply(message, !routed(request) ? await transcriber.availability() : selectAsrModelRoute(request) === "automatic-realtime" ? await automaticModels().availability() : await capabilityAvailability(capabilities(), capabilityLanguage(request), request.profile));
                    break;
                case "legacy.model.remove":
                    await transcriber.removeModel();
                    reply(message, { ok: true });
                    break;
                case "start":
                    await controller.start(request.sourceLanguage, !request.profile ? "whisper" : selectAsrEngineKind({ sourceLanguage: request.sourceLanguage, profile: request.profile }));
                    reply(message, { ok: true });
                    break;
                case "push":
                    controller.pushAudio(request.frame);
                    reply(message, { ok: true });
                    break;
                case "pause":
                    controller.setPaused(request.paused);
                    reply(message, { ok: true });
                    break;
                case "idle-pause":
                    controller.idlePause();
                    reply(message, { ok: true });
                    break;
                case "resume-from-idle":
                    controller.resumeFromIdle();
                    reply(message, { ok: true });
                    break;
                case "reset":
                    controller.reset(request.reason ?? "overload");
                    reply(message, { ok: true });
                    break;
                case "flush":
                    controller.flush();
                    reply(message, { ok: true });
                    break;
                case "stop":
                    controller.stop();
                    reply(message, { ok: true });
                    break;
                case "metrics":
                    reply(message, controller.metrics());
                    break;
                default: throw new Error("asr_unknown_request");
            }
        }
        catch (error) {
            deps.post(encodeWorkerResponse(message.sessionId, message.sequence, { ok: false, error: errorCode(error) }));
        }
    }
    function reply(message, result) {
        deps.post(encodeWorkerResponse(message.sessionId, message.sequence, { ok: true, result }));
    }
    return {
        handle,
        dispose() {
            controller.stop();
            return Promise.resolve();
        },
    };
}
function capabilityFor(sourceLanguage, profile) {
    const capability = selectAsrCapability({ sourceLanguage, profile, webgpuAvailable: profile !== "precision" || hasWebGpu() });
    if (capability === "unsupported")
        throw new ProviderError("audio_unavailable");
    return capability;
}
async function capabilityStatus(controller, sourceLanguage, profile) {
    if (profile === "precision" && !hasWebGpu())
        return { ...(await controller.status(WHISPER_BASE_WEBGPU_FP16)), executionPath: "unsupported", canDownload: false, error: "webgpu_unavailable" };
    return controller.status(capabilityFor(sourceLanguage, profile));
}
async function capabilityInspect(controller, sourceLanguage, profile) {
    if (profile === "precision" && !hasWebGpu())
        return { ...(await controller.status(WHISPER_BASE_WEBGPU_FP16)), executionPath: "unsupported", canDownload: false, error: "webgpu_unavailable" };
    return controller.inspect(capabilityFor(sourceLanguage, profile));
}
async function capabilityAvailability(controller, sourceLanguage, profile) {
    if (profile === "precision" && !hasWebGpu())
        return "unavailable";
    return controller.availability(capabilityFor(sourceLanguage, profile));
}
function routed(request) {
    return request.sourceLanguage !== undefined && request.profile !== undefined;
}
/** Automatic realtime probes require both Sherpa models; precision uses Whisper alone. */
export function selectAsrModelRoute(request) {
    return request.sourceLanguage === "auto" && request.profile === "realtime" ? "automatic-realtime" : "capability";
}
function capabilityLanguage(request) {
    // The precision capability is multilingual, so its lookup needs only a
    // concrete representative language when the UI selection is automatic.
    return request.sourceLanguage === "auto" ? "en" : request.sourceLanguage;
}
function createEngine(kind, transcriber, runtime, emit) {
    if (kind === "sherpa-streaming")
        return new SherpaStreamingEngine(runtime().streamingFactory(SHERPA_EN_STREAMING_20M), undefined, emit);
    if (kind === "sherpa-segmented")
        return new SherpaSegmentedEngine(runtime().segmentedFactory(SHERPA_JA_REAZONSPEECH), undefined, emit);
    if (kind === "whisper-webgpu")
        return new WhisperWebGpuEngine();
    if (kind === "auto-language")
        return new AutoLanguageEngine(new SherpaStreamingEngine(runtime().streamingFactory(SHERPA_EN_STREAMING_20M), undefined, emit), new SherpaSegmentedEngine(runtime().segmentedFactory(SHERPA_JA_REAZONSPEECH), undefined, emit));
    return new WhisperAsrEngine(transcriber, emit);
}
function errorCode(error) {
    if (error instanceof ProviderError)
        return error.code;
    if (error instanceof Error)
        return error.message;
    return String(error);
}
