import {GET, POST} from '../modules/fetch.ts';
import {showGlobalErrorMessage} from '../modules/errors.ts';
import {fomanticQuery} from '../modules/fomantic/base.ts';
import {initTabSwitcher} from '../modules/fomantic/tab.ts';
import {addDelegatedEventListener, queryElems} from '../utils/dom.ts';
import {registerGlobalInitFunc, registerGlobalSelectorFunc} from '../modules/observer.ts';
import {initAvatarUploaderWithCropper} from './comp/Cropper.ts';
import {initCompSearchRepoBox} from './comp/SearchRepoBox.ts';
import {initScopedWorkflowRequired} from './comp/ScopedWorkflows.ts';

const {appUrl, appSubUrl} = window.config;

function initHeadNavbarContentToggle() {
  const navbar = document.querySelector('#navbar');
  const btn = document.querySelector('#navbar-expand-toggle');
  if (!navbar || !btn) return;

  btn.addEventListener('click', () => {
    const isExpanded = btn.classList.contains('active');
    navbar.classList.toggle('navbar-menu-open', !isExpanded);
    btn.classList.toggle('active', !isExpanded);
  });
}

function initFooterLanguageMenu() {
  document.querySelector('.ui.dropdown .menu.language-menu')?.addEventListener('click', async (e) => {
    const item = (e.target as HTMLElement).closest('.item');
    if (!item) return;
    e.preventDefault();
    await GET(item.getAttribute('data-url')!);
    window.location.reload();
  });
}

function initFooterThemeSelector() {
  const elDropdown = document.querySelector('#footer-theme-selector');
  if (!elDropdown) return; // some pages don't have footer, for example: 500.tmpl
  const $dropdown = fomanticQuery(elDropdown);
  $dropdown.dropdown({
    direction: 'upward',
    apiSettings: {url: `${appSubUrl}/-/web-theme/list`, cache: false},
  });
  addDelegatedEventListener(elDropdown, 'click', '.menu > .item', async (el) => {
    const themeName = el.getAttribute('data-value')!;
    await POST(`${appSubUrl}/-/web-theme/apply?theme=${encodeURIComponent(themeName)}`);
    window.location.reload();
  });
}

export function initCommmPageComponents() {
  initHeadNavbarContentToggle();
  initFooterLanguageMenu();
  initFooterThemeSelector();
}

export function initGlobalDropdown() {
  // do not init "custom" dropdowns, "custom" dropdowns are managed by their own code.
  registerGlobalSelectorFunc('.ui.dropdown:not(.custom)', (el) => {
    const $dropdown = fomanticQuery(el);
    if ($dropdown.data('module-dropdown')) return; // do not re-init if other code has already initialized it.

    $dropdown.dropdown('setting', {hideDividers: 'empty'});

    if (el.classList.contains('jump')) {
      // The "jump" means this dropdown is mainly used for "menu" purpose,
      // clicking an item will jump to somewhere else or trigger an action/function.
      // When a dropdown is used for non-refresh actions with tippy,
      // it must have this "jump" class to hide the tippy when dropdown is closed.
      $dropdown.dropdown('setting', {
        action: 'hide',
        onShow() {
          // hide associated tooltip while dropdown is open
          this._tippy?.hide();
          this._tippy?.disable();
        },
        onHide() {
          this._tippy?.enable();
          // eslint-disable-next-line unicorn/no-this-assignment
          const elDropdown = this;

          // hide all tippy elements of items after a while. eg: use Enter to click "Copy Link" in the Issue Context Menu
          setTimeout(() => {
            const $dropdown = fomanticQuery(elDropdown);
            if ($dropdown.dropdown('is hidden')) {
              queryElems(elDropdown, '.menu > .item', (el) => el._tippy?.hide());
            }
          }, 2000);
        },
      });
    }

    // Special popup-directions, prevent Fomantic from guessing the popup direction.
    // With default "direction: auto", if the viewport height is small, Fomantic would show the popup upward,
    //   if the dropdown is at the beginning of the page, then the top part would be clipped by the window view.
    //   eg: Issue List "Sort" dropdown
    // But we can not set "direction: downward" for all dropdowns, because there is a bug in dropdown menu positioning when calculating the "left" position,
    //   which would make some dropdown popups slightly shift out of the right viewport edge in some cases.
    //   eg: the "Create New Repo" menu on the navbar.
    if (el.classList.contains('upward')) $dropdown.dropdown('setting', 'direction', 'upward');
    if (el.classList.contains('downward')) $dropdown.dropdown('setting', 'direction', 'downward');
  });
}

