// retoor <retoor@molodetz.nl>

const RECONNECT_DELAY_MS = 1500;
const WRONG_WORKER_RETRY_MS = 200;
const WRONG_WORKER_CODE = 4013;

export default class DeviiSocket {
  constructor(handlers, channel = "main") {
    this.handlers = handlers || {};
    this.ws = null;
    const base = (location.protocol === "https:" ? "wss://" : "ws://") + location.host + "/devii/ws";
    this.url = channel && channel !== "main" ? base + "?channel=" + encodeURIComponent(channel) : base;
    this._shouldRun = true;
    this._settled = false;
  }

  connect() {
    this._shouldRun = true;
    this._open();
  }

  _open() {
    this._settled = false;
    this.ws = new WebSocket(this.url);
    this.ws.addEventListener("open", () => this._emit("onOpen"));
    this.ws.addEventListener("close", (event) => {
      const transient = !this._settled && event.code === WRONG_WORKER_CODE;
      if (!transient) this._emit("onClose");
      if (this._shouldRun) {
        const delay = transient ? WRONG_WORKER_RETRY_MS : RECONNECT_DELAY_MS;
        window.setTimeout(() => this._open(), delay);
      }
    });
    this.ws.addEventListener("error", () => this._emit("onError"));
    this.ws.addEventListener("message", (event) => {
      let payload;
      try {
        payload = JSON.parse(event.data);
      } catch {
        return;
      }
      if (!this._settled) {
        this._settled = true;
        this._emit("onReady");
      }
      this._emit("onMessage", payload);
    });
  }

  send(message) {
    if (this.ws && this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify(message));
      return true;
    }
    return false;
  }

  close() {
    this._shouldRun = false;
    if (this.ws) this.ws.close();
  }

  _emit(name, payload) {
    const handler = this.handlers[name];
    if (typeof handler === "function") handler(payload);
  }
}
