// Modern ES6+ version of jamClientProxy.js for React compatibility
// Ported from web/app/assets/javascripts/jamClientProxy.js

import { QWebChannel } from 'qwebchannel'
// import useJamServer from './hooks/useJamServer';

class Deferred {
  constructor(request_id) {
    this.request_id = request_id;
    this.promise = new Promise((resolve, reject) => {
      this.reject = reject;
      this.resolve = resolve;
    });
  }
}

class JamClientProxy {
  constructor(app, logger, context = window) {
    this.app = app;
    this.logger = logger;
    this.context = context;
    this.deferredQueue = [];
    this.jkfrontendchannel = null;
    this.enumAppCounter = 1000;
    this.request_id = 1;
    this.skipLogMethods = [];
    this.displayLogMethod = [];
    // const server = useJamServer();
    // this.JamServer = server;

    this.JKFrontendMethods = Object.freeze({
      UnknownJKAppMessage: this.enumAppCounter++,
      AbortRecording: this.enumAppCounter++,
      addUserBackingTracksToJamkazamAsset: this.enumAppCounter++,
      applySubscriptionPolicy: this.enumAppCounter++,
      BringVideoWindowToFront: this.enumAppCounter++,
      browseForUserBackingTracksCandidate: this.enumAppCounter++,
      cancelVideoUpload: this.enumAppCounter++,
      clientID: this.enumAppCounter++,
      ClientJoinedSession: this.enumAppCounter++,
      ClientLeftSession: this.enumAppCounter++,
      ClientUpdateStartDownload: this.enumAppCounter++,
      ClientUpdateStartUpdate: this.enumAppCounter++,
      ClientUpdateVersion: this.enumAppCounter++,
      ClosePreviewRecording: this.enumAppCounter++,
      CloseRecording: this.enumAppCounter++,
      deleteVideo: this.enumAppCounter++,
      DownloadFileStatus: this.enumAppCounter++,
      endPairing: this.enumAppCounter++,
      FTUEAudioResync: this.enumAppCounter++,
      FTUECancel: this.enumAppCounter++,
      FTUEChatFrameSize: this.enumAppCounter++,
      FTUEClearChannelAssignments: this.enumAppCounter++,
      FTUEClearChatInput: this.enumAppCounter++,
      FTUECreateUpdatePlayBackProfile: this.enumAppCounter++,
      FTUECurrentSelectedVideoDevice: this.enumAppCounter++,
      FTUEGetAllAudioConfigurations: this.enumAppCounter++,
      FTUEGetAllConfigurationList: this.enumAppCounter++,
      FTUEGetAudioConfigurationsMissingDevice: this.enumAppCounter++,
      FTUEGetAudioDevices: this.enumAppCounter++,
      FTUEGetAutoSelectVideoLayout: this.enumAppCounter++,
      FTUEGetAvailableEncodeVideoResolutions: this.enumAppCounter++,
      FTUEGetAvailableVideoLayoutGroupStyles: this.enumAppCounter++,
      FTUEGetBadConfigurationMissingDev: this.enumAppCounter++,
      FTUEGetCaptureResolution: this.enumAppCounter++,
      FTUEGetChannels: this.enumAppCounter++,
      FTUEGetChatInputs: this.enumAppCounter++,
      FTUEGetChatInputVolume: this.enumAppCounter++,
      FTUEGetChatLatency: this.enumAppCounter++,
      FTUEGetConfigurationDevice: this.enumAppCounter++,
      FTUEGetConfigurationDevs: this.enumAppCounter++,
      FTUEGetCurrentCaptureResolution: this.enumAppCounter++,
      FTUEGetCurrentVideoCaptureDeviceCapability: this.enumAppCounter++,
      FTUEGetDefaultAudioConfigurations: this.enumAppCounter++,
      FTUEGetDevices: this.enumAppCounter++,
      FTUEGetExpectedLatency: this.enumAppCounter++,
      FTUEGetFrameSize: this.enumAppCounter++,
      FTUEGetGoodAudioConfigurations: this.enumAppCounter++,
      FTUEGetGoodConfigurationList: this.enumAppCounter++,
      FTUEgetInputDeviceSampleRate: this.enumAppCounter++,
      FTUEGetInputLatency: this.enumAppCounter++,
      FTUEGetInputMusicDevice: this.enumAppCounter++,
      FTUEGetInputVolume: this.enumAppCounter++,
      FTUEGetIoPerfData: this.enumAppCounter++,
      FTUEGetMapUserCntPreferredVideoLayoutGroupStyle: this.enumAppCounter++,
      FTUEGetMusicInputs: this.enumAppCounter++,
      FTUEGetMusicOutputs: this.enumAppCounter++,
      FTUEGetMusicProfileName: this.enumAppCounter++,
      FTUEgetOutputDeviceSampleRate: this.enumAppCounter++,
      FTUEGetOutputLatency: this.enumAppCounter++,
      FTUEGetOutputMusicDevice: this.enumAppCounter++,
      FTUEGetOutputVolume: this.enumAppCounter++,
      FTUEGetPreferredChatSampleRate: this.enumAppCounter++,
      FTUEGetPreferredMixerSampleRate: this.enumAppCounter++,
      FTUEGetPreferredOutputSampleRate: this.enumAppCounter++,
      FTUEGetSendFrameRates: this.enumAppCounter++,
      FTUEGetStatus: this.enumAppCounter++,
      FTUEGetUserCountPreferredVideoLayoutGroupStyle: this.enumAppCounter++,
      FTUEGetVideoCaptureDeviceCapabilities: this.enumAppCounter++,
      FTUEGetVideoCaptureDeviceNames: this.enumAppCounter++,
      FTUEGetVideoShareEnable: this.enumAppCounter++,
      FTUEGetVolumeRanges: this.enumAppCounter++,
      FTUEHasControlPanel: this.enumAppCounter++,
      FTUEInit: this.enumAppCounter++,
      FTUEIsMusicDeviceWDM: this.enumAppCounter++,
      FTUELoadAudioConfiguration: this.enumAppCounter++,
      FTUEOpenControlPanel: this.enumAppCounter++,
      FTUEPageEnter: this.enumAppCounter++,
      FTUEPageLeave: this.enumAppCounter++,
      FTUERefreshDevices: this.enumAppCounter++,
      FTUERegisterLatencyCallback: this.enumAppCounter++,
      FTUERegisterVUCallbacks: this.enumAppCounter++,
      FTUESave: this.enumAppCounter++,
      FTUESelectVideoCaptureDevice: this.enumAppCounter++,
      FTUESetAutoSelectVideoLayout: this.enumAppCounter++,
      FTUESetCaptureResolution: this.enumAppCounter++,
      FTUESetChatInput: this.enumAppCounter++,
      FTUESetChatInputVolume: this.enumAppCounter++,
      FTUESetChatLatency: this.enumAppCounter++,
      FTUESetFrameSize: this.enumAppCounter++,
      FTUESetInputLatency: this.enumAppCounter++,
      FTUESetInputMusicDevice: this.enumAppCounter++,
      FTUESetInputVolume: this.enumAppCounter++,
      FTUESetLatencySamples: this.enumAppCounter++,
      FTUESetMusicDevice: this.enumAppCounter++,
      FTUESetMusicInput: this.enumAppCounter++,
      FTUESetMusicInput2: this.enumAppCounter++,
      FTUESetMusicOutput: this.enumAppCounter++,
      FTUESetMusicProfileName: this.enumAppCounter++,
      FTUESetOutputLatency: this.enumAppCounter++,
      FTUESetOutputMusicDevice: this.enumAppCounter++,
      FTUESetOutputVolume: this.enumAppCounter++,
      FTUESetPreferredChatSampleRate: this.enumAppCounter++,
      FTUESetPreferredMixerSampleRate: this.enumAppCounter++,
      FTUESetPreferredOutputSampleRate: this.enumAppCounter++,
      FTUESetPreferredVideoLayoutGroupStyle: this.enumAppCounter++,
      FTUESetSendFrameRates: this.enumAppCounter++,
      FTUESetStatus: this.enumAppCounter++,
      FTUESetUserCountPreferredVideoLayoutGroupStyle: this.enumAppCounter++,
      FTUESetVideoEncodeResolution: this.enumAppCounter++,
      FTUESetVideoShareEnable: this.enumAppCounter++,
      FTUEStartIoPerfTest: this.enumAppCounter++,
      FTUEStartLatency: this.enumAppCounter++,
      FTUEUnsetMusicInput2: this.enumAppCounter++,
      get48vAndLineInstState: this.enumAppCounter++,
      getABLoopState: this.enumAppCounter++,
      getAllClientsStateMap: this.enumAppCounter++,
      getAllClientsStateStr: this.enumAppCounter++,
      GetASIODevices: this.enumAppCounter++,
      GetAutoStart: this.enumAppCounter++,
      getAvailableMetronomeClickNames: this.enumAppCounter++,
      getBackingTrackList: this.enumAppCounter++,
      getClientParentChildRole: this.enumAppCounter++,
      getConnectionDetail: this.enumAppCounter++,
      GetCurrentVideoFrameRate: this.enumAppCounter++,
      GetCurrentVideoResolution: this.enumAppCounter++,
      GetDetailedOS: this.enumAppCounter++,
      GetFTUE: this.enumAppCounter++,
      GetJamTrackSettings: this.enumAppCounter++,
      GetJamTrackTimeline: this.enumAppCounter++,
      getJBAutoPair: this.enumAppCounter++,
      getJbNetworkState: this.enumAppCounter++,
      getJbPortBindState: this.enumAppCounter++,
      getJbPreampState: this.enumAppCounter++,
      getJbStaticPortFlag: this.enumAppCounter++,
      getJbStaticPortValue: this.enumAppCounter++,
      getJbTrackState: this.enumAppCounter++,
      getJbUsbState: this.enumAppCounter++,
      GetLastLatencyTestTimes: this.enumAppCounter++,
      getLatencyServerState: this.enumAppCounter++,
      getLocalClients: this.enumAppCounter++,
      GetLocalRecordingState: this.enumAppCounter++,
      getLogLevel: this.enumAppCounter++,
      getMetronomeCricketTestState: this.enumAppCounter++,
      GetMixerIDs: this.enumAppCounter++,
      GetMixerMode: this.enumAppCounter++,
      GetMixerVolume: this.enumAppCounter++,
      getMyChildren: this.enumAppCounter++,
      getMyNetworkState: this.enumAppCounter++,
      getMyParentState: this.enumAppCounter++,
      GetNetworkTestScore: this.enumAppCounter++,
      getOpenVideoSources: this.enumAppCounter++,
      getOperatingMode: this.enumAppCounter++,
      GetOS: this.enumAppCounter++,
      GetOSAsString: this.enumAppCounter++,
      getParentClientId: this.enumAppCounter++,
      getPeerState: this.enumAppCounter++,
      GetRecordingManagerState: this.enumAppCounter++,
      GetSampleRate: this.enumAppCounter++,
      GetScoreWorkTimingInterval: this.enumAppCounter++,
      getSessionSetCompressorState: this.enumAppCounter++,
      GetStaticPort: this.enumAppCounter++,
      GetUseStaticPort: this.enumAppCounter++,
      GetVideoNetworkTestScore: this.enumAppCounter++,
      hasBonjour: this.enumAppCounter++,
      hasVstAssignment: this.enumAppCounter++,
      hasVstHost: this.enumAppCounter++,
      hasWebrtc: this.enumAppCounter++,
      InitiateScoringSession: this.enumAppCounter++,
      InvalidateJamTrack: this.enumAppCounter++,
      IsAppInWritableVolume: this.enumAppCounter++,
      IsAudioStarted: this.enumAppCounter++,
      IsFrontendVisible: this.enumAppCounter++,
      isJamBlaster: this.enumAppCounter++,
      isLatencyTestBlocked: this.enumAppCounter++,
      IsMyNetworkWireless: this.enumAppCounter++,
      IsNativeClient: this.enumAppCounter++,
      IsSessionLiveBroadCastRunning: this.enumAppCounter++,
      isSessionTrackPaused: this.enumAppCounter++,
      isSessionTrackPlaying: this.enumAppCounter++,
      isSessVideoRecording: this.enumAppCounter++,
      isSessVideoShared: this.enumAppCounter++,
      isSessYouTubeVideoBroadcasting: this.enumAppCounter++,
      isVideoRecordingAllowed: this.enumAppCounter++,
      isVideoWindowOpen: this.enumAppCounter++,
      IsVstLoaded: this.enumAppCounter++,
      isWebCamOpen: this.enumAppCounter++,
      jamBlasterSerialNo: this.enumAppCounter++,
      JamTrackDownload: this.enumAppCounter++,
      JamTrackGetImage: this.enumAppCounter++,
      JamTrackGetTrackDetail: this.enumAppCounter++,
      JamTrackGetTracks: this.enumAppCounter++,
      JamTrackIsPlayable: this.enumAppCounter++,
      JamTrackIsPlaying: this.enumAppCounter++,
      JamTrackKeysRequest: this.enumAppCounter++,
      JamTrackLoadJmep: this.enumAppCounter++,
      JamTrackPlay: this.enumAppCounter++,
      JamTrackStopPlay: this.enumAppCounter++,
      JoinSession: this.enumAppCounter++,
      LastUsedProfileName: this.enumAppCounter++,
      LeaveSession: this.enumAppCounter++,
      LeaveSessionAndMinimize: this.enumAppCounter++,
      log: this.enumAppCounter++,
      NetworkTest: this.enumAppCounter++,
      NetworkTestResult: this.enumAppCounter++,
      OnDownloadAvailable: this.enumAppCounter++,
      OnLoggedIn: this.enumAppCounter++,
      OnLoggedInUserName: this.enumAppCounter++,
      OnLoggedOut: this.enumAppCounter++,
      OnTrySyncCommand: this.enumAppCounter++,
      openBackingTrackFile: this.enumAppCounter++,
      OpenBackingTracksDirectory: this.enumAppCounter++,
      OpenRecording: this.enumAppCounter++,
      OpenSystemBrowser: this.enumAppCounter++,
      openVideo: this.enumAppCounter++,
      P2PMessageReceived: this.enumAppCounter++,
      ParticipantJoined: this.enumAppCounter++,
      ParticipantLeft: this.enumAppCounter++,
      pauseVideoUpload: this.enumAppCounter++,
      PerformQuit: this.enumAppCounter++,
      PostTaskBarMessage: this.enumAppCounter++,
      PreviewRecording: this.enumAppCounter++,
      rebootJamBlaster: this.enumAppCounter++,
      RegisterGenericCallBack: this.enumAppCounter++,
      registerMasterClippingCallback: this.enumAppCounter++,
      RegisterMixerInterfaceModeChangeCallback: this.enumAppCounter++,
      RegisterMuteChangeCallBack: this.enumAppCounter++,
      RegisterQuitCallback: this.enumAppCounter++,
      RegisterRecordingCallbacks: this.enumAppCounter++,
      RegisterRecordingManagerCallbacks: this.enumAppCounter++,
      RegisterSessionJoinLeaveRequestCallBack: this.enumAppCounter++,
      RegisterVolChangeCallBack: this.enumAppCounter++,
      ReloadAudioSystem: this.enumAppCounter++,
      removeBackingTrackFiles: this.enumAppCounter++,
      ResetPageCounters: this.enumAppCounter++,
      RestartApplication: this.enumAppCounter++,
      restartParentClient: this.enumAppCounter++,
      resumeVideoUpload: this.enumAppCounter++,
      SaveSettings: this.enumAppCounter++,
      SaveToClipboard: this.enumAppCounter++,
      SendP2PMessage: this.enumAppCounter++,
      SessGetInsetPosition: this.enumAppCounter++,
      SessGetListOfPeersSharingVideo: this.enumAppCounter++,
      SessGetPeersVideoPosition: this.enumAppCounter++,
      SessGetVideoDisplayLayoutGroup: this.enumAppCounter++,
      SessGetVideoReceiveEnable: this.enumAppCounter++,
      SessGetVideoRecordings: this.enumAppCounter++,
      SessionAddPlayTrack: this.enumAppCounter++,
      SessionAddTrack: this.enumAppCounter++,
      SessionAudioResync: this.enumAppCounter++,
      SessionCloseBackingTrackFile: this.enumAppCounter++,
      SessionCloseMetronome: this.enumAppCounter++,
      SessionCurrentPlaybackMode: this.enumAppCounter++,
      SessionCurrrentJamTrackPlayPosMs: this.enumAppCounter++,
      SessionCurrrentPlayPosMs: this.enumAppCounter++,
      SessionFirstGet: this.enumAppCounter++,
      SessionFirstSet: this.enumAppCounter++,
      SessionGetAllControlState: this.enumAppCounter++,
      SessionGetControlState: this.enumAppCounter++,
      SessionGetDeviceLatency: this.enumAppCounter++,
      SessionGetIDs: this.enumAppCounter++,
      SessionGetJamTracksPlayDurationMs: this.enumAppCounter++,
      SessionGetMacHash: this.enumAppCounter++,
      SessionGetMasterLocalMix: this.enumAppCounter++,
      SessionGetTracksPlayDurationMs: this.enumAppCounter++,
      SessionJamTrackSeekMs: this.enumAppCounter++,
      SessionLiveBroadcastStart: this.enumAppCounter++,
      SessionLiveBroadcastStop: this.enumAppCounter++,
      SessionOpenBackingTrackFile: this.enumAppCounter++,
      SessionOpenMetronome: this.enumAppCounter++,
      SessionPageEnter: this.enumAppCounter++,
      SessionPageLeave: this.enumAppCounter++,
      SessionPausePlay: this.enumAppCounter++,
      SessionRegisterCallback: this.enumAppCounter++,
      SessionRemoveAllPlayTracks: this.enumAppCounter++,
      SessionRemovePlayTrack: this.enumAppCounter++,
      SessionRemoveTrack: this.enumAppCounter++,
      SessionRequestUserControlUpdate: this.enumAppCounter++,
      SessionSetAlertCallback: this.enumAppCounter++,
      SessionSetAlertPeriod: this.enumAppCounter++,
      SessionSetConnectionStatusRefreshRate: this.enumAppCounter++,
      SessionSetControlState: this.enumAppCounter++,
      SessionSetMasterLocalMix: this.enumAppCounter++,
      SessionSetMetronome: this.enumAppCounter++,
      SessionSetRecordingFilename: this.enumAppCounter++,
      SessionSetRecordingFolder: this.enumAppCounter++,
      SessionSetTrackVolumeData: this.enumAppCounter++,
      SessionSetUserData: this.enumAppCounter++,
      SessionSetUserName: this.enumAppCounter++,
      SessionShowMetronomeGui: this.enumAppCounter++,
      SessionStartPlay: this.enumAppCounter++,
      SessionStopPlay: this.enumAppCounter++,
      SessionTrackSeekMs: this.enumAppCounter++,
      SessionTracksSeek: this.enumAppCounter++,
      SessPlayVideoRecording: this.enumAppCounter++,
      SessRotatePeerVideoPositions: this.enumAppCounter++,
      SessSelectVideoDisplayLayoutGroup: this.enumAppCounter++,
      SessSetInsetPosition: this.enumAppCounter++,
      SessSetInsetSize: this.enumAppCounter++,
      SessSetPeersVideoPosition: this.enumAppCounter++,
      SessSetVideoReceiveEnable: this.enumAppCounter++,
      SessStartVideoRecordAndReturnFileName: this.enumAppCounter++,
      SessStartVideoSharing: this.enumAppCounter++,
      SessStartWebcamVideoRecordAndReturnFileName: this.enumAppCounter++,
      SessStartYouTubeVideoBroadcast: this.enumAppCounter++,
      SessStopVideoRecord: this.enumAppCounter++,
      SessStopVideoSharing: this.enumAppCounter++,
      SessStopYouTubeVideoBroadcast: this.enumAppCounter++,
      SessSubscribePeerVideo: this.enumAppCounter++,
      SessUnsubscribePeerVideo: this.enumAppCounter++,
      set48vAndLineInstState: this.enumAppCounter++,
      setABLoopState: this.enumAppCounter++,
      SetAutoStart: this.enumAppCounter++,
      setBackingTrackOpenCallback: this.enumAppCounter++,
      SetFakeRecordingImpl: this.enumAppCounter++,
      SetFTUE: this.enumAppCounter++,
      setJBAutoPair: this.enumAppCounter++,
      setJBName: this.enumAppCounter++,
      setJbNetworkState: this.enumAppCounter++,
      setJbPortBindState: this.enumAppCounter++,
      setJbPreampState: this.enumAppCounter++,
      setJbStaticPortFlag: this.enumAppCounter++,
      setJbStaticPortValue: this.enumAppCounter++,
      setJbTrackState: this.enumAppCounter++,
      setJbUsbState: this.enumAppCounter++,
      setJmepMetronomeVolume: this.enumAppCounter++,
      SetLastUsedProfileName: this.enumAppCounter++,
      SetLatencyTestBlocked: this.enumAppCounter++,
      setMetronomeCricketTestState: this.enumAppCounter++,
      setMetronomeOpenCallback: this.enumAppCounter++,
      SetMixerMode: this.enumAppCounter++,
      SetNetworkTestScore: this.enumAppCounter++,
      SetRecordingFilename: this.enumAppCounter++,
      SetRecordingFolder: this.enumAppCounter++,
      SetScoreWorkTimingInterval: this.enumAppCounter++,
      setSessionMixerCategoryPlayoutState: this.enumAppCounter++,
      setSessionSetCompressorState: this.enumAppCounter++,
      SetStaticPort: this.enumAppCounter++,
      SetUseStaticPort: this.enumAppCounter++,
      SetVideoNetworkTestScore: this.enumAppCounter++,
      SetVURefreshRate: this.enumAppCounter++,
      ShowSelectBackingTrackDialog: this.enumAppCounter++,
      ShowSelectVSTScanDialog: this.enumAppCounter++,
      ShutdownApplication: this.enumAppCounter++,
      StartLiveStreaming: this.enumAppCounter++,
      startPairing: this.enumAppCounter++,
      StartRecording: this.enumAppCounter++,
      StopAudio: this.enumAppCounter++,
      StopLiveStreaming: this.enumAppCounter++,
      StopNetworkTest: this.enumAppCounter++,
      StopRecording: this.enumAppCounter++,
      TestLatency: this.enumAppCounter++,
      TestNetworkPktBwRate: this.enumAppCounter++,
      testVideoRender: this.enumAppCounter++,
      TrackDeleteProfile: this.enumAppCounter++,
      TrackGetAssignment: this.enumAppCounter++,
      TrackGetChannels: this.enumAppCounter++,
      TrackGetChatEnable: this.enumAppCounter++,
      TrackGetChatUsesMusic: this.enumAppCounter++,
      TrackGetCount: this.enumAppCounter++,
      TrackGetDevices: this.enumAppCounter++,
      TrackGetInstrument: this.enumAppCounter++,
      TrackGetMusicDeviceID: this.enumAppCounter++,
      TrackGetMusicDeviceNames: this.enumAppCounter++,
      TrackHasControlPanel: this.enumAppCounter++,
      TrackIsMusicDeviceType: this.enumAppCounter++,
      TrackLoadAssignments: this.enumAppCounter++,
      TrackLoadAudioProfile: this.enumAppCounter++,
      TrackOpenControlPanel: this.enumAppCounter++,
      TrackRefreshDevices: this.enumAppCounter++,
      TrackSaveAssignments: this.enumAppCounter++,
      TrackSetAssignment: this.enumAppCounter++,
      TrackSetChatEnable: this.enumAppCounter++,
      TrackSetChatInput: this.enumAppCounter++,
      TrackSetChatUsesMusic: this.enumAppCounter++,
      TrackSetCount: this.enumAppCounter++,
      TrackSetInstrument: this.enumAppCounter++,
      TrackSetMusicDevice: this.enumAppCounter++,
      UpdateMixer: this.enumAppCounter++,
      UpdateSessionInfo: this.enumAppCounter++,
      uploadVideo: this.enumAppCounter++,
      UserAttention: this.enumAppCounter++,
      VideoDecision: this.enumAppCounter++,
      VSTAddSearchPath: this.enumAppCounter++,
      VSTClearAll: this.enumAppCounter++,
      VSTListSearchPaths: this.enumAppCounter++,
      VSTListTrackAssignments: this.enumAppCounter++,
      VSTListVsts: this.enumAppCounter++,
      VSTLoad: this.enumAppCounter++,
      VSTRemoveSearchPath: this.enumAppCounter++,
      VSTScan: this.enumAppCounter++,
      VSTSetTrackAssignment: this.enumAppCounter++,
      VSTShowHideGui: this.enumAppCounter++,
      VST_EnableMidiForTrack: this.enumAppCounter++,
      VST_GetMidiDeviceList: this.enumAppCounter++,
      VST_ScanForMidiDevices: this.enumAppCounter++,
      IsOBSAvailable: this.enumAppCounter++,
      StartMediaRecording: this.enumAppCounter++,
      LaunchBroadcastSettings: this.enumAppCounter++,
      SetRecordingOptionStartedByPeer: this.enumAppCounter++,
      GetRecordingOptionStartedByPeer: this.enumAppCounter++,
      SetAudioRecordingPreference: this.enumAppCounter++,
      GetAudioRecordingPreference: this.enumAppCounter++,
      PeerStopRecording: this.enumAppCounter++,
      FrontStopRecording: this.enumAppCounter++,
      GetCurrentRecordingId: this.enumAppCounter++,
    });
  }