export function initGlobalComponent() {
  registerGlobalInitFunc('initTabSwitcher', initTabSwitcher);
  registerGlobalInitFunc('initAvatarUploader', initAvatarUploaderWithCropper);
  registerGlobalInitFunc('initSearchRepoBox', initCompSearchRepoBox);
  registerGlobalInitFunc('initScopedWorkflowRequired', initScopedWorkflowRequired);
}

// for performance considerations, it only uses performant syntax
function attachInputDirAuto(el: Partial<HTMLInputElement | HTMLTextAreaElement>) {
  if (el.type !== 'hidden' &&
    el.type !== 'checkbox' &&
    el.type !== 'radio' &&
    el.type !== 'range' &&
    el.type !== 'color') {
    el.dir = 'auto';
  }
}

function autoFocusEnd(el: HTMLInputElement | HTMLTextAreaElement) {
  el.focus();
  el.setSelectionRange(el.value.length, el.value.length);
}

export function applyAutoFocus(container: Element) {
  // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/autofocus
  // "autofocus" behavior is defined by the standard: when a container (e.g.: dialog) becomes visible, focus the element with "autofocus" attribute
  // Fomantic UI already supports it for its modal dialog, we need to cover more cases (e.g.: ".show-panel" button)
  // Here is just a simple support, we don't expect more than one element that need "autofocus" appearing in the same container
  container.querySelector<HTMLElement>('[autofocus]')?.focus();
  // Also, apply our autoFocusEnd behavior
  // TODO: GLOBAL-INIT-MULTIPLE-FUNCTIONS: use "~=" operator in case we would extend the "data-global-init" to support more functions in the future.
  const el = container.querySelector<HTMLInputElement>('[data-global-init~="autoFocusEnd"]');
  if (el) autoFocusEnd(el);
}

export function initGlobalInput() {
  registerGlobalSelectorFunc('input, textarea', attachInputDirAuto);

  // autoFocusEnd is used for autofocus an input/textarea and move the cursor to the end of the text.
  // It is useful for "New Issue"/"New PR" pages when the title is pre-filled with prefix text (e.g.: from template or commit message)
  // The native "autofocus" isn't used because there is a delay between "focused (DOM rendering)" and "move cursor to end (our JS)", it causes flickers.
  registerGlobalInitFunc('autoFocusEnd', autoFocusEnd);
}

/**
 * Too many users set their ROOT_URL to wrong value, and it causes a lot of problems:
 *   * Cross-origin API request without correct cookie
 *   * Incorrect href in <a>
 *   * ...
 * So we check whether current URL starts with AppUrl(ROOT_URL).
 * If they don't match, show a warning to users.
 */
export function checkAppUrl() {
  const curUrl = window.location.href;
  // some users visit "https://domain/gitea" while appUrl is "https://domain/gitea/", there should be no warning
  if (curUrl.startsWith(appUrl) || `${curUrl}/` === appUrl) {
    return;
  }
  showGlobalErrorMessage(`The detected web site URL is "${appUrl}", it's unlikely matching the site config.
Mismatched app.ini ROOT_URL or reverse proxy "Host/X-Forwarded-Proto" config might cause wrong URL links for web UI/mail content/webhook notification/OAuth2 sign-in.`, 'warning');
}

export function checkAppUrlScheme() {
  const curUrl = window.location.href;
  // some users visit "http://domain" while appUrl is "https://domain", COOKIE_SECURE makes it impossible to sign in
  if (curUrl.startsWith('http:') && appUrl.startsWith('https:')) {
    showGlobalErrorMessage(`This instance is configured to run under HTTPS (by ROOT_URL config), you are accessing by HTTP. Mismatched scheme might cause problems for sign-in/sign-up.`, 'warning');
  }
}
