const kHandler = Symbol("handler");
const kListeners = Symbol("listeners");
const kOrigin = Symbol("origin");
const kOther = Symbol("other");

class PostMessageEventEmitter {
  constructor(other, origin) {
    if (typeof other.postMessage !== "function") {
      throw new TypeError("Expected other.postMessage to be a function");
    }

    if (typeof origin !== "string") {
      throw new TypeError("Expected origin to be a string");
    }

    this[kListeners] = {};
    this[kOrigin] = origin;
    this[kOther] = other;

    this[kHandler] = (ev) => {
      // Check origin of message
      if (ev.origin !== this[kOrigin]) return;
      if (ev.source !== this[kOther]) return;

      // Check message data
      if (ev.data === null) return;
      if (typeof ev.data !== "object") return;
      if (typeof ev.data.name !== "string") return;

      // Emit event
      if (this[kListeners][ev.data.name]) {
        for (const fn of this[kListeners][ev.data.name]) {
          Promise.resolve().then(() => fn(ev.data.name, ev.data.data));
        }
      }
    };

    window.addEventListener("message", this[kHandler]);
  }

  on(name, fn) {
    if (typeof name !== "string") throw new TypeError("Expected name to be a string");
    if (typeof fn !== "function") throw new TypeError("Expected fn to be a function");

    if (this[kListeners][name]) {
      this[kListeners][name].push(fn);
    } else {
      this[kListeners][name] = [fn];
    }
  }

  emit(name, data) {
    if (typeof name !== "string") throw new TypeError("Expected name to be a string");

    this[kOther].postMessage({ name, data }, this[kOrigin]);
  }

  stop() {
    window.removeEventListener("message", this[kHandler]);
  }
}

module.exports = PostMessageEventEmitter;
