import LocalStorageEvent from "./localStorageEvent";

class LocalStorageEmitter {
  namespace = "wfc-";

  /**
   *
   * @type {Map<string, any>}
   */
  subscriptions;

  /**
   *
   * @type {Map<string, any>}
   */
  handles;

  /**
   *
   * @type {Map<string, any>}
   */
  invokes;

  constructor() {
    window.__lse = this;
    window.addEventListener("storage", (ev) => {
      let key = ev.key;
      if (!key) {
        return;
      }
      if (!key.startsWith(this.namespace + "$") || !key.endsWith("$")) {
        return;
      }
      let value;
      try {
        value = JSON.parse(ev.newValue);
      } catch (e) {
        value = null;
      }
      if (value === null) {
        console.log("localStorageEmitter ignore null", key);
        return;
      }

      if (key.endsWith("$r$")) {
        this.invokes.forEach((invoke, k) => {
          let invokeReturnValueKey = k + "r" + "$";
          if (invokeReturnValueKey === key) {
            invoke.callback(value.r);
            let b = this.invokes.delete(k);

            localStorage.removeItem(invokeReturnValueKey);
            localStorage.removeItem(k);
          }
        });
      } else {
        this.subscriptions.forEach((subscription, k) => {
          if (k === key) {
            subscription.listener(new LocalStorageEvent(this), value.args);
            if (subscription.once) {
              this.subscriptions.delete(k);
            }
            localStorage.removeItem(k);
          }
        });

        this.handles.forEach((handle, k) => {
          if (key.indexOf("$$") === -1 || key.indexOf("$r$") > -1) {
            return;
          }
          let index = key.indexOf("$$");
          let invokeKey = key.substr(0, index + 1);
          if (k === invokeKey) {
            let invokeId = value.invokeId;
            let returnValue = handle.listener(new LocalStorageEvent(this), value.args);
            let invokeReturnValueKey = k + "$" + invokeId + "$" + "r" + "$";
            if (returnValue instanceof Promise) {
              returnValue
                .then((v) => {
                  let r = { r: v };
                  localStorage.setItem(invokeReturnValueKey, JSON.stringify(r));
                })
                .catch((reason) => {
                  console.log("handle failed", reason);
                });
              if (handle.once) {
                this.handles.delete(k);
              }
            } else {
              let r = { r: returnValue };
              console.log("handle res", invokeId, returnValue);
              localStorage.setItem(invokeReturnValueKey, JSON.stringify(r));
              if (handle.once) {
                this.handles.delete(k);
              }
            }
            localStorage.removeItem(invokeKey);
          }
        });
      }
    });
    this.subscriptions = new Map();
    this.handles = new Map();
    this.invokes = new Map();
  }

  /**
   * 订阅通知
   * @param {string} channel
   * @param {function(LocalStorageEvent, args)} listener
   */
  on(channel, listener) {
    this.subscriptions.set(this.namespace + "$" + channel + "$", { once: false, listener: listener });
  }

  /**
   * 一次性订阅
   * @param {string} channel
   * @param {function(LocalStorageEvent, args)} listener
   */
  once(channel, listener) {
    this.subscriptions.set(this.namespace + "$" + channel + "$", { once: true, listener: listener });
  }

  /**
   * 发布通知
   * @param {string} channel
   * @param {any} args
   */
  send(channel, args) {
    let tmp = { args: args };
    localStorage.setItem(this.namespace + "$" + channel + "$", JSON.stringify(tmp));
  }

  //
  // sendSync(channel, args) {
  //     this.send(channel, args);
  // }

  /**
   * 处理远程调用
   * @param {string} channel
   * @param {function (any)} listener
   */
  handle(channel, listener) {
    this.handles.set(this.namespace + "$" + channel + "$", { once: false, listener: listener });
  }

  /**
   * 一次性处理远程调用
   * @param {string} channel
   * @param {function (any)} listener
   */
  handleOnce(channel, listener) {
    this.handles.set(this.namespace + "$" + channel + "$", { once: true, listener: listener });
  }

  /**
   * 执行远程调用，调用结果通过callback回调
   * @param {string} channel
   * @param {any} args
   * @param {function (any)} callback
   */
  invoke(channel, args, callback) {
    let invokeId = new Date().getTime() + "-" + Math.ceil(Math.random() * 10000);
    this.invokes.set(this.namespace + "$" + channel + "$$" + invokeId + "$", {
      invokeId: invokeId,
      callback: callback,
    });
    let tmp = {
      invokeId: invokeId,
      args: args,
    };
    localStorage.setItem(this.namespace + "$" + channel + "$$" + invokeId + "$", JSON.stringify(tmp));
  }

  /**
   * 执行远程调用，调用结果通过promise返回
   * @param channel
   * @param args
   * @return {Promise<any>}
   */
  promiseInvoke(channel, args) {
    return new Promise((resolve, reject) => {
      this.invoke(channel, args, (v) => {
        resolve(v);
      });
    });
  }

  _dump() {
    console.log("dump localStorageEmitter");
    this.subscriptions.forEach((value, key) => {
      console.log(key, value);
    });
    this.handles.forEach((value, key) => {
      console.log(key, value);
    });
    this.invokes.forEach((value, key) => {
      console.log(key, value);
    });
  }
}

let self = new LocalStorageEmitter();
export default self;