  setupWebSocketConnection() {
    const socket = new WebSocket('ws://localhost:3060');
    socket.onopen = () => {
      console.log("QWebChannel socket opened");
      new QWebChannel(socket, channel => {
        this.jkfrontendchannel = channel.objects.jkfrontendchannel;

        if (this.jkfrontendchannel) {
          window.isWebChannelReady = true;
          let deferred;

          try {
            this.jkfrontendchannel.sendText.connect(message => {
              // Handle method not implemented - resp variable was undefined in original
              const resp = message; // Fix undefined resp variable
              if (typeof resp === 'object' && resp[0] === 'Method Not Implemented') {
                if (deferred) deferred.resolve(null);
              } else {
                let msg = JSON.parse(message);
                //console.log("[jamClientProxy] Message received via QWebChannel: ", msg);
                let req_id = msg.request_id;
                let response = msg.response;
                let evt_id = msg.event_id;
                let methodName = msg.method_name;

                deferred = this.deferredQueue.find(d => d.request_id === req_id);

                if (deferred) {
                  if (this.skipLogMethods.length > 0 && this.skipLogMethods.includes(methodName)) {
                    // Skip logging
                  } else if (this.displayLogMethod.includes(methodName)) {
                    //this.logger.log('[jamClientProxy] Message received via QWebChannel: ', msg);
                  }

                  deferred.resolve(response);
                  this.deferredQueue = this.deferredQueue.filter(d => d.request_id !== deferred.request_id);
                } else if (evt_id) {
                  this.handleEvent(evt_id, response);
                }
              }
            });
          } catch (e) {
            this.logger.log('[jamClientProxy] Error when receiving message via QWebChannel');
            if (deferred) {
              deferred.reject(e.message);
              deferred = null;
            }
            // Note: Bugsnag integration would need to be handled separately
            console.error('jamClientProxy error:', e);
          }
        }
      });

    }

    socket.onclose = () => { }; // Handle close if needed

    socket.onerror = error => { }; // Handle error if needed
  }

