const isVersion20230926 = () => $('.version-2023-09-26').length > 0;

/**
 * Moves focus to the `<main>` element or its first `<h1>`
 * @returns {void}
 */
function focusOnMain() {
  if (!isVersion20230926()) {
    return;
  }

  const $h1 = $('main h1:visible');
  const $target = ($h1.length > 0 ? $h1 : $('main')).first();
  /** @type {HTMLElement} */
  const target = $target.attr('tabIndex', -1)[0];
  
  if (target) {
    target.focus({ preventScroll: true })
  }
}

/**
 * Fired after a data store's state is updated
 */
class DataStoreUpdatedEvent extends Event {
  constructor() {
    super('updated');
  }
}

/**
 * A simple reactive store. Stores state and fires events when the state
 * changes. Listen using native EventTarget methods.
 *
 * @template {object} State
 */
class DataStore extends EventTarget {
  /**
   * @type {State}
   * @readonly
   */
  state;

  /**
   * @param {State} data 
   */
  constructor(data) {
    super();

    this.state = new Proxy(data, {
      get: (obj, prop) => obj[prop],

      set: (obj, prop, value) => {
        if (obj[prop] !== value) {
          obj[prop] = value;
          this.#dispatchUpdated();
        }

        return true;
      },

      deleteProperty: (obj, prop) => {
        delete obj[prop];
        this.#dispatchUpdated();

        return true;
      },
    }); 
  }

  #dispatchUpdated() {
    this.dispatchEvent(new DataStoreUpdatedEvent());
  }
}

/**
 * @param {string} title 
 * @returns {void}
 */
function setPageTitle(title) {
  const el = document.querySelector('title');
  
  if (el) {
    el.textContent = `${title} - GORequest`;
  }
}

/**
 * Keeps track of data about the current page
 */
const pageStore = new DataStore({
  /** @type {'main' | 'topic' | 'loggedin' | 'faq_search' | 'faq_display' | 'subscribe' | null} */
  page: null,
  /** @type {string | null} */
  topic: null,
});

// Update the page title when the page store is updated
pageStore.addEventListener('updated', () => {
  const { page, topic } = pageStore.state;

  /**
   * @type {Record<Exclude<typeof pageStore.state.page, null>, string>}
   */
  const map = {
    main: topic ? `New Issue: ${topic}` : 'New Issue',
    topic: 'New Issue',
    loggedin: 'Issues',
    faq_search: 'Knowledgebase',
    faq_display: topic ? `Knowledgebase: ${topic}` : 'Knowledgebase',
    subscribe: 'Subscribe',
  };

  const title = page ? map[page] : null;

  if (title) {
    setPageTitle(title);
  }
});

/**
 * @param {JQuery<HTMLElement>} $dialog 
 * @returns {boolean}
 */
function canUseModalMethods($dialog) {
  return $dialog.is('.go-dialog-version-2023-10-27');
}

/**
 * @param {JQuery<HTMLDialogElement>} $dialog 
 * @param {JQuery<HTMLElement> | ((event: Event) => JQuery<HTMLElement>)} [explicitTriggerOrGetter]
 * @returns {void}
 */
function showModal($dialog, explicitTriggerOrGetter) {
  /** @type {JQuery<HTMLElement>} */
  const $activeEl = $(document.activeElement);

  /**
   * @param {Event} event
   * @returns {JQuery<HTMLElement>}
   */
  function getElToFocusOnClose(event) {
    // Use the explicitly set trigger, or default to the currently focused
    // element
    if (explicitTriggerOrGetter) {
      if (typeof explicitTriggerOrGetter === 'function') {
        return explicitTriggerOrGetter(event);
      }

      if (explicitTriggerOrGetter.length > 0) {
        return explicitTriggerOrGetter;
      }
    }

    return $activeEl;
  }

  if ($dialog.length > 0) {
    // Native <dialog> modal behavior
    $dialog[0].showModal();
  }

  $dialog.on('close', (event) => {
    const $elToFocusOnClose = getElToFocusOnClose(event);

    if ($elToFocusOnClose) {
      $elToFocusOnClose.trigger('focus');
    }
  });

  // Handle clicking outside
  $(document).on('click', (event) => {
    if (($dialog.is(event.target) || !$dialog.has(event.target)) && $dialog[0]) {
      $dialog[0].close();
    }
  });

  // Animate modal in and focus first focusable element - on next tick to avoid
  // interfering with showModal()
  setTimeout(
    () => $dialog
      .addClass('in')
      .find('input, select, textarea, button, [tabindex]')
      .first()
      .trigger('focus'),
    0
  );
}

/**
 * @param {JQuery<HTMLDialogElement>} $dialog 
 * @returns {void}
 */
function hideParentModal($dialog) {
  const dialog = $dialog.closest('dialog')[0];

  if (dialog) {
    dialog.close();
  }
}

/**
 * @param {JQuery<HTMLDialogElement>} $dialog
 * @returns {void}
 */
function hideModal($dialog) {
  $dialog.removeClass('in');

  if ($dialog[0]) {
    $dialog[0].close();
  }
}
