// Copyright 2015-present 650 Industries. All rights reserved.

// swiftlint:disable line_length

/**
 * This file contains the browser scripts that are injected into the WebView.
 * @generated by buildBrowserScripts.ts
 */

internal let INSTALL_GLOBALS_SCRIPT: String = """
// browserScripts/InstallGlobals/Deferred.ts
class Deferred {
  promise;
  resolveCallback;
  rejectCallback;
  constructor() {
    this.promise = new Promise((resolve, reject) => {
      this.resolveCallback = resolve;
      this.rejectCallback = reject;
    });
  }
  resolve(value) {
    this.resolveCallback(value);
  }
  reject(reason) {
    this.rejectCallback(reason);
  }
  getPromise() {
    return this.promise;
  }
}

// browserScripts/InstallGlobals/EventEmitterProxy.ts
class EventEmitterProxy {
  moduleName;
  listeners;
  constructor(moduleName) {
    this.moduleName = moduleName;
  }
  addListener = (eventName, listener) => {
    if (!this.listeners) {
      this.listeners = new Map;
    }
    if (!this.listeners.has(eventName)) {
      this.listeners.set(eventName, new Set);
    }
    this.listeners.get(eventName)?.add(listener);
    const nativeListenerId = window.ExpoDomWebView.nextEventListenerId++;
    listener.$$nativeListenerId = nativeListenerId;
    const source = `
      globalThis.expo.$$DomWebViewEventListenerMap ||= {};
      globalThis.expo.$$DomWebViewEventListenerMap['${eventName}'] ||= new Map();
      const listener = (...args) => {
        const serializeArgs = args.map((arg) => JSON.stringify(arg)).join(',');
        const script = 'window.ExpoDomWebView.eventEmitterProxy.${this.moduleName}.emit("${eventName}", ' + serializeArgs + ')';
        globalThis.expo.modules.ExpoDomWebViewModule.evalJsForWebViewAsync("%%WEBVIEW_ID%%", script);
      };
      globalThis.expo.$$DomWebViewEventListenerMap['${eventName}'].set(${nativeListenerId}, listener);
      globalThis.expo.modules.${this.moduleName}.addListener('${eventName}', listener);
    `;
    window.ExpoDomWebView.eval(source);
    return {
      remove: () => {
        this.removeListener(eventName, listener);
      }
    };
  };
  removeListener = (eventName, listener) => {
    const nativeListenerId = listener.$$nativeListenerId;
    if (nativeListenerId != null) {
      const source = `(function() {
        const nativeListener = globalThis.expo.$$DomWebViewEventListenerMap['${eventName}'].get(${nativeListenerId});
        if (nativeListener != null) {
          globalThis.expo.modules.${this.moduleName}.removeListener('${eventName}', nativeListener);
          globalThis.expo.$$DomWebViewEventListenerMap['${eventName}'].delete(${nativeListenerId});
        }
      })();
      true;
      `;
      window.ExpoDomWebView.eval(source);
    }
    this.listeners?.get(eventName)?.delete(listener);
  };
  removeAllListeners = (eventName) => {
    const source = `
      globalThis.expo.$$DomWebViewEventListenerMap['${eventName}'].clear();
      globalThis.expo.modules.${this.moduleName}.removeAllListeners('${eventName}');
    `;
    window.ExpoDomWebView.eval(source);
    this.listeners?.get(eventName)?.clear();
  };
  emit = (eventName, ...args) => {
    const listeners = new Set(this.listeners?.get(eventName));
    listeners.forEach((listener) => {
      try {
        listener(...args);
      } catch (error) {
        console.error(error);
      }
    });
  };
}

// browserScripts/InstallGlobals/utils.ts
function serializeArgs(args) {
  return args.map((arg) => {
    if (typeof arg === "object" && arg.sharedObjectId != null) {
      return `globalThis.expo.sharedObjectRegistry.get(${arg.sharedObjectId})`;
    }
    return JSON.stringify(arg);
  }).join(",");
}

// browserScripts/InstallGlobals/proxies.ts
function createSharedObjectProxy(sharedObjectId) {
  return new Proxy({}, {
    get: (target, prop) => {
      const name = String(prop);
      if (name === "sharedObjectId") {
        return sharedObjectId;
      }
      return function(...args) {
        const serializedArgs = serializeArgs(args);
        const source = `globalThis.expo.sharedObjectRegistry.get(${sharedObjectId})?.${name}?.call(globalThis.expo.sharedObjectRegistry.get(${sharedObjectId}),${serializedArgs})`;
        return window.ExpoDomWebView.eval(source);
      };
    }
  });
}
function createConstructorProxy(moduleName, property, propertyName) {
  return new Proxy(function() {
  }, {
    construct(target, args) {
      const serializedArgs = serializeArgs(args);
      const sharedObjectId = window.ExpoDomWebView.nextSharedObjectId++;
      const sharedObjectProxy = createSharedObjectProxy(sharedObjectId);
      window.ExpoDomWebView.sharedObjectFinalizationRegistry.register(sharedObjectProxy, sharedObjectId);
      const source = `globalThis.expo.sharedObjectRegistry ||= new Map(); globalThis.expo.sharedObjectRegistry.set(${sharedObjectId}, new ${property}(${serializedArgs}));`;
      window.ExpoDomWebView.eval(source);
      return sharedObjectProxy;
    }
  });
}
function createPropertyProxy(propertyTypeCache, moduleName, propertyName) {
  const property = `globalThis.expo.modules.${moduleName}.${propertyName}`;
  let propertyType = propertyTypeCache[propertyName];
  if (!propertyType) {
    const typeCheck = `${property}?.prototype?.__expo_shared_object_id__ != null ? 'sharedObject' : typeof ${property}`;
    propertyType = window.ExpoDomWebView.eval(typeCheck);
    propertyTypeCache[propertyName] = propertyType;
  }
  if (propertyType === "sharedObject") {
    return createConstructorProxy(moduleName, property, propertyName);
  }
  if (propertyType === "function") {
    return function(...args) {
      const serializedArgs = serializeArgs(args);
      const source = `${property}(${serializedArgs})`;
      return window.ExpoDomWebView.eval(source);
    };
  }
  return window.ExpoDomWebView.eval(property);
}
function createExpoModuleProxy(moduleName) {
  const propertyTypeCache = {};
  return new Proxy({}, {
    get: (target, prop) => {
      const name = String(prop);
      if (["addListener", "removeListener", "removeAllListeners"].includes(name)) {
        return window.ExpoDomWebView.eventEmitterProxy[moduleName][name];
      }
      return createPropertyProxy(propertyTypeCache, moduleName, name);
    }
  });
}

// browserScripts/InstallGlobals/ExpoDomWebView.ts
class ExpoDomWebView {
  nextDeferredId;
  nextSharedObjectId;
  nextEventListenerId;
  deferredMap;
  sharedObjectFinalizationRegistry;
  expoModulesProxy;
  eventEmitterProxy;
  constructor() {
    this.nextDeferredId = 0;
    this.nextSharedObjectId = 0;
    this.nextEventListenerId = 0;
    this.deferredMap = new Map;
    this.sharedObjectFinalizationRegistry = new FinalizationRegistry((sharedObjectId) => {
      this.eval(`globalThis.expo.sharedObjectRegistry.delete(${sharedObjectId})`);
    });
    const expoModules = {};
    const eventEmitterProxy = {};
    this.eval("Object.keys(globalThis.expo.modules)").forEach((name) => {
      expoModules[name] = createExpoModuleProxy(name);
      eventEmitterProxy[name] = new EventEmitterProxy(name);
    });
    this.expoModulesProxy = expoModules;
    this.eventEmitterProxy = eventEmitterProxy;
  }
  eval(source) {
    const { deferredId, deferred } = this.createDeferred();
    const args = JSON.stringify({ source, deferredId });
    const result = JSON.parse(window.ExpoDomWebViewBridge.eval(args));
    if (result.isPromise) {
      return deferred.getPromise();
    }
    this.removeDeferred(deferredId);
    return result.value;
  }
  createDeferred() {
    const deferred = new Deferred;
    const deferredId = this.nextDeferredId;
    this.deferredMap.set(deferredId, deferred);
    this.nextDeferredId += 1;
    return { deferredId, deferred };
  }
  resolveDeferred(deferredId, value) {
    const deferred = this.deferredMap.get(deferredId);
    if (deferred) {
      deferred.resolve(value);
      this.deferredMap.delete(deferredId);
    }
  }
  rejectDeferred(deferredId, reason) {
    const deferred = this.deferredMap.get(deferredId);
    if (deferred) {
      deferred.reject(reason);
      this.deferredMap.delete(deferredId);
    }
  }
  removeDeferred(deferredId) {
    this.deferredMap.delete(deferredId);
  }
}

// browserScripts/InstallGlobals/index.ts
window.ExpoDomWebView = new ExpoDomWebView;

"""

internal let NATIVE_EVAL_WRAPPER_SCRIPT: String = """
// browserScripts/NativeEvalWrapper/index.ts
(function() {
  const result = "%%SOURCE%%";
  if (result instanceof Promise) {
    result.then((resolved) => {
      const resolvedString = JSON.stringify(resolved);
      const script = 'window.ExpoDomWebView.resolveDeferred("%%DEFERRED_ID%%", ' + resolvedString + ")";
      globalThis.expo.modules.ExpoDomWebViewModule.evalJsForWebViewAsync("%%WEBVIEW_ID%%", script);
    }).catch((error) => {
      const errorString = JSON.stringify(error);
      const script = 'window.ExpoDomWebView.rejectDeferred("%%DEFERRED_ID%%", ' + errorString + ")";
      globalThis.expo.modules.ExpoDomWebViewModule.evalJsForWebViewAsync("%%WEBVIEW_ID%%", script);
    });
    return JSON.stringify({ isPromise: true, value: null });
  } else {
    return JSON.stringify({ isPromise: false, value: result });
  }
})();

"""

// swiftlint:enable line_length