  handleEvent(evt_id, response) {
    // const method = Object.keys(response)[0]; // Available if needed for debugging

    switch (evt_id.toString()) {
      case '3006': // execute_script
        try {
          // eslint-disable-next-line no-eval
          //console.log(`[jamClientProxy] execute_script: ${response['execute_script']}`);
          eval(response['execute_script']);
        } catch (error) {
          this.logger.log(`[jamClientProxy] error: execute_script: ${response['execute_script']}`);
          this.logger.log(error);
        }
        break;

      case '3007': // message
        try {
          const msg = response['message'];
          const clientId = response['targetClientID'];
          if (this.context.JK && this.context.JK.JamServer) {
            this.context.JK.JamServer.sendP2PMessage(clientId, msg);
          }
        } catch (error) {
          this.logger.log(`[jamClientProxy] error: sendP2PMessage: ${response['message']}`);
          this.logger.log(error);
        }
        break;

      case '3010': // JKVideoSession
        //this.logger.log(`[jamClientProxy] 3010 JKVideoSession: ${response['JKVideoSession']['connect']}`);
        const vidConnect = response['JKVideoSession']['connect'];
        if (this.context.ExternalVideoActions) {
          this.context.ExternalVideoActions.setVideoEnabled(vidConnect);
        }
        if (this.context.JK) {
          this.context.JK.videoIsOngoing = vidConnect;
        }
        break;

      case '3011': // AudioFormatChangeEvent
        //this.logger.log(`[jamClientProxy] 3011 AudioFormatChangeEvent: ${response['AudioFormat']}`);
        const audioFormat = response['AudioFormat'];
        if (this.context.RecordingActions) {
          this.context.RecordingActions.audioRecordingFormatChanged(`.${audioFormat}`);
        }
        break;

      case '3012': // customUrl
        const customUrl = response['CustomUrl'];
        if (typeof localStorage !== 'undefined') {
          if (localStorage.getItem('customUrl')) {
            localStorage.removeItem('customUrl');
          }
          if (customUrl) {
            const httpUrl = window.location.protocol + '//' + customUrl;
            // Note: JK.CustomUrlHandler would need to be implemented separately
            const customUrlHandler =
              this.context.JK && this.context.JK.CustomUrlHandler
                ? new this.context.JK.CustomUrlHandler()
                : { isValidUrl: () => true }; // Fallback

            if (customUrlHandler.isValidUrl(httpUrl)) {
              localStorage.setItem('customUrl', httpUrl);
              if (this.context.JK && this.context.JK.currentUserId) {
                window.location.href = httpUrl;
              }
            } else {
              this.logger.log(`[jamClientProxy] invalid customUrl: ${httpUrl}`);
            }
          }
        }
        break;

      default:
        break;
    }
  }

