"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Autopilot = void 0;
/**
 * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
 * http://github.com/fonoster/fonoster
 *
 * This file is part of Fonoster
 *
 * Licensed under the MIT License (the "License");
 * you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *    https://opensource.org/licenses/MIT
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
const path_1 = __importDefault(require("path"));
const worker_threads_1 = require("worker_threads");
const logger_1 = require("@fonoster/logger");
const xstate_1 = require("xstate");
const machine_1 = require("./machine/machine");
const logger = (0, logger_1.getLogger)({ service: "autopilot", filePath: __filename });
class Autopilot {
    constructor(params) {
        this.params = params;
        const { voice, languageModel, conversationSettings } = this.params;
        const vadWorkerPath = path_1.default.resolve(__dirname, "../dist", "./vadWorker");
        this.vadWorker = new worker_threads_1.Worker(vadWorkerPath, {
            workerData: conversationSettings.vad
        });
        this.actor = (0, xstate_1.createActor)(machine_1.machine, {
            input: {
                conversationSettings,
                languageModel,
                voice
            }
        });
    }
    start() {
        this.actor.start();
        this.actor.subscribe((state) => {
            logger.verbose("actor's new state is", { state: state.value });
        });
        this.setupVoiceStream();
        this.setupSpeechGathering();
        this.vadWorker.on("error", (err) => {
            logger.error("vad worker error", err);
        });
        this.vadWorker.on("exit", (code) => {
            if (code !== 0) {
                logger.error("vad worker stopped with exit code", { code });
            }
        });
    }
    stop() {
        logger.verbose("stopping autopilot");
        this.actor.stop();
    }
    async setupVoiceStream() {
        const { voice } = this.params;
        const stream = await voice.stream();
        stream.onData(this.handleVoicePayload.bind(this));
        this.vadWorker.on("message", (event) => {
            logger.verbose("received speech event from vad", { event });
            if (event === "SPEECH_START") {
                this.actor.send({ type: "SPEECH_START" });
            }
            else if (event === "SPEECH_END") {
                this.actor.send({ type: "SPEECH_END" });
            }
        });
    }
    handleVoicePayload(chunk) {
        try {
            this.vadWorker.postMessage(chunk);
        }
        catch (err) {
            logger.error("an error occurred while processing vad", err);
        }
    }
    async setupSpeechGathering() {
        const { voice } = this.params;
        const stream = await voice.sgather();
        stream.onData((payload) => {
            const { speech, responseTime } = payload;
            logger.verbose("received speech result", { speech, responseTime });
            if (payload.speech) {
                this.actor.send({
                    type: "SPEECH_RESULT",
                    speech: payload.speech ?? "",
                    responseTime
                });
            }
        });
    }
}
exports.Autopilot = Autopilot;
