var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import { logger } from "adminforth";
import { getErrorMessage, isAbortError } from "../../errors.js";
import { sanitizeSpeechText } from "../../sanitizeSpeechText.js";
export class SpeechTurnService {
    constructor(runAndPersistAgentResponse) {
        this.runAndPersistAgentResponse = runAndPersistAgentResponse;
    }
    handle(input) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a, _b, _c, _d, _e, _f, _g, _h, _j;
            let transcription;
            try {
                transcription = yield input.audioAdapter.transcribe({
                    buffer: input.audio.buffer,
                    filename: input.audio.filename,
                    mimeType: input.audio.mimeType,
                    language: "auto",
                    abortSignal: input.abortSignal,
                });
            }
            catch (error) {
                if (((_a = input.abortSignal) === null || _a === void 0 ? void 0 : _a.aborted) || isAbortError(error)) {
                    logger.info("Agent speech transcription aborted by the client");
                    yield input.emit({ type: "finish" });
                    return null;
                }
                logger.error(`Agent speech transcription failed:\n${getErrorMessage(error)}`);
                yield input.emit({
                    type: "error",
                    error: "Speech transcription failed. Check server logs for details.",
                });
                yield input.emit({ type: "finish" });
                return null;
            }
            if ((_b = input.abortSignal) === null || _b === void 0 ? void 0 : _b.aborted) {
                yield input.emit({ type: "finish" });
                return null;
            }
            const prompt = transcription.text;
            if (!prompt) {
                yield input.emit({
                    type: "error",
                    error: "Speech transcription is empty",
                });
                yield input.emit({ type: "finish" });
                return null;
            }
            yield input.emit({
                type: "transcript",
                text: transcription.text,
                language: transcription.language,
            });
            const agentResponse = yield this.runAndPersistAgentResponse({
                prompt,
                sessionId: input.sessionId,
                modeName: input.modeName,
                userTimeZone: input.userTimeZone,
                currentPage: input.currentPage,
                chatSurface: input.chatSurface,
                adminPublicOrigin: input.adminPublicOrigin,
                abortSignal: input.abortSignal,
                adminUser: input.adminUser,
                emit: (event) => __awaiter(this, void 0, void 0, function* () {
                    if (event.type === "tool-call") {
                        yield input.emit(event);
                    }
                }),
                failureLogMessage: (_c = input.failureLogMessage) !== null && _c !== void 0 ? _c : "Agent speech response failed",
                abortLogMessage: (_d = input.abortLogMessage) !== null && _d !== void 0 ? _d : "Agent speech response aborted by the client",
            });
            if (agentResponse.aborted) {
                yield input.emit({ type: "finish" });
                return agentResponse;
            }
            if (agentResponse.failed) {
                yield input.emit({
                    type: "error",
                    error: agentResponse.text,
                });
                yield input.emit({ type: "finish" });
                return agentResponse;
            }
            try {
                yield input.emit({
                    type: "speech-response",
                    transcript: {
                        text: transcription.text,
                        language: transcription.language,
                    },
                    response: {
                        text: agentResponse.text,
                    },
                    sessionId: input.sessionId,
                    turnId: agentResponse.turnId,
                });
                const speech = yield input.audioAdapter.synthesize({
                    text: sanitizeSpeechText(agentResponse.text),
                    stream: true,
                    streamFormat: "audio",
                    format: "pcm",
                    abortSignal: input.abortSignal,
                });
                yield input.emit({
                    type: "audio-start",
                    mimeType: speech.mimeType,
                    format: speech.format,
                    sampleRate: 24000,
                    channelCount: 1,
                    bitsPerSample: 16,
                });
                const reader = speech.audioStream.getReader();
                const cancelAudioStream = () => {
                    void reader.cancel().catch(() => undefined);
                };
                try {
                    (_e = input.abortSignal) === null || _e === void 0 ? void 0 : _e.addEventListener("abort", cancelAudioStream, { once: true });
                    while (true) {
                        if ((_f = input.abortSignal) === null || _f === void 0 ? void 0 : _f.aborted) {
                            yield reader.cancel().catch(() => undefined);
                            break;
                        }
                        const { value, done } = yield reader.read();
                        if (done) {
                            break;
                        }
                        if ((_g = input.abortSignal) === null || _g === void 0 ? void 0 : _g.aborted) {
                            break;
                        }
                        yield input.emit({
                            type: "audio-delta",
                            value,
                        });
                    }
                }
                finally {
                    (_h = input.abortSignal) === null || _h === void 0 ? void 0 : _h.removeEventListener("abort", cancelAudioStream);
                    reader.releaseLock();
                }
                yield input.emit({ type: "audio-done" });
                yield input.emit({ type: "finish" });
                return agentResponse;
            }
            catch (error) {
                if (((_j = input.abortSignal) === null || _j === void 0 ? void 0 : _j.aborted) || isAbortError(error)) {
                    logger.info("Agent speech audio streaming aborted by the client");
                }
                else {
                    logger.error(`Agent speech audio streaming failed:\n${getErrorMessage(error)}`);
                    yield input.emit({
                        type: "error",
                        error: getErrorMessage(error),
                    });
                }
                yield input.emit({ type: "finish" });
                return agentResponse;
            }
        });
    }
}
