import { v4 as uuid } from 'uuid';

class PostMessageApi {
  /**
   *
   * @param {string} channel
   * @param {number} timeout
   */
  constructor(channel, timeout = 2000) {
    this.channelRequest = ['ATSP', channel, 'REQUEST'].join('_');
    this.channelReply = ['ATSP', channel, 'REPLY'].join('_');

    this.timeout = timeout;
  }

  /**
   *
   * @param {MessageEvent} eventMessage
   * @param {Function} handler
   * @returns {boolean}
   */
  handlerMessage(eventMessage, handler) {
    if (eventMessage.source !== window) {
      return false;
    }

    const { channelRequest, channelReply } = this;

    if (!(eventMessage.data.channelRequest === channelRequest)) {
      return false;
    }

    const { messageId, payload } = eventMessage.data;

    handler(payload).then((response) => {
      const payloadReply = {
        channelReply,
        messageId,
        response,
      };

      window.postMessage(payloadReply, '*');
    });

    return true;
  }

  /**
   *
   * @param {Function} callback
   */
  startHandler(callback) {
    if (!callback) {
      throw 'HANDLER_REQURIED';
    }

    window.addEventListener('message', (eventMessage) => {
      return this.handlerMessage(eventMessage, callback);
    });
  }

  /**
   *
   * @param {object} payload
   * @returns
   */
  sendMessage(payload) {
    const messageId = uuid();

    const { channelRequest, channelReply, timeout } = this;

    const payloadSend = {
      channelRequest,
      messageId,
      payload,
    };

    return new Promise((resolve, reject) => {
      /**
       *
       * @param {MessageEvent} eventMessage
       * @returns
       */
      const responseListener = (eventMessage) => {
        if (eventMessage.source !== window) {
          return false;
        }

        if (eventMessage.data.channelReply === channelReply && eventMessage.data.messageId === messageId) {
          window.removeEventListener('message', responseListener);
          resolve(eventMessage.data.response);
        }
      };

      window.addEventListener('message', responseListener);

      window.postMessage(payloadSend, '*');
      setTimeout(() => {
        window.removeEventListener('message', responseListener);
        reject(new Error('TIME_OUT'));
      }, timeout);
    });
  }
}

export const PostMessageFetchUrl = new PostMessageApi('FETCH_URL', 5000);
export const PostMessageFetchListener = new PostMessageApi('FETCH_LISTENER', 3000);
