import {registerGlobalInitFunc} from '../modules/observer.ts';
import {svg} from '../svg.ts';
import {toggleElem} from '../utils/dom.ts';

export function initPasswordVisibility(): void {
  registerGlobalInitFunc('initPasswordVisibilityToggle', initPasswordVisibilityToggle);
}

function initPasswordVisibilityToggle(container: HTMLElement) {
  const passwordInput = container.querySelector<HTMLInputElement>('.js-password-toggle-source');
  if (!passwordInput) return;

  const form = container.closest('form');
  const confirmFieldSelector = container.getAttribute('data-confirm-field-selector');
  const confirmInputSelector = container.getAttribute('data-confirm-input-selector');
  const confirmField = confirmFieldSelector ? form?.querySelector<HTMLElement>(confirmFieldSelector) : null;
  const confirmInput = confirmInputSelector ? form?.querySelector<HTMLInputElement>(confirmInputSelector) : null;
  const showLabel = container.getAttribute('data-show-label')!;
  const hideLabel = container.getAttribute('data-hide-label')!;
  let wasEnteredManually = false;
  let pendingManualInput = false;
  let valueWasEmptyBeforeManualInput = passwordInput.value === '';
  let fullValueWasSelectedBeforeManualInput = false;
  const toggleButton = document.createElement('button');

  toggleButton.className = 'ui icon button js-password-toggle';
  toggleButton.type = 'button';

  const removeToggleButtons = () => {
    for (const button of container.querySelectorAll<HTMLButtonElement>('.js-password-toggle')) {
      button.remove();
    }
  };

  const isAutofilled = () => {
    try {
      if (passwordInput.matches(':autofill')) return true;
    } catch {}
    try {
      if (passwordInput.matches(':-webkit-autofill')) return true;
    } catch {}
    return false;
  };

  container.classList.remove('has-visible-toggle');
  removeToggleButtons();

  const syncToggleButton = () => {
    const isVisible = passwordInput.type === 'text';
    toggleButton.setAttribute('aria-label', isVisible ? hideLabel : showLabel);
    toggleButton.title = isVisible ? hideLabel : showLabel;
    toggleButton.innerHTML = isVisible ? svg('octicon-eye-closed') : svg('octicon-eye');
  };

  const restoreCaretPosition = (selectionStart: number | null, selectionEnd: number | null, selectionDirection: string | null) => {
    passwordInput.focus({preventScroll: true});
    if (selectionStart === null || selectionEnd === null) return;

    const direction = selectionDirection === 'backward' || selectionDirection === 'forward' ? selectionDirection : 'none';
    const applySelection = () => {
      try {
        passwordInput.setSelectionRange(selectionStart, selectionEnd, direction);
      } catch {}
    };

    applySelection();
    window.requestAnimationFrame(applySelection);
  };

  const syncConfirmField = () => {
    const isVisible = passwordInput.type === 'text';
    if (confirmField && confirmInput) {
      toggleElem(confirmField, !isVisible);
      if (isVisible) {
        confirmInput.required = false;
        confirmInput.value = passwordInput.value;
      } else {
        confirmInput.required = true;
      }
    }
    syncToggleButton();
  };

  const syncToggleAvailability = () => {
    const canRevealPassword = wasEnteredManually && Boolean(passwordInput.value);
    container.classList.toggle('has-visible-toggle', canRevealPassword);
    if (!canRevealPassword && passwordInput.type === 'text') {
      passwordInput.type = 'password';
    }
    if (canRevealPassword) {
      if (!container.contains(toggleButton)) {
        removeToggleButtons();
        container.append(toggleButton);
      }
      toggleButton.tabIndex = 0;
    } else {
      removeToggleButtons();
    }
    syncConfirmField();
  };

  toggleButton.addEventListener('pointerdown', (event) => {
    if (!container.classList.contains('has-visible-toggle')) return;
    event.preventDefault();
  });

  toggleButton.addEventListener('click', () => {
    if (!container.classList.contains('has-visible-toggle')) return;
    const selectionStart = passwordInput.selectionStart;
    const selectionEnd = passwordInput.selectionEnd;
    const selectionDirection = passwordInput.selectionDirection;
    passwordInput.type = passwordInput.type === 'password' ? 'text' : 'password';
    syncConfirmField();
    restoreCaretPosition(selectionStart, selectionEnd, selectionDirection);
  });

  passwordInput.addEventListener('beforeinput', (event) => {
    const manualInputTypes = new Set([
      'insertText',
      'insertReplacementText',
      'insertFromPaste',
      'insertFromDrop',
      'insertCompositionText',
      'deleteContentBackward',
      'deleteContentForward',
      'deleteByCut',
      'deleteByDrag',
      'deleteByComposition',
    ]);
    pendingManualInput = event.isTrusted && manualInputTypes.has(event.inputType);
    if (pendingManualInput) {
      valueWasEmptyBeforeManualInput = passwordInput.value === '';
      fullValueWasSelectedBeforeManualInput =
        passwordInput.value.length > 0 &&
        passwordInput.selectionStart === 0 &&
        passwordInput.selectionEnd === passwordInput.value.length;
    }
  });

  passwordInput.addEventListener('input', (event) => {
    if (pendingManualInput && passwordInput.value === '') {
      wasEnteredManually = false;
    } else if (
      pendingManualInput &&
      passwordInput.value !== '' &&
      (wasEnteredManually || valueWasEmptyBeforeManualInput || fullValueWasSelectedBeforeManualInput)
    ) {
      wasEnteredManually = true;
    } else if (isAutofilled() || (!pendingManualInput && event.isTrusted && !valueWasEmptyBeforeManualInput)) {
      wasEnteredManually = false;
    }
    pendingManualInput = false;
    fullValueWasSelectedBeforeManualInput = false;
    if (passwordInput.type === 'text' && confirmInput) {
      confirmInput.value = passwordInput.value;
    }
    syncToggleAvailability();
  });

  syncToggleAvailability();
}
