import { EMPTY_STRING } from '../constants/constants.js';
import { MelHtml } from '../html/JsHtml/MelHtml.js';
import { BnumEvent } from '../mel_events.js';
import { MelEnumerable } from './enum.js';

const default_template = MelHtml.start
  .div({ class: 'bnum-popover', role: 'dialog', 'aria-modal': true })
  .div({ class: 'bnum-popover-content' })
  .end()
  .end();

const default_arrow = '<div data-popper-arrow></div>';

export class MelPopover {
  /**
   *
   * @param {*} $item
   * @param {____JsHtml} tooltip
   * @param {*} config
   */
  constructor(
    $item,
    tooltip,
    {
      template = default_template,
      arrow = default_arrow,
      config = {},
      container = 'body',
      context = window,
    } = {},
  ) {
    let $tmp = template
      .generate({ context })
      .appendTo(typeof container === 'string' ? $(container) : container)
      .css({ 'min-width': '30px', 'min-height': '1px' });
    const width = tooltip
      .generate({ context })
      .appendTo($tmp.find('.bnum-popover-content'))
      .css('width');

    $tmp
      .css('width', `${width}px`)
      .prepend($(arrow).addClass('bnum-popover-arrow'))
      .on('keydown', (e) => {
        console.log('e', e);
        switch (e.originalEvent.keyCode) {
          case 27:
            this.hide();
            break;

          default:
            break;
        }
      });

    $tmp.find('.bnum-popover-content').css('display', 'none');
    $tmp.find('.bnum-popover-arrow').addClass('hide');

    let $last = this._find_last_focusable($tmp);

    $tmp.find('.bnum-popover-content').append('<span tabindex=0></span>');

    if ($last && $last.length) {
      $last.on('focusout', () => {
        if (this.is_shown()) {
          let $element = this._find_first_focusable(
            $(this._pop.state.elements.popper),
          );

          if ($element) {
            $element[0].focus();
          }
        }
      });
    }

    if (!config) config = {};
    if (!config.modifiers) config.modifiers = [];

    if (!MelEnumerable.from(config.modifiers).any((x) => x.name === 'offset')) {
      let offset;

      switch (config.placement) {
        case 'left':
        case 'right':
          offset = [8, 0];
          break;

        case 'top':
        case 'bottom':
        default:
          offset = [0, 8];
          break;
      }

      config.modifiers.push({
        name: 'offset',
        options: {
          offset,
        },
      });
    }

    this._pop = Popper.createPopper($item[0], $tmp[0], config);

    this.onhide = new BnumEvent();

    $last = null;
    $tmp = null;
  }

  _find_last_focusable($element) {
    return this._find_focusable(
      MelEnumerable.from(this._flat_jquery($element))
        .select((x, i) => {
          return { x, i };
        })
        .orderByDescending((x) => x.i)
        .select((x) => x.x),
    );
  }

  _find_first_focusable($element) {
    return this._find_focusable(
      MelEnumerable.from(this._flat_jquery($element)),
    );
  }

  _find_focusable(elements) {
    // for (const element of $element) {
    //   if (aria.Utils.isFocusable(element)) return $(element);
    // }

    return elements
      .where((x) => aria.Utils.isFocusable(x?.[0] ? x[0] : x))
      .firstOrDefault();
  }

  *_flat_jquery($element) {
    yield $element;
    for (const element of $element.children()) {
      yield* this._flat_jquery($(element));
    }
  }

  show() {
    let $pop;
    let $button;

    $pop = $(this._pop.state.elements.popper);

    $pop.find('.bnum-popover-content').css('display', EMPTY_STRING);
    $pop.find('.bnum-popover-arrow').removeClass('hide');

    $button = $pop.find('button');

    if ($button.length > 0) $button.first().focus();
    else $pop.css('tabindex', 0).focus();

    $button = null;
    $pop = null;

    return this;
  }

  hide() {
    let $pop = $(this._pop.state.elements.popper);
    $pop.find('.bnum-popover-content').css('display', 'none');

    $pop.find('.bnum-popover-arrow').addClass('hide');

    $pop = null;

    this.onhide.call();
    return this;
  }

  toggle() {
    if (this.is_shown()) this.hide();
    else this.show();
  }

  is_shown() {
    return (
      $(this._pop.state.elements.popper)
        .find('.bnum-popover-content')
        .css('display') !== 'none'
    );
  }
}