import { getTabbableBoundary } from './tabbable';

let activeModals: HTMLElement[] = [];

export default class Modal {
  element: HTMLElement;
  tabDirection: 'forward' | 'backward' = 'forward';

  constructor(element: HTMLElement) {
    this.element = element;
    this.handleFocusIn = this.handleFocusIn.bind(this);
    this.handleKeyDown = this.handleKeyDown.bind(this);
    this.handleKeyUp = this.handleKeyUp.bind(this);
  }

  activate() {
    activeModals.push(this.element);
    document.addEventListener('focusin', this.handleFocusIn);
    document.addEventListener('keydown', this.handleKeyDown);
    document.addEventListener('keyup', this.handleKeyUp);
  }

  deactivate() {
    activeModals = activeModals.filter(modal => modal !== this.element);
    document.removeEventListener('focusin', this.handleFocusIn);
    document.removeEventListener('keydown', this.handleKeyDown);
    document.removeEventListener('keyup', this.handleKeyUp);
  }

  isActive() {
    // The "active" modal is always the most recent one shown
    return activeModals[activeModals.length - 1] === this.element;
  }

  checkFocus() {
    if (this.isActive()) {
      if (!this.element.matches(':focus-within')) {
        const { start, end } = getTabbableBoundary(this.element);
        const target = this.tabDirection === 'forward' ? start : end;

        if (typeof target?.focus === 'function') {
          target.focus({ preventScroll: true });
        }
      }
    }
  }

  handleFocusIn() {
    this.checkFocus();
  }

  handleKeyDown(event: KeyboardEvent) {
    if (event.key === 'Tab' && event.shiftKey) {
      this.tabDirection = 'backward';
    }

    // Ensure focus remains trapped after they key is pressed
    requestAnimationFrame(() => this.checkFocus());
  }

  handleKeyUp() {
    this.tabDirection = 'forward';
  }
}
