"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.useOpenAiRealtimeSession = void 0;
var _dataSources = require("@mirai/data-sources");
var _react = require("react");
var _helpers = require("../../../../helpers");
var _lobby = require("../../../services/lobby.service");
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
const createMcpState = () => ({
  pending: false,
  pendingCallIds: new Set(),
  pendingCount: 0,
  handledCallIds: new Set(),
  handledApprovalIds: new Set(),
  failedCallIds: new Set(),
  responseIds: new Set(),
  responsesDone: new Set(),
  responsesWithMessage: new Set(),
  resumeRequestedCallIds: new Set(),
  resumeRequestedAny: false,
  retry: {
    count: 0,
    timeoutId: null
  }
});
const createResponseState = () => ({
  activeId: null,
  resumeAfterActive: false
});
const createFunctionState = () => ({
  pendingCalls: new Map(),
  pendingFollowup: false,
  pendingFollowupReason: null,
  pendingFollowupResponse: null,
  handledOpenUrlCalls: new Set()
});
const useOpenAiRealtimeSession = () => {
  const audioElementRef = (0, _react.useRef)(null);
  const audioContextRef = (0, _react.useRef)(null);
  const peerConnectionRef = (0, _react.useRef)(null);
  const dataChannelRef = (0, _react.useRef)(null);
  const localStreamRef = (0, _react.useRef)(null);
  const localAudioTrackRef = (0, _react.useRef)(null);
  const microphoneEnabledRef = (0, _react.useRef)(false);
  const pendingIntroRef = (0, _react.useRef)(false);
  const introResponseIdRef = (0, _react.useRef)(null);
  const mcpStateRef = (0, _react.useRef)(createMcpState());
  const responseStateRef = (0, _react.useRef)(createResponseState());
  const functionStateRef = (0, _react.useRef)(createFunctionState());
  const pendingUserTranscriptRef = (0, _react.useRef)('');
  const inputSourceRef = (0, _react.useRef)(null);
  const outputSourceRef = (0, _react.useRef)(null);
  const inputFrequencyDataRef = (0, _react.useRef)(null);
  const outputFrequencyDataRef = (0, _react.useRef)(null);
  const audioAnalyzersRef = (0, _react.useRef)({
    inputAnalyser: null,
    outputAnalyser: null,
    getInputByteFrequencyData: () => null,
    getOutputByteFrequencyData: () => null
  });
  const cleanupRealtimeSession = (0, _react.useCallback)(function () {
    let shouldSignal = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
    const dataChannel = dataChannelRef.current;
    if (dataChannel) {
      if (shouldSignal && dataChannel.readyState === 'open') {
        dataChannel.send(JSON.stringify({
          type: 'session.close'
        }));
      }
      dataChannel.close();
      dataChannelRef.current = null;
    }
    const peerConnection = peerConnectionRef.current;
    if (peerConnection) {
      peerConnection.getSenders().forEach(sender => {
        if (sender.track) sender.track.stop();
      });
      peerConnection.close();
      peerConnectionRef.current = null;
    }
    const localStream = localStreamRef.current;
    if (localStream) {
      localStream.getTracks().forEach(track => track.stop());
      localStreamRef.current = null;
    }
    localAudioTrackRef.current = null;
    microphoneEnabledRef.current = false;
    pendingIntroRef.current = false;
    introResponseIdRef.current = null;
    if (mcpStateRef.current.retry.timeoutId) {
      clearTimeout(mcpStateRef.current.retry.timeoutId);
    }
    mcpStateRef.current = createMcpState();
    responseStateRef.current = createResponseState();
    functionStateRef.current = createFunctionState();
    if (audioElementRef.current) {
      audioElementRef.current.srcObject = null;
      audioElementRef.current = null;
    }
    if (inputSourceRef.current) {
      inputSourceRef.current.disconnect();
      inputSourceRef.current = null;
    }
    if (outputSourceRef.current) {
      outputSourceRef.current.disconnect();
      outputSourceRef.current = null;
    }
    audioAnalyzersRef.current.inputAnalyser = null;
    audioAnalyzersRef.current.outputAnalyser = null;
    audioAnalyzersRef.current.getInputByteFrequencyData = () => null;
    audioAnalyzersRef.current.getOutputByteFrequencyData = () => null;
    inputFrequencyDataRef.current = null;
    outputFrequencyDataRef.current = null;
    const audioContext = audioContextRef.current;
    if (audioContext) {
      audioContext.close();
      audioContextRef.current = null;
    }
    pendingUserTranscriptRef.current = '';
  }, []);
  const safeJsonParse = (0, _react.useCallback)(value => {
    if (typeof value !== 'string') return null;
    try {
      return JSON.parse(value);
    } catch (_unused) {
      return null;
    }
  }, []);
  const isAllowedOpenUrl = (0, _react.useCallback)(url => {
    if (typeof url !== 'string') return false;
    return url.trim().startsWith('https://reservation.mirai');
  }, []);
  const enableMicrophone = (0, _react.useCallback)(() => {
    const track = localAudioTrackRef.current;
    if (!track || microphoneEnabledRef.current) return;
    track.enabled = true;
    microphoneEnabledRef.current = true;
  }, []);
  const clearPendingMcp = (0, _react.useCallback)(() => {
    if (mcpStateRef.current.retry.timeoutId) {
      clearTimeout(mcpStateRef.current.retry.timeoutId);
    }
    mcpStateRef.current = createMcpState();
  }, []);
  const sendResponseCreate = (0, _react.useCallback)((reason, responseOverrides) => {
    if (responseStateRef.current.activeId) {
      functionStateRef.current.pendingFollowup = true;
      functionStateRef.current.pendingFollowupReason = reason || null;
      functionStateRef.current.pendingFollowupResponse = responseOverrides || null;
      return false;
    }
    const channel = dataChannelRef.current;
    if (channel && channel.readyState === 'open') {
      const responsePayload = responseOverrides ? {
        type: 'response.create',
        response: responseOverrides
      } : {
        type: 'response.create'
      };
      channel.send(JSON.stringify(responsePayload));
      return true;
    }
    return false;
  }, []);
  const resumeMcpIfNeeded = (0, _react.useCallback)(outputCallId => {
    if (outputCallId) {
      mcpStateRef.current.pendingCallIds.delete(outputCallId);
    }
    if (!mcpStateRef.current.pending || mcpStateRef.current.pendingCount > 0) return;
    if (outputCallId && mcpStateRef.current.resumeRequestedCallIds.has(outputCallId)) return;
    if (!outputCallId && mcpStateRef.current.resumeRequestedAny) return;
    if (!outputCallId && mcpStateRef.current.resumeRequestedCallIds.size > 0) return;
    const shouldResume = [...mcpStateRef.current.responseIds].some(responseId => mcpStateRef.current.responsesDone.has(responseId) && !mcpStateRef.current.responsesWithMessage.has(responseId));
    if (!shouldResume) return;
    if (sendResponseCreate('mcp_completed')) {
      if (outputCallId) mcpStateRef.current.resumeRequestedCallIds.add(outputCallId);
      if (!outputCallId) mcpStateRef.current.resumeRequestedAny = true;
    } else if (responseStateRef.current.activeId) {
      responseStateRef.current.resumeAfterActive = true;
    }
  }, [sendResponseCreate]);
  const trackMcpStart = (0, _react.useCallback)(callId => {
    mcpStateRef.current.pending = true;
    if (callId) {
      if (mcpStateRef.current.handledCallIds.has(callId)) return;
      if (mcpStateRef.current.pendingCallIds.has(callId)) return;
      mcpStateRef.current.pendingCallIds.add(callId);
      mcpStateRef.current.pendingCount += 1;
      return;
    }
    mcpStateRef.current.pendingCount += 1;
  }, []);
  const getFunctionCallKey = (0, _react.useCallback)((callId, responseId, name) => {
    if (callId) return callId;
    if (responseId && name) return "".concat(responseId, ":").concat(name);
    return null;
  }, []);
  const upsertFunctionCall = (0, _react.useCallback)(_ref => {
    let {
      key,
      name,
      responseId,
      callId,
      args
    } = _ref;
    if (!key) return;
    const existing = functionStateRef.current.pendingCalls.get(key) || {
      key
    };
    functionStateRef.current.pendingCalls.set(key, _objectSpread(_objectSpread({}, existing), {}, {
      name: name !== null && name !== void 0 ? name : existing.name,
      responseId: responseId !== null && responseId !== void 0 ? responseId : existing.responseId,
      callId: callId !== null && callId !== void 0 ? callId : existing.callId,
      args: args !== null && args !== void 0 ? args : existing.args
    }));
  }, []);
  const handleMcpCompletion = (0, _react.useCallback)(outputCallId => {
    mcpStateRef.current.pending = true;
    if (outputCallId) {
      if (mcpStateRef.current.handledCallIds.has(outputCallId)) return;
      mcpStateRef.current.handledCallIds.add(outputCallId);
    }
    if (mcpStateRef.current.pendingCount > 0) mcpStateRef.current.pendingCount -= 1;
    resumeMcpIfNeeded(outputCallId);
  }, [resumeMcpIfNeeded]);
  const scheduleResumeRetry = (0, _react.useCallback)(outputCallId => {
    if (!mcpStateRef.current.pending) return;
    if (mcpStateRef.current.retry.timeoutId) return;
    if (mcpStateRef.current.retry.count >= 2) {
      clearPendingMcp();
      return;
    }
    const attempt = mcpStateRef.current.retry.count + 1;
    const delay = attempt * 600;
    mcpStateRef.current.retry.count = attempt;
    mcpStateRef.current.retry.timeoutId = setTimeout(() => {
      mcpStateRef.current.retry.timeoutId = null;
      if (outputCallId) mcpStateRef.current.resumeRequestedCallIds.delete(outputCallId);
      mcpStateRef.current.resumeRequestedAny = false;
      resumeMcpIfNeeded(outputCallId);
    }, delay);
  }, [clearPendingMcp, resumeMcpIfNeeded]);
  const sendFunctionCallOutput = (0, _react.useCallback)((callId, output) => {
    const channel = dataChannelRef.current;
    if (!callId || !channel || channel.readyState !== 'open') return false;
    channel.send(JSON.stringify({
      type: 'conversation.item.create',
      item: {
        type: 'function_call_output',
        call_id: callId,
        output: JSON.stringify(output || {})
      }
    }));
    return true;
  }, []);
  const sendItemRetrieve = (0, _react.useCallback)(itemId => {
    const channel = dataChannelRef.current;
    if (!itemId || !channel || channel.readyState !== 'open') return false;
    channel.send(JSON.stringify({
      type: 'conversation.item.retrieve',
      item_id: itemId
    }));
    return true;
  }, []);
  const sendMcpApproval = (0, _react.useCallback)(function (approvalRequestId) {
    let approve = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
    const channel = dataChannelRef.current;
    if (!approvalRequestId || !channel || channel.readyState !== 'open') return false;
    channel.send(JSON.stringify({
      type: 'conversation.item.create',
      previous_item_id: approvalRequestId,
      item: {
        type: 'mcp_approval_response',
        approval_request_id: approvalRequestId,
        approve
      }
    }));
    return true;
  }, []);
  const requestFollowupResponse = (0, _react.useCallback)((reason, outputCallId, responseOverrides) => {
    functionStateRef.current.pendingFollowup = true;
    functionStateRef.current.pendingFollowupReason = reason || null;
    functionStateRef.current.pendingFollowupResponse = responseOverrides || null;
    if (sendResponseCreate(reason || 'followup', responseOverrides)) {
      mcpStateRef.current.resumeRequestedAny = true;
      if (outputCallId) mcpStateRef.current.resumeRequestedCallIds.add(outputCallId);
    }
  }, [sendResponseCreate]);
  const buildMcpFailureInstructions = (0, _react.useCallback)(localeHint => {
    const normalizedLocale = localeHint || 'en-US';
    return "Respond in the language that matches the locale code \"".concat(normalizedLocale, "\". The requested information could not be retrieved because a data source failed. Apologize briefly and ask if the user wants to try again or ask something else.");
  }, []);
  const handleMcpFailure = (0, _react.useCallback)((callId, localeHint) => {
    const failureKey = callId || 'unknown';
    if (mcpStateRef.current.failedCallIds.has(failureKey)) return;
    mcpStateRef.current.failedCallIds.add(failureKey);
    const instructions = buildMcpFailureInstructions(localeHint);
    requestFollowupResponse('mcp_failed', callId, {
      instructions
    });
  }, [buildMcpFailureInstructions, requestFollowupResponse]);
  const handleOpenUrlCalls = (0, _react.useCallback)(_ref2 => {
    let {
      responseId,
      outputItems
    } = _ref2;
    const responseOpenUrlItems = Array.isArray(outputItems) ? outputItems.filter(item => (item === null || item === void 0 ? void 0 : item.type) === 'function_call' && (item === null || item === void 0 ? void 0 : item.name) === 'open_url') : [];
    responseOpenUrlItems.forEach(item => {
      const key = getFunctionCallKey(item.call_id || item.id || null, responseId, item.name);
      upsertFunctionCall({
        key,
        name: item.name,
        responseId,
        callId: item.call_id || item.id || null,
        args: item.arguments || null
      });
    });
    const pendingCalls = [...functionStateRef.current.pendingCalls.values()].filter(call => {
      if (!(call !== null && call !== void 0 && call.name) || call.name !== 'open_url') return false;
      if (!responseId) return true;
      return !call.responseId || call.responseId === responseId;
    });
    for (const call of pendingCalls) {
      const callKey = call.key || call.callId || call.args || null;
      if (callKey && functionStateRef.current.handledOpenUrlCalls.has(callKey)) continue;
      const args = safeJsonParse(call.args);
      const url = args === null || args === void 0 ? void 0 : args.url;
      if (!isAllowedOpenUrl(url)) {
        if (call.callId) {
          sendFunctionCallOutput(call.callId, {
            error: 'blocked_url',
            allowed_prefix: 'https://reservation.mirai'
          });
          requestFollowupResponse('open_url_blocked', call.callId);
        }
        if (call.key) functionStateRef.current.pendingCalls.delete(call.key);
        continue;
      }
      if (callKey) functionStateRef.current.handledOpenUrlCalls.add(callKey);
      if (call.key) functionStateRef.current.pendingCalls.delete(call.key);
      cleanupRealtimeSession(true);
      window.location.assign(url.trim());
      return true;
    }
    return false;
  }, [cleanupRealtimeSession, getFunctionCallKey, isAllowedOpenUrl, requestFollowupResponse, safeJsonParse, sendFunctionCallOutput, upsertFunctionCall]);
  const startSession = (0, _react.useCallback)(async _ref3 => {
    let {
      assistant_name,
      chainId,
      currency,
      hotelId,
      isChain,
      locale,
      setIsConnecting,
      setReady
    } = _ref3;
    cleanupRealtimeSession();
    const fingerprint = localStorage.getItem('MIRAI:FINGERPRINT');
    const finalizeSession = error => {
      const errorMessage = typeof error === 'string' ? error : error === null || error === void 0 ? void 0 : error.message;
      if (errorMessage) {
        _dataSources.Event.publish(_helpers.EVENT.METRICS, {
          id: 'AURA:ERROR',
          error: errorMessage
        });
      }
      cleanupRealtimeSession(true);
      setReady(false);
      setIsConnecting(false);
    };
    try {
      const sessionResponse = await (0, _dataSources.request)({
        endpoint: "/voice/session?fingerprint=".concat(fingerprint, "&hotelId=").concat(isChain ? '' : hotelId !== null && hotelId !== void 0 ? hotelId : '', "&chainId=").concat(isChain ? chainId !== null && chainId !== void 0 ? chainId : '' : '', "&locale=").concat(locale !== null && locale !== void 0 ? locale : '', "&currency=").concat(currency !== null && currency !== void 0 ? currency : ''),
        hostname: process.env.SERVICE_SYNAPSE,
        method: 'GET'
      });
      const ephemeralKey = sessionResponse.value;
      const peerConnection = new RTCPeerConnection();
      peerConnectionRef.current = peerConnection;
      peerConnection.addEventListener('connectionstatechange', () => {
        const {
          connectionState
        } = peerConnection;
        if (connectionState === 'failed' || connectionState === 'disconnected' || connectionState === 'closed') {
          finalizeSession();
        }
      });
      audioElementRef.current = document.createElement('audio');
      audioElementRef.current.autoplay = true;
      peerConnection.ontrack = _ref4 => {
        let {
          streams
        } = _ref4;
        const [remoteStream] = streams;
        if (audioElementRef.current) {
          audioElementRef.current.srcObject = remoteStream || null;
        }
        if (!audioContextRef.current || !audioAnalyzersRef.current.outputAnalyser || !remoteStream) return;
        if (outputSourceRef.current) outputSourceRef.current.disconnect();
        outputSourceRef.current = audioContextRef.current.createMediaStreamSource(remoteStream);
        outputSourceRef.current.connect(audioAnalyzersRef.current.outputAnalyser);
      };
      const mediaStream = await navigator.mediaDevices.getUserMedia({
        audio: {
          echoCancellation: true,
          noiseSuppression: true,
          autoGainControl: true,
          channelCount: 1,
          sampleRate: 48000
        }
      });
      localStreamRef.current = mediaStream;
      const [track] = mediaStream.getAudioTracks();
      const AudioContextConstructor = globalThis.AudioContext || globalThis.webkitAudioContext;
      if (!AudioContextConstructor) throw new Error('AudioContext not supported');
      const audioContext = new AudioContextConstructor();
      audioContextRef.current = audioContext;
      if (audioContext.state === 'suspended') await audioContext.resume();
      const inputAnalyser = audioContext.createAnalyser();
      const outputAnalyser = audioContext.createAnalyser();
      inputAnalyser.fftSize = 1024;
      outputAnalyser.fftSize = 1024;
      audioAnalyzersRef.current.inputAnalyser = inputAnalyser;
      audioAnalyzersRef.current.outputAnalyser = outputAnalyser;
      inputFrequencyDataRef.current = new Uint8Array(inputAnalyser.frequencyBinCount);
      outputFrequencyDataRef.current = new Uint8Array(outputAnalyser.frequencyBinCount);
      audioAnalyzersRef.current.getInputByteFrequencyData = () => {
        if (!audioAnalyzersRef.current.inputAnalyser || !inputFrequencyDataRef.current) return null;
        audioAnalyzersRef.current.inputAnalyser.getByteFrequencyData(inputFrequencyDataRef.current);
        return inputFrequencyDataRef.current;
      };
      audioAnalyzersRef.current.getOutputByteFrequencyData = () => {
        if (!audioAnalyzersRef.current.outputAnalyser || !outputFrequencyDataRef.current) return null;
        audioAnalyzersRef.current.outputAnalyser.getByteFrequencyData(outputFrequencyDataRef.current);
        return outputFrequencyDataRef.current;
      };
      const inputSource = audioContext.createMediaStreamSource(mediaStream);
      inputSourceRef.current = inputSource;
      inputSource.connect(inputAnalyser);
      if (track) {
        localAudioTrackRef.current = track;
        track.enabled = false;
        await track.applyConstraints({
          echoCancellation: true,
          noiseSuppression: true,
          autoGainControl: true
        });
        peerConnection.addTrack(track, mediaStream);
      }
      const dataChannel = peerConnection.createDataChannel('oai-events');
      dataChannelRef.current = dataChannel;
      dataChannel.addEventListener('close', () => {
        finalizeSession();
      });
      dataChannel.addEventListener('error', event => {
        finalizeSession(event);
      });
      dataChannel.addEventListener('open', () => {
        pendingUserTranscriptRef.current = '';
        _dataSources.Event.publish(_helpers.EVENT.METRICS, {
          id: 'AURA:START'
        });
        const localeHint = locale || 'en-US';
        const introductionMessage = "Respond in the language that matches the locale code \"".concat(localeHint, "\". Introduce yourself using the following sentence, translated into that language: \"Hello, I am ").concat(assistant_name, ", your virtual assistant. I can help you with information about our services and facilities. What would you like to know today?\"");
        pendingIntroRef.current = true;
        introResponseIdRef.current = null;
        dataChannel.send(JSON.stringify({
          type: 'response.create',
          response: {
            instructions: introductionMessage
          }
        }));
      });
      dataChannel.addEventListener('message', async event => {
        var _payload$item, _payload$item2, _payload$item3, _payload$item4, _payload$item5, _payload$item6, _payload$item9, _payload$item0;
        const payload = JSON.parse(event.data);
        const {
          response
        } = payload;
        const itemId = ((_payload$item = payload.item) === null || _payload$item === void 0 ? void 0 : _payload$item.id) || payload.item_id || null;
        const callId = ((_payload$item2 = payload.item) === null || _payload$item2 === void 0 ? void 0 : _payload$item2.call_id) || payload.call_id || itemId;
        const responseId = payload.response_id || (response === null || response === void 0 ? void 0 : response.id) || null;
        if (payload.type === 'response.output_item.added' && ((_payload$item3 = payload.item) === null || _payload$item3 === void 0 ? void 0 : _payload$item3.type) === 'message') {
          if (pendingIntroRef.current && !introResponseIdRef.current && payload.response_id) {
            introResponseIdRef.current = payload.response_id;
          }
          if (responseId) {
            mcpStateRef.current.responsesWithMessage.add(responseId);
            mcpStateRef.current.responseIds.delete(responseId);
          }
          if (mcpStateRef.current.pending && mcpStateRef.current.pendingCount === 0) {
            clearPendingMcp();
          }
        }
        if (payload.type === 'output_audio_buffer.stopped') {
          if (pendingIntroRef.current && introResponseIdRef.current && payload.response_id === introResponseIdRef.current) {
            enableMicrophone();
            pendingIntroRef.current = false;
            introResponseIdRef.current = null;
          }
        }
        if (payload.type === 'response.output_item.added' && (((_payload$item4 = payload.item) === null || _payload$item4 === void 0 ? void 0 : _payload$item4.type) === 'mcp_call' || ((_payload$item5 = payload.item) === null || _payload$item5 === void 0 ? void 0 : _payload$item5.type) === 'mcp_tool_call')) {
          if (responseId) mcpStateRef.current.responseIds.add(responseId);
          trackMcpStart(callId);
        }
        if (payload.type === 'response.output_item.added' && ((_payload$item6 = payload.item) === null || _payload$item6 === void 0 ? void 0 : _payload$item6.type) === 'function_call') {
          var _payload$item7, _payload$item8;
          const name = ((_payload$item7 = payload.item) === null || _payload$item7 === void 0 ? void 0 : _payload$item7.name) || payload.name || null;
          const key = getFunctionCallKey(callId, responseId, name);
          upsertFunctionCall({
            key,
            name,
            responseId,
            callId,
            args: ((_payload$item8 = payload.item) === null || _payload$item8 === void 0 ? void 0 : _payload$item8.arguments) || null
          });
        }
        if (payload.type === 'response.function_call_arguments.done') {
          const name = payload.name || null;
          const key = getFunctionCallKey(payload.call_id || null, responseId, name);
          upsertFunctionCall({
            key,
            name,
            responseId,
            callId: payload.call_id || null,
            args: payload.arguments
          });
        }
        if (payload.type === 'response.mcp_call.in_progress') {
          trackMcpStart(callId);
          if (payload.item_id) sendItemRetrieve(payload.item_id);
        }
        if (payload.type === 'response.mcp_tool_call.in_progress') {
          trackMcpStart(callId);
          if (payload.item_id) sendItemRetrieve(payload.item_id);
        }
        if (payload.type === 'mcp_list_tools.in_progress' || payload.type === 'response.mcp_list_tools.in_progress') {
          if (payload.item_id) sendItemRetrieve(payload.item_id);
        }
        if (payload.type === 'response.output_item.done' && (((_payload$item9 = payload.item) === null || _payload$item9 === void 0 ? void 0 : _payload$item9.type) === 'mcp_call' || ((_payload$item0 = payload.item) === null || _payload$item0 === void 0 ? void 0 : _payload$item0.type) === 'mcp_tool_call')) {
          var _payload$item1, _payload$item10;
          if ((_payload$item1 = payload.item) !== null && _payload$item1 !== void 0 && _payload$item1.error || ((_payload$item10 = payload.item) === null || _payload$item10 === void 0 ? void 0 : _payload$item10.status) === 'failed') {
            handleMcpFailure(callId, locale);
          }
          handleMcpCompletion(callId);
        }
        if (payload.type === 'response.mcp_call.completed' || payload.type === 'response.mcp_call.failed' || payload.type === 'response.mcp_tool_call.completed' || payload.type === 'response.mcp_tool_call.failed') {
          if (payload.type === 'response.mcp_call.failed' || payload.type === 'response.mcp_tool_call.failed') {
            handleMcpFailure(callId, locale);
          }
          handleMcpCompletion(callId);
        }
        if (payload.type === 'conversation.item.added' || payload.type === 'conversation.item.retrieved') {
          var _payload$item11, _payload$item12, _payload$item13;
          if (((_payload$item11 = payload.item) === null || _payload$item11 === void 0 ? void 0 : _payload$item11.type) === 'mcp_call' || ((_payload$item12 = payload.item) === null || _payload$item12 === void 0 ? void 0 : _payload$item12.type) === 'mcp_tool_call') {
            trackMcpStart(callId);
          }
          if (((_payload$item13 = payload.item) === null || _payload$item13 === void 0 ? void 0 : _payload$item13.type) === 'mcp_approval_request') {
            var _payload$item14;
            const approvalId = ((_payload$item14 = payload.item) === null || _payload$item14 === void 0 ? void 0 : _payload$item14.id) || payload.item_id || null;
            if (approvalId && !mcpStateRef.current.handledApprovalIds.has(approvalId)) {
              mcpStateRef.current.handledApprovalIds.add(approvalId);
              sendMcpApproval(approvalId, true);
            }
          }
        }
        if (payload.type === 'conversation.item.done') {
          var _payload$item15, _payload$item16, _payload$item19;
          if (((_payload$item15 = payload.item) === null || _payload$item15 === void 0 ? void 0 : _payload$item15.type) === 'mcp_call' || ((_payload$item16 = payload.item) === null || _payload$item16 === void 0 ? void 0 : _payload$item16.type) === 'mcp_tool_call') {
            var _payload$item17, _payload$item18;
            if (((_payload$item17 = payload.item) === null || _payload$item17 === void 0 ? void 0 : _payload$item17.status) === 'failed' || (_payload$item18 = payload.item) !== null && _payload$item18 !== void 0 && _payload$item18.error) {
              handleMcpFailure(callId, locale);
            }
            handleMcpCompletion(callId);
          }
          if (((_payload$item19 = payload.item) === null || _payload$item19 === void 0 ? void 0 : _payload$item19.type) === 'mcp_approval_request') {
            var _payload$item20;
            const approvalId = ((_payload$item20 = payload.item) === null || _payload$item20 === void 0 ? void 0 : _payload$item20.id) || payload.item_id || null;
            if (approvalId && !mcpStateRef.current.handledApprovalIds.has(approvalId)) {
              mcpStateRef.current.handledApprovalIds.add(approvalId);
              sendMcpApproval(approvalId, true);
            }
          }
        }
        if (payload.type === 'response.done') {
          var _response$status_deta, _response$status_deta2, _response$status_deta3;
          if (responseId) mcpStateRef.current.responsesDone.add(responseId);
          const outputItems = Array.isArray(response === null || response === void 0 ? void 0 : response.output) ? response.output : [];
          const hasMessage = outputItems.some(item => (item === null || item === void 0 ? void 0 : item.type) === 'message');
          const status = response === null || response === void 0 ? void 0 : response.status;
          const reason = (response === null || response === void 0 ? void 0 : (_response$status_deta = response.status_details) === null || _response$status_deta === void 0 ? void 0 : _response$status_deta.reason) || (response === null || response === void 0 ? void 0 : (_response$status_deta2 = response.status_details) === null || _response$status_deta2 === void 0 ? void 0 : (_response$status_deta3 = _response$status_deta2.error) === null || _response$status_deta3 === void 0 ? void 0 : _response$status_deta3.code) || null;
          if (status === 'completed') {
            if (hasMessage && mcpStateRef.current.pending && mcpStateRef.current.pendingCount === 0 && mcpStateRef.current.responseIds.size === 0) {
              clearPendingMcp();
            }
          } else if (mcpStateRef.current.pending) {
            if (status === 'failed') {
              if (mcpStateRef.current.failedCallIds.size === 0) {
                scheduleResumeRetry(callId);
              }
            } else if (status === 'cancelled' && reason !== 'turn_detected') {
              if (mcpStateRef.current.failedCallIds.size === 0) {
                scheduleResumeRetry(callId);
              }
            }
          }
          resumeMcpIfNeeded(callId);
          handleOpenUrlCalls({
            responseId,
            outputItems
          });
        }
        if (payload.type === 'response.created') {
          if (responseId) responseStateRef.current.activeId = responseId;
        }
        if (payload.type === 'conversation.item.input_audio_transcription.completed') {
          var _payload$transcript;
          const transcript = (_payload$transcript = payload.transcript) === null || _payload$transcript === void 0 ? void 0 : _payload$transcript.trim();
          if (transcript) pendingUserTranscriptRef.current += transcript;
        }
        if (payload.type === 'response.done' && responseId && responseStateRef.current.activeId === responseId) {
          responseStateRef.current.activeId = null;
          if (responseStateRef.current.resumeAfterActive) {
            responseStateRef.current.resumeAfterActive = false;
            mcpStateRef.current.resumeRequestedAny = false;
            resumeMcpIfNeeded();
          }
          if (functionStateRef.current.pendingFollowup) {
            const reason = functionStateRef.current.pendingFollowupReason;
            const responseOverrides = functionStateRef.current.pendingFollowupResponse;
            functionStateRef.current.pendingFollowup = false;
            functionStateRef.current.pendingFollowupReason = null;
            functionStateRef.current.pendingFollowupResponse = null;
            sendResponseCreate(reason || 'followup', responseOverrides);
          }
        }
        if (payload.type === 'response.output_audio_transcript.done') {
          var _payload$transcript2;
          const transcript = (_payload$transcript2 = payload.transcript) === null || _payload$transcript2 === void 0 ? void 0 : _payload$transcript2.trim();
          if (!transcript || !pendingUserTranscriptRef.current) return;
          const input = pendingUserTranscriptRef.current.trim();
          pendingUserTranscriptRef.current = '';
          const activeLocale = locale || 'en-US';
          if (hotelId) {
            try {
              const chatId = await (0, _lobby.ensureChatId)({
                fingerprint,
                channelId: hotelId,
                session: {
                  locale: activeLocale
                }
              });
              if (chatId) {
                await (0, _lobby.createChatMessage)({
                  author: 'user',
                  body: input,
                  chatId,
                  meta: {
                    audio: true
                  }
                });
                await (0, _lobby.createChatMessage)({
                  author: 'ai',
                  body: transcript,
                  chatId,
                  meta: {
                    audio: true
                  }
                });
              }
            } catch (error) {
              _dataSources.Event.publish(_helpers.EVENT.METRICS, {
                id: 'AURA:ERROR',
                error: error === null || error === void 0 ? void 0 : error.message
              });
            }
          }
          _dataSources.Event.publish(_helpers.EVENT.METRICS, {
            id: 'SYNAPSE_RESPONSE',
            fingerprint,
            text: transcript,
            input,
            agent: 'aura',
            locale: activeLocale
          });
        }
      });
      const offer = await peerConnection.createOffer();
      await peerConnection.setLocalDescription(offer);
      const sdpResponse = await fetch('https://api.openai.com/v1/realtime/calls', {
        method: 'POST',
        body: offer.sdp,
        headers: {
          Authorization: "Bearer ".concat(ephemeralKey),
          'Content-Type': 'application/sdp'
        }
      });
      if (!sdpResponse.ok) throw new Error('Failed to establish OpenAI Realtime session');
      const answer = {
        type: 'answer',
        sdp: await sdpResponse.text()
      };
      await peerConnection.setRemoteDescription(answer);
      setReady(true);
    } catch (error) {
      _dataSources.Event.publish(_helpers.EVENT.METRICS, {
        id: 'AURA:ERROR',
        error: error === null || error === void 0 ? void 0 : error.message
      });
      setReady(false);
      throw error;
    } finally {
      setIsConnecting(false);
    }
  }, [cleanupRealtimeSession, clearPendingMcp, enableMicrophone, handleMcpCompletion, handleMcpFailure, handleOpenUrlCalls, resumeMcpIfNeeded, scheduleResumeRetry, sendItemRetrieve, sendMcpApproval, sendResponseCreate, trackMcpStart, upsertFunctionCall, getFunctionCallKey]);
  const endSession = (0, _react.useCallback)(() => {
    cleanupRealtimeSession(true);
  }, [cleanupRealtimeSession]);
  return {
    startSession,
    endSession,
    audioAnalyzers: audioAnalyzersRef.current
  };
};
exports.useOpenAiRealtimeSession = useOpenAiRealtimeSession;
//# sourceMappingURL=useOpenAiRealtimeSession.js.map