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 { Filters, logger } from "adminforth";
import { getErrorMessage, isAbortError } from "./errors.js";
import { sanitizeSpeechText } from "./sanitizeSpeechText.js";
export class ChatSurfaceService {
    constructor(getAdminforth, options, sessionStore, handleTurn, runAndPersistAgentResponse) {
        this.getAdminforth = getAdminforth;
        this.options = options;
        this.sessionStore = sessionStore;
        this.handleTurn = handleTurn;
        this.runAndPersistAgentResponse = runAndPersistAgentResponse;
    }
    createEventEmitter(sink) {
        return (event) => __awaiter(this, void 0, void 0, function* () {
            if (event.type === "text-delta") {
                yield sink.emit({
                    type: "text_delta",
                    delta: event.delta,
                });
                return;
            }
            if (event.type === "response") {
                yield sink.emit({
                    type: "done",
                    text: event.text,
                });
                return;
            }
            if (event.type === "error") {
                yield sink.emit({
                    type: "error",
                    message: event.error,
                });
            }
        });
    }
    handleLink(incoming, sink) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a;
            if (((_a = incoming.metadata) === null || _a === void 0 ? void 0 : _a.isStartCommand) !== true) {
                return false;
            }
            yield sink.emit({
                type: "done",
                text: `Open AdminForth and connect your ${incoming.surface} account from Connected Accounts settings.`,
            });
            return true;
        });
    }
    handleAudioMessage(incoming, sink, adminUser) {
        return __awaiter(this, void 0, void 0, function* () {
            const audioAdapter = this.options.audioAdapter;
            if (!audioAdapter) {
                yield sink.emit({
                    type: "error",
                    message: "Audio adapter is not configured for AdminForth Agent.",
                });
                return;
            }
            let transcription;
            try {
                transcription = yield audioAdapter.transcribe({
                    buffer: incoming.audio.buffer,
                    filename: incoming.audio.filename,
                    mimeType: incoming.audio.mimeType,
                    language: "auto",
                });
            }
            catch (error) {
                if (isAbortError(error)) {
                    logger.info(`Agent ${incoming.surface} surface speech transcription aborted`);
                    return;
                }
                logger.error(`Agent ${incoming.surface} surface speech transcription failed:\n${getErrorMessage(error)}`);
                yield sink.emit({
                    type: "error",
                    message: "Speech transcription failed. Check server logs for details.",
                });
                return;
            }
            if (!transcription.text) {
                yield sink.emit({
                    type: "error",
                    message: "Speech transcription is empty",
                });
                return;
            }
            const agentResponse = yield this.handleAgentSurfaceResponse(incoming, sink, adminUser, transcription.text, { emitDone: false });
            if (!agentResponse || agentResponse.aborted || agentResponse.failed) {
                return;
            }
            yield sink.emit({
                type: "done",
                text: agentResponse.text,
            });
            try {
                const speech = yield audioAdapter.synthesize({
                    text: sanitizeSpeechText(agentResponse.text),
                    stream: false,
                    format: "opus",
                });
                yield sink.emit({
                    type: "audio",
                    audio: speech.audio,
                    filename: "agent-response.ogg",
                    mimeType: speech.mimeType,
                });
            }
            catch (error) {
                if (isAbortError(error)) {
                    logger.info(`Agent ${incoming.surface} surface speech synthesis aborted`);
                    return;
                }
                logger.error(`Agent ${incoming.surface} surface speech synthesis failed:\n${getErrorMessage(error)}`);
                yield sink.emit({
                    type: "error",
                    message: getErrorMessage(error),
                });
            }
        });
    }
    handleAgentSurfaceResponse(incoming, sink, adminUser, prompt, options) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a, _b, _c, _d;
            const emitDone = (_a = options === null || options === void 0 ? void 0 : options.emitDone) !== null && _a !== void 0 ? _a : true;
            const adminPublicOrigin = typeof ((_b = incoming.metadata) === null || _b === void 0 ? void 0 : _b.adminPublicOrigin) === "string"
                ? incoming.metadata.adminPublicOrigin
                : undefined;
            const sessionId = yield this.sessionStore.getOrCreateChatSurfaceSession(Object.assign(Object.assign({}, incoming), { prompt }), adminUser);
            if (emitDone) {
                yield this.handleTurn({
                    prompt,
                    sessionId,
                    modeName: incoming.modeName,
                    userTimeZone: (_c = incoming.userTimeZone) !== null && _c !== void 0 ? _c : "UTC",
                    chatSurface: incoming.surface,
                    adminPublicOrigin,
                    adminUser,
                    emit: this.createEventEmitter(sink),
                    failureLogMessage: `Agent ${incoming.surface} surface response failed`,
                    abortLogMessage: `Agent ${incoming.surface} surface response aborted`,
                });
                return null;
            }
            const agentResponse = yield this.runAndPersistAgentResponse({
                prompt,
                sessionId,
                modeName: incoming.modeName,
                userTimeZone: (_d = incoming.userTimeZone) !== null && _d !== void 0 ? _d : "UTC",
                chatSurface: incoming.surface,
                adminPublicOrigin,
                adminUser,
                emit: this.createEventEmitter(sink),
                failureLogMessage: `Agent ${incoming.surface} surface response failed`,
                abortLogMessage: `Agent ${incoming.surface} surface response aborted`,
            });
            if (agentResponse.failed) {
                yield sink.emit({
                    type: "error",
                    message: agentResponse.text,
                });
            }
            return agentResponse;
        });
    }
    getAdminUserRecordForChatSurface(adapter, incoming) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a, _b, _c, _d;
            const adminforth = this.getAdminforth();
            const authResourceId = adminforth.config.auth.usersResourceId;
            const externalIdentityResource = this.options.chatExternalIdentityResource;
            if (!externalIdentityResource) {
                return null;
            }
            const surfaceIdentityConfig = externalIdentityResource.surfaces[adapter.name];
            if (!surfaceIdentityConfig) {
                return null;
            }
            const providerField = (_a = externalIdentityResource.providerField) !== null && _a !== void 0 ? _a : 'provider';
            const subjectField = (_b = externalIdentityResource.subjectField) !== null && _b !== void 0 ? _b : 'subject';
            const adminUserIdField = (_c = externalIdentityResource.adminUserIdField) !== null && _c !== void 0 ? _c : 'adminUserId';
            const externalUserIdField = (_d = externalIdentityResource.externalUserIdField) !== null && _d !== void 0 ? _d : 'externalUserId';
            const identityFilters = [
                Filters.EQ(providerField, surfaceIdentityConfig.provider),
                Filters.EQ(externalUserIdField, incoming.externalUserId),
            ];
            const identities = yield adminforth.resource(externalIdentityResource.resourceId).list(identityFilters);
            const identity = identities.find((identity) => {
                if (String(identity[externalUserIdField]) === incoming.externalUserId) {
                    return true;
                }
                if (String(identity[subjectField]) === incoming.externalUserId) {
                    return true;
                }
                return false;
            });
            if (!identity) {
                return null;
            }
            const authResource = adminforth.config.resources.find((resource) => resource.resourceId === authResourceId);
            const primaryKeyField = authResource.columns.find((column) => column.primaryKey).name;
            return adminforth.resource(authResourceId).get([
                Filters.EQ(primaryKeyField, identity[adminUserIdField]),
            ]);
        });
    }
    handleMessage(adapter, incoming, sink) {
        return __awaiter(this, void 0, void 0, function* () {
            if (yield this.handleLink(incoming, sink)) {
                return;
            }
            const adminforth = this.getAdminforth();
            const authResourceId = adminforth.config.auth.usersResourceId;
            const authResource = adminforth.config.resources.find((resource) => resource.resourceId === authResourceId);
            const primaryKeyField = authResource.columns.find((column) => column.primaryKey).name;
            const adminUserRecord = yield this.getAdminUserRecordForChatSurface(adapter, incoming);
            if (!adminUserRecord) {
                yield sink.emit({
                    type: "error",
                    message: "This chat account is not authorized to use AdminForth Agent.",
                });
                return;
            }
            const adminUser = {
                pk: adminUserRecord[primaryKeyField],
                username: adminUserRecord[adminforth.config.auth.usernameField],
                dbUser: adminUserRecord,
            };
            const incomingWithAudio = incoming;
            if (incomingWithAudio.audio) {
                yield this.handleAudioMessage(incomingWithAudio, sink, adminUser);
                return;
            }
            yield this.handleAgentSurfaceResponse(incoming, sink, adminUser, incoming.prompt);
        });
    }
}
