/**
 * @file
 * Adds default classes to buttons for styling purposes.
 */

(function ($, { tabbable, isTabbable }) {
  $.widget('ui.dialog', $.ui.dialog, {
    options: {
      buttonClass: 'button',
      buttonPrimaryClass: 'button--primary',
    },
    _createButtons() {
      const opts = this.options;
      let primaryIndex;
      let index;
      const il = opts.buttons.length;
      for (index = 0; index < il; index++) {
        if (
          opts.buttons[index].primary &&
          opts.buttons[index].primary === true
        ) {
          primaryIndex = index;
          delete opts.buttons[index].primary;
          break;
        }
      }
      this._super();
      const $buttons = this.uiButtonSet.children().addClass(opts.buttonClass);
      if (typeof primaryIndex !== 'undefined') {
        $buttons.eq(index).addClass(opts.buttonPrimaryClass);
      }
    },
    _createWrapper() {
      this.uiDialog = $('<div>')
        .hide()
        .attr({
          // Setting tabIndex makes the div focusable
          tabIndex: -1,
          role: 'dialog',
        })
        .appendTo(this._appendTo());

      this._addClass(
        this.uiDialog,
        'ui-dialog',
        'ui-widget ui-widget-content ui-front',
      );
      this._on(this.uiDialog, {
        keydown(event) {
          if (
            this.options.closeOnEscape &&
            !event.isDefaultPrevented() &&
            event.keyCode &&
            event.keyCode === $.ui.keyCode.ESCAPE
          ) {
            event.preventDefault();
            this.close(event);
            return;
          }

          // Prevent tabbing out of dialogs
          if (
            event.keyCode !== $.ui.keyCode.TAB ||
            event.isDefaultPrevented()
          ) {
            return;
          }

          const tabbableElements = tabbable(this.uiDialog[0]);
          if (tabbableElements.length) {
            const first = tabbableElements[0];
            const last = tabbableElements[tabbableElements.length - 1];

            if (
              (event.target === last || event.target === this.uiDialog[0]) &&
              !event.shiftKey
            ) {
              this._delay(function () {
                $(first).trigger('focus');
              });
              event.preventDefault();
            } else if (
              (event.target === first || event.target === this.uiDialog[0]) &&
              event.shiftKey
            ) {
              this._delay(function () {
                $(last).trigger('focus');
              });
              event.preventDefault();
            }
          }
        },
        mousedown(event) {
          if (this._moveToTop(event)) {
            this._focusTabbable();
          }
        },
      });

      // We assume that any existing aria-describedby attribute means
      // that the dialog content is marked up properly
      // otherwise we brute force the content as the description
      if (!this.element.find('[aria-describedby]').length) {
        this.uiDialog.attr({
          'aria-describedby': this.element.uniqueId().attr('id'),
        });
      }
    },
    // Override jQuery UI's `_focusTabbable()` so finding tabbable elements uses
    // the core/tabbable library instead of jQuery UI's `:tabbable` selector.
    _focusTabbable() {
      // Set focus to the first match:

      // 1. An element that was focused previously.
      let hasFocus = this._focusedElement ? this._focusedElement.get(0) : null;

      // 2. First element inside the dialog matching [autofocus].
      if (!hasFocus) {
        hasFocus = this.element.find('[autofocus]').get(0);
      }

      // 3. Tabbable element inside the content element.
      // 4. Tabbable element inside the buttonpane.
      if (!hasFocus) {
        const $elements = [this.element, this.uiDialogButtonPane];
        for (let i = 0; i < $elements.length; i++) {
          const element = $elements[i].get(0);
          if (element) {
            const elementTabbable = tabbable(element);
            hasFocus = elementTabbable.length ? elementTabbable[0] : null;
          }
          if (hasFocus) {
            break;
          }
        }
      }

      // 5. The close button.
      if (!hasFocus) {
        const closeBtn = this.uiDialogTitlebarClose.get(0);
        hasFocus = closeBtn && isTabbable(closeBtn) ? closeBtn : null;
      }

      // 6. The dialog itself.
      if (!hasFocus) {
        hasFocus = this.uiDialog.get(0);
      }
      $(hasFocus).eq(0).trigger('focus');
    },
  });
})(jQuery, window.tabbable);