/**
 * @typedef {'development' | 'staging' | 'test' | 'production'} Environment
 */

/**
 * @typedef {'buy' | 'sell'} Flow
 */

/**
 * @typedef {Object} Options
 * @property {Environment} [env] - The environment where the widget will run (e.g., 'development', 'staging'). Defaults to 'production' if not provided.
 * @property {string|null} [domain] - Custom domain if running the widget locally. Defaults to `null`.
 * @property {Flow} [flow] - Preselect the initial page of the widget ('buy' or 'sell'). Defaults to 'buy' if not provided.
 * @property {string|null} [cryptoCurrency] - Preselect a cryptocurrency for the widget. Defaults to `null`.
 */

/**
 * A class representing the BitPay Crypto Widget.
 * This class is responsible for creating and managing a cryptocurrency widget, handling configuration,
 * iframe embedding, and postMessage communication between the widget and the parent window.
 */
class BitPayCryptoWidget {
  /**
   * The environment configuration for the widget.
   * @type {Environment|null}
   * @private
   */
  env = null;

  /**
   * Custom domain if running widget locally.
   * @type {string|null}
   * @private
   */
  domain = null;

  /**
   * The on-ramp or off-ramp flow of the widget.
   * @type {Flow|null}
   * @private
   */
  flow = 'buy';

  /**
   * The preselected cryptocurrency for the widget.
   * @type {string|null}
   * @private
   */
  cryptoCurrency = null;

  /**
   * The parent's DOM element container where the widget will be attached.
   * @type {HTMLElement|null}
   * @private
   */
  container = null;

  /**
   * The iframe element that will contain the widget.
   * @type {HTMLIFrameElement|null}
   * @private
   */
  iFrame = null;

  /**
   * The iFrames src url
   * @type {string|null}
   * @private
   */
  src = null;

  /**
   * Creates an instance of the BitPayCryptoWidget.
   * This constructor initializes the widget based on the provided options, setting up the environment,
   * preselected cryptocurrency, and the widget flow (buy/sell).
   *
   * @param {Options} options - Object of options required for iFrame initialization.
   *
   * @example
   * const widget = new BitPayCryptoWidget({
   *   env: 'development'
   *   domain: '<user>.bp:8088',
   *   flow: 'buy',
   *   cryptoCurrency: 'BTC'
   * });
   */
  constructor(options) {
    this.env = options.env || 'production';
    this.domain = options.domain || null;

    this.src = this.getDomain(options);
    this.flow = options.flow || this.flow;
    this.cryptoCurrency = options.cryptoCurrency || null;

    window.addEventListener('message', this.postMessageBitPayWidgetToParent.bind(this));
  }

  /**
   * @param {Options} options
   * @private
   */
  getDomain(options) {
    const scheme = 'https';
    let subdomain = '';
    let host = 'bitpay.com';
    let base = 'crypto-widget';

    switch (options.env) {
      case 'development':
        host = options.domain;
        break;
      case 'production':
      default:
        break;
    }

    const domain = subdomain ? `${subdomain}.${host}` : host;
    let url = `${scheme}://${domain}/${base}/${options.flow}`;

    if (options.cryptoCurrency) {
      url += `?crypto_currency=${encodeURIComponent(options.cryptoCurrency)}`;
    }

    return url;
  }

  /**
   * Handles postMessage events sent from the widget iFrame to the parent window.
   * @param {MessageEvent} event - The message event.
   * @private
   */
  postMessageBitPayWidgetToParent(event) {
    const isWidgetMessage = event.data.channel === 'bitpay-widget';
    const isMounted = event.data.payload === 'mounted';
    if (!isWidgetMessage || !isMounted) {
      return;
    }

    const bitpayWidget = document.querySelector('#bitpay-widget');
    if (!bitpayWidget) {
      console.error('BitPay widget not found');
      return;
    }

    const queryParams = bitpayWidget.getAttribute('data-query-params');
    const data = {
      channel: 'parent',
      payload: {
        queryParams,
        url: window.location.href,
      },
    };

    bitpayWidget.contentWindow?.postMessage(data, '*');
  }

  /**
   * Attaches the iFrame to the DOM element with id "bitpay-widget-container".
   */
  attach() {
    this.container = document.getElementById('bitpay-widget-container');

    if (!this.container) {
      console.error('Unable to find DOM element #bitpay-widget-container to attach iFrame to');
      return;
    }

    this.iFrame = document.createElement('iframe');

    this.iFrame.setAttribute('src', this.src);

    this.iFrame.setAttribute('id', 'bitpay-widget');
    this.iFrame.setAttribute('scrolling', 'no');
    this.iFrame.setAttribute('data-query-params', location.search);

    this.iFrame.style.width = '100%';
    this.iFrame.style.height = '100%';
    this.iFrame.style.border = 'none';
    this.iFrame.style.borderRadius = 'inherit';
    this.iFrame.style.flex = '1';

    this.container.innerHTML = '';
    this.container.appendChild(this.iFrame);
  }
}

window.BitPayCryptoWidget = BitPayCryptoWidget;