  waitForOpenConnection() {
    return new Promise((resolve, reject) => {
      const maxNumberOfAttempts = 10;
      const intervalTime = 200; // ms

      let currentAttempt = 0;
      const interval = setInterval(() => {
        if (currentAttempt > maxNumberOfAttempts - 1) {
          clearInterval(interval);
          reject(new Error('Maximum number of attempts exceeded'));
        } else if (this.jkfrontendchannel) {
          clearInterval(interval);
          resolve();
        }
        currentAttempt++;
      }, intervalTime);
    });
  }

  async sendWhenReady(prop, args) {
    if (this.jkfrontendchannel) {
      return this.sendMessage(prop, args);
    } else {
      try {
        await this.waitForOpenConnection();
        return this.sendMessage(prop, args);
      } catch (err) {
        console.error(err);
      }
    }
  }

  sendMessage(prop, args) {

    let appMessage = {
      request_id: ++this.request_id,
      arguments: Array.from(args) || [],
      method: this.JKFrontendMethods[prop]
    };

    //console.log('_DEBUG_ appMessage', prop, appMessage);

    let deferred = new Deferred(appMessage.request_id);

    if (this.skipLogMethods.length > 0 && this.skipLogMethods.includes(prop)) {
      // Skip logging
    } else if (this.displayLogMethod.includes(prop)) {
      this.logger.log('[jamClientProxy] diverting to backend:', prop, appMessage);
    }

    if (this.jkfrontendchannel) {
      try {
        this.jkfrontendchannel.receiveText(JSON.stringify(appMessage));
        this.deferredQueue.push(deferred);
        return deferred.promise;
      } catch (e) {
        this.logger.error('[jamClientProxy] Native app not connected', e.message);
        deferred.reject('Native app not connected');
        return deferred.promise;
      }
    } else {
      this.logger.info('[jamClientProxy] jkfrontendchannel is not ready yet');
      deferred.reject('frontendchannel is not ready yet');
      return deferred.promise;
    }
  }

  setupAsyncProxy() {
    const handler = {
      get: (target, prop, receiver) => {
        return (...args) => {
          return this.sendWhenReady(prop, args);
        };
      }
    };

    const proxy = new Proxy(this, handler);
    this.logger.log('[jamClientProxy] Connected to WebChannel, ready to send/receive messages!');
    return proxy;
  }

  init() {
    this.setupWebSocketConnection();
    return this.setupAsyncProxy();
  }
}

export default JamClientProxy;
