/**
 * Composable Modal Component Template
 *
 * Based on James Simone's composable modal pattern with:
 * - Focus trap implementation
 * - ESC key to close
 * - Backdrop click to close
 * - Slot-based composition
 * - ARIA accessibility attributes
 * - Proper cleanup on disconnect
 *
 * @see https://www.jamessimone.net/blog/joys-of-apex/lwc-composable-modal/
 */
import { LightningElement, api } from 'lwc';

const OUTER_MODAL_CLASS = 'outerModalContent';
const ESCAPE_KEY = 'Escape';
const TAB_KEY = 'Tab';

export default class ModalComponent extends LightningElement {
  // Public API properties
  @api modalHeader;
  @api modalTagline;
  @api modalSaveHandler;
  @api hideFooter = false;
  @api size = 'medium'; // small, medium, large

  // Private state
  _isOpen = false;
  _focusableElements = [];
  _boundHandleKeyUp;

  constructor() {
    super();
    // Bind the handler once to ensure we can remove it later
    this._boundHandleKeyUp = this._handleKeyUp.bind(this);
  }

  /**
   * Public method to toggle modal visibility
   * Called by parent component to open/close the modal
   */
  @api
  toggleModal() {
    this._isOpen = !this._isOpen;

    if (this._isOpen) {
      this._onOpen();
    } else {
      this._onClose();
    }
  }

  /**
   * Public method to explicitly open the modal
   */
  @api
  open() {
    if (!this._isOpen) {
      this._isOpen = true;
      this._onOpen();
    }
  }

  /**
   * Public method to explicitly close the modal
   */
  @api
  close() {
    if (this._isOpen) {
      this._isOpen = false;
      this._onClose();
    }
  }

  // Computed properties for template
  get isOpen() {
    return this._isOpen;
  }

  get modalAriaHidden() {
    return String(!this._isOpen);
  }

  get modalContainerClass() {
    const sizeClass =
      this.size === 'large' ? 'slds-modal_large' : this.size === 'small' ? 'slds-modal_small' : '';
    return this._isOpen ? `slds-modal slds-fade-in-open ${sizeClass}` : 'slds-modal slds-hidden';
  }

  get backdropClass() {
    return this._isOpen ? 'slds-backdrop slds-backdrop_open' : 'slds-backdrop';
  }

  get showHeader() {
    return this.modalHeader || this.modalTagline;
  }

  // Lifecycle hooks
  disconnectedCallback() {
    // Clean up window event listener to prevent memory leaks
    window.removeEventListener('keyup', this._boundHandleKeyUp);
  }

  // Private methods
  _onOpen() {
    // Collect all focusable elements within the modal
    // Elements must have the 'focusable' class
    this._focusableElements = [
      ...this.querySelectorAll('.focusable'),
      ...this.template.querySelectorAll(
        'lightning-button, lightning-button-icon, lightning-input, button, [tabindex="0"]'
      ),
    ].filter((el) => !el.disabled);

    // Focus the first focusable element
    this._focusFirstElement();

    // Add window-level keyboard listener for ESC and Tab
    window.addEventListener('keyup', this._boundHandleKeyUp);

    // Dispatch open event
    this.dispatchEvent(new CustomEvent('modalopen'));
  }

  _onClose() {
    // Remove keyboard listener
    window.removeEventListener('keyup', this._boundHandleKeyUp);

    // Dispatch close event
    this.dispatchEvent(new CustomEvent('modalclose'));
  }

  _handleKeyUp(event) {
    if (event.code === ESCAPE_KEY || event.key === ESCAPE_KEY) {
      this.close();
    } else if (event.code === TAB_KEY || event.key === TAB_KEY) {
      this._handleTabNavigation(event);
    }
  }

  _handleTabNavigation(event) {
    // Implement focus trap - keep focus within modal
    if (this._focusableElements.length === 0) return;

    const activeElement = this.template.activeElement || document.activeElement;
    const lastIndex = this._focusableElements.length - 1;
    const currentIndex = this._focusableElements.indexOf(activeElement);

    if (event.shiftKey && currentIndex <= 0) {
      // Shift+Tab on first element - wrap to last
      event.preventDefault();
      this._focusableElements[lastIndex]?.focus();
    } else if (!event.shiftKey && currentIndex >= lastIndex) {
      // Tab on last element - wrap to first
      event.preventDefault();
      this._focusFirstElement();
    }
  }

  _focusFirstElement() {
    // Small delay to ensure modal is fully rendered
    // eslint-disable-next-line @lwc/lwc/no-async-operation
    setTimeout(() => {
      if (this._focusableElements.length > 0) {
        this._focusableElements[0].focus();
      }
    }, 0);
  }

  // Event handlers
  handleBackdropClick(event) {
    // Close modal if clicking on the backdrop (outside modal content)
    if (event.target.classList.contains(OUTER_MODAL_CLASS)) {
      this.close();
    }
  }

  handleCancel() {
    this.close();
  }

  handleSave() {
    // If a save handler was provided, call it
    if (typeof this.modalSaveHandler === 'function') {
      this.modalSaveHandler();
    }

    // Dispatch save event for parent components
    this.dispatchEvent(new CustomEvent('modalsave'));

    // Close the modal
    this.close();
  }
}
