export class MockWebSocketHelper extends EventTarget {
  static #mocks = new Map();

  static async init(page) {
    this.#mocks = new Map();

    await page.exposeFunction("onMockWebSocketConstructor", (url) => {
      const webSocket = new MockWebSocketHelper(page, url);
      this.#mocks.set(url, webSocket);
    });
    await page.exposeFunction("onMockWebSocketSpyMessage", (url, data) => {
      if (!this.#mocks.has(url)) {
        throw new Error(`WebSocket with URL ${url} not found`);
      }
      this.#mocks.get(url).dispatchEvent(new MessageEvent("message", { data }));
    });
    await page.exposeFunction("onMockWebSocketSpyClose", (url, code, reason) => {
      if (!this.#mocks.has(url)) {
        throw new Error(`WebSocket with URL ${url} not found`);
      }
      this.#mocks.get(url).dispatchEvent(new CloseEvent("close", { code, reason }));
    });
    await page.addInitScript({ path: "playwright/scripts/MockWebSocket.js" });
  }

  static waitForURL(url) {
    return new Promise((resolve) => {
      const intervalID = setInterval(() => {
        for (const [wsURL, ws] of this.#mocks) {
          if (wsURL.includes(url)) {
            clearInterval(intervalID);
            return resolve(ws);
          }
        }
      }, 30);
    });
  }

  #page = null;
  #url;

  constructor(page, url, protocols) {
    super();
    this.#page = page;
    this.#url = url;
  }

  mockOpen(options) {
    return this.#page.evaluate(
      ({ url, options }) => {
        if (typeof WebSocket.getByURL !== "function") {
          throw new Error(
            "WebSocket.getByURL is not a function. Did you forget to call MockWebSocket.init(page)?",
          );
        }
        WebSocket.getByURL(url).mockOpen(options);
      },
      { url: this.#url, options },
    );
  }

  mockMessage(data) {
    return this.#page.evaluate(
      ({ url, data }) => {
        if (typeof WebSocket.getByURL !== "function") {
          throw new Error(
            "WebSocket.getByURL is not a function. Did you forget to call MockWebSocket.init(page)?",
          );
        }
        WebSocket.getByURL(url).mockMessage(data);
      },
      { url: this.#url, data },
    );
  }

  mockClose() {
    return this.#page.evaluate(
      ({ url }) => {
        if (typeof WebSocket.getByURL !== "function") {
          throw new Error(
            "WebSocket.getByURL is not a function. Did you forget to call MockWebSocket.init(page)?",
          );
        }
        WebSocket.getByURL(url).mockClose();
      },
      { url: this.#url },
    );
  }
}
