/**
 * @file
 * Poster selector JavaScript.
 */

(function (Drupal, once) {
  'use strict';

  Drupal.ffmpegMedia = Drupal.ffmpegMedia || {};

  /**
   * Poster selector controller.
   */
  Drupal.ffmpegMedia.PosterSelector = class PosterSelector {
    /**
     * @param {HTMLElement} container - Thumbnail grid container.
     */
    constructor(container) {
      this.container = container;
      this.form = container.closest('form');
      this.items = Array.from(container.querySelectorAll('.ffmpeg-thumbnail-item'));
      this.hiddenInput = this.form
        ? this.form.querySelector('#ffmpeg-selected-timestamp')
        : document.getElementById('ffmpeg-selected-timestamp');
      this.selectedIndex = this.items.findIndex((item) => item.classList.contains('selected'));
      this.focusIndex = this.selectedIndex >= 0 ? this.selectedIndex : 0;
      this.itemHandlers = [];
      this.customButton = null;
      this._onCustomButtonClickBound = this._onCustomButtonClick.bind(this);
      this._focusTrapBound = this._onFocusTrapKeydown.bind(this);
      this._customTimeSection = null;

      this._init();
    }

    /**
     * Initializes DOM semantics and event handlers.
     *
     * @private
     */
    _init() {
      this.container.setAttribute('role', 'listbox');
      this.container.setAttribute('aria-label', Drupal.t('Poster frame selection'));
      this.container.setAttribute('aria-orientation', 'horizontal');

      this.items.forEach((item, index) => {
        const clickHandler = () => this.selectItem(index, true);
        const keydownHandler = (event) => this._onItemKeydown(event, index);

        item.setAttribute('role', 'option');
        item.setAttribute('tabindex', index === this.focusIndex ? '0' : '-1');
        item.setAttribute('aria-selected', index === this.selectedIndex ? 'true' : 'false');
        item.addEventListener('click', clickHandler);
        item.addEventListener('keydown', keydownHandler);

        this.itemHandlers.push({ item, clickHandler, keydownHandler });
      });

      if (this.form) {
        this.form.addEventListener('click', this._onCustomButtonClickBound);
      }

      // Activate focus trap on custom time section if present.
      this._customTimeSection = this.form
        ? this.form.querySelector('.ffmpeg-custom-time')
        : document.querySelector('.ffmpeg-custom-time');
      if (this._customTimeSection) {
        this._customTimeSection.addEventListener('keydown', this._focusTrapBound);
      }
    }

    /**
     * Handles keyboard interactions for thumbnail options.
     *
     * @param {KeyboardEvent} event - Keyboard event.
     * @param {number} index - Current item index.
     * @private
     */
    _onItemKeydown(event, index) {
      switch (event.key) {
        case 'Enter':
        case ' ':
          event.preventDefault();
          this.selectItem(index, true);
          break;

        case 'ArrowRight':
        case 'ArrowDown':
          event.preventDefault();
          this._focusItem(index + 1);
          break;

        case 'ArrowLeft':
        case 'ArrowUp':
          event.preventDefault();
          this._focusItem(index - 1);
          break;

        case 'Home':
          event.preventDefault();
          this._focusItem(0);
          break;

        case 'End':
          event.preventDefault();
          this._focusItem(this.items.length - 1);
          break;
      }
    }

    /**
     * Focuses one thumbnail item and applies roving tabindex.
     *
     * @param {number} requestedIndex - Requested item index.
     * @private
     */
    _focusItem(requestedIndex) {
      if (this.items.length === 0) {
        return;
      }

      const normalizedIndex = this._setFocusIndex(requestedIndex);
      this.items[normalizedIndex].focus();
    }

    /**
     * Applies roving tabindex without changing focus.
     *
     * @param {number} requestedIndex - Requested focus index.
     * @returns {number} Normalized focus index.
     * @private
     */
    _setFocusIndex(requestedIndex) {
      if (this.items.length === 0) {
        return 0;
      }

      const normalizedIndex = Math.max(0, Math.min(this.items.length - 1, requestedIndex));
      this.focusIndex = normalizedIndex;

      this.items.forEach((item, index) => {
        item.setAttribute('tabindex', index === normalizedIndex ? '0' : '-1');
      });

      return normalizedIndex;
    }

    /**
     * Selects one thumbnail.
     *
     * @param {number} index - Thumbnail index.
     * @param {boolean} moveFocus - Whether to move focus to selected item.
     */
    selectItem(index, moveFocus) {
      if (index < 0 || index >= this.items.length) {
        return;
      }

      this.selectedIndex = index;
      this.focusIndex = index;

      this.items.forEach((item, itemIndex) => {
        const isSelected = itemIndex === index;
        item.classList.toggle('selected', isSelected);
        item.setAttribute('aria-selected', isSelected ? 'true' : 'false');
        item.setAttribute('tabindex', isSelected ? '0' : '-1');
      });

      const selectedItem = this.items[index];
      const timestamp = selectedItem.getAttribute('data-timestamp');
      if (this.hiddenInput && timestamp) {
        this.hiddenInput.value = timestamp;
      }

      if (moveFocus) {
        selectedItem.focus();
      }

      if (this.customButton) {
        this.customButton.disabled = false;
        this.customButton.textContent = Drupal.t('Use This Frame');
        this.customButton = null;
      }
    }

    /**
     * Handles "Use This Frame" clicks from custom timestamp preview.
     *
     * @param {MouseEvent} event - Click event.
     * @private
     */
    _onCustomButtonClick(event) {
      const button = event.target.closest('.ffmpeg-use-custom-btn');
      if (!button || (this.form && !this.form.contains(button))) {
        return;
      }

      const timestamp = button.getAttribute('data-timestamp');
      if (!timestamp) {
        return;
      }

      this.selectedIndex = -1;
      this.items.forEach((item) => {
        item.classList.remove('selected');
        item.setAttribute('aria-selected', 'false');
      });
      this._setFocusIndex(0);

      if (this.hiddenInput) {
        this.hiddenInput.value = timestamp;
      }

      button.textContent = Drupal.t('Selected!');
      button.disabled = true;
      this.customButton = button;
    }

    /**
     * Traps Tab focus within the custom time section.
     *
     * @param {KeyboardEvent} event - Keyboard event.
     * @private
     */
    _onFocusTrapKeydown(event) {
      if (event.key !== 'Tab' || !this._customTimeSection) {
        return;
      }

      var focusable = this._customTimeSection.querySelectorAll(
        'input, button:not([disabled]), select, textarea, [tabindex]:not([tabindex="-1"])'
      );
      if (focusable.length === 0) {
        return;
      }

      var first = focusable[0];
      var last = focusable[focusable.length - 1];

      if (event.shiftKey) {
        if (document.activeElement === first) {
          event.preventDefault();
          last.focus();
        }
      } else {
        if (document.activeElement === last) {
          event.preventDefault();
          first.focus();
        }
      }
    }

    /**
     * Removes all event listeners.
     */
    destroy() {
      this.itemHandlers.forEach(({ item, clickHandler, keydownHandler }) => {
        item.removeEventListener('click', clickHandler);
        item.removeEventListener('keydown', keydownHandler);
      });
      this.itemHandlers = [];

      if (this.form) {
        this.form.removeEventListener('click', this._onCustomButtonClickBound);
      }

      if (this._customTimeSection) {
        this._customTimeSection.removeEventListener('keydown', this._focusTrapBound);
      }
    }

    /**
     * Loads thumbnails via AJAX with CSRF protection.
     *
     * @param {object} settings - The ffmpegPosterSelector drupalSettings.
     * @returns {Promise<object>} JSON response from the thumbnails endpoint.
     */
    static async loadThumbnails(settings) {
      var csrfResponse = await fetch(settings.csrfTokenUrl);
      var csrfToken = await csrfResponse.text();

      var response = await fetch(settings.ajaxUrl, {
        method: 'POST',
        headers: {
          'X-CSRF-Token': csrfToken
        }
      });

      return response.json();
    }
  };

  /**
   * Handles poster thumbnail selection.
   */
  Drupal.behaviors.ffmpegPosterSelector = {
    attach: function (context) {
      once('ffmpeg-poster-selector', '.ffmpeg-thumbnail-grid', context).forEach(function (container) {
        container.ffmpegPosterSelector = new Drupal.ffmpegMedia.PosterSelector(container);
      });
    },

    detach: function (context, settings, trigger) {
      if (trigger !== 'unload') {
        return;
      }

      const grids = context.querySelectorAll ? context.querySelectorAll('.ffmpeg-thumbnail-grid') : [];
      grids.forEach(function (grid) {
        if (grid.ffmpegPosterSelector) {
          grid.ffmpegPosterSelector.destroy();
          delete grid.ffmpegPosterSelector;
        }
      });
    }
  };

})(Drupal, once);
