/**
 * Accessibility Utility Functions
 *
 * Helper functions for implementing accessibility features across the app.
 */

/**
 * Announce a message to screen readers
 *
 * Creates a visually hidden element with aria-live attribute to announce
 * messages to screen reader users without visual interruption.
 *
 * @param message - The message to announce
 * @param priority - 'polite' (wait for pause) or 'assertive' (interrupt immediately)
 */
export function announceToScreenReader(
  message: string,
  priority: 'polite' | 'assertive' = 'polite',
): void {
  const announcement = document.createElement('div');
  announcement.setAttribute('role', 'status');
  announcement.setAttribute('aria-live', priority);
  announcement.setAttribute('aria-atomic', 'true');
  announcement.className = 'sr-only';
  announcement.textContent = message;
  announcement.style.position = 'absolute';
  announcement.style.left = '-10000px';
  announcement.style.width = '1px';
  announcement.style.height = '1px';
  announcement.style.overflow = 'hidden';

  document.body.appendChild(announcement);

  // Remove after screen reader has had time to announce
  setTimeout(() => {
    if (document.body.contains(announcement)) {
      document.body.removeChild(announcement);
    }
  }, 1000);
}

/**
 * Check if user prefers reduced motion
 *
 * Returns true if the user has enabled "reduce motion" in their system preferences.
 * Use this to disable or minimize animations for users with vestibular disorders.
 */
export function prefersReducedMotion(): boolean {
  if (typeof window === 'undefined') return false;
  return window.matchMedia('(prefers-reduced-motion: reduce)').matches;
}

/**
 * Trap focus within an element
 *
 * Useful for modals and dialogs to ensure keyboard users can't
 * tab out of the modal and into background content.
 *
 * @param element - The container element to trap focus within
 * @returns Cleanup function to remove the focus trap
 */
export function trapFocus(element: HTMLElement): () => void {
  const focusableElements = element.querySelectorAll<HTMLElement>(
    'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])',
  );

  const firstElement = focusableElements[0];
  const lastElement = focusableElements[focusableElements.length - 1];

  const handleKeyDown = (e: KeyboardEvent) => {
    if (e.key !== 'Tab') return;

    if (e.shiftKey) {
      // Shift + Tab: moving backwards
      if (document.activeElement === firstElement) {
        e.preventDefault();
        lastElement?.focus();
      }
    } else {
      // Tab: moving forwards
      if (document.activeElement === lastElement) {
        e.preventDefault();
        firstElement?.focus();
      }
    }
  };

  element.addEventListener('keydown', handleKeyDown);

  // Focus the first element
  firstElement?.focus();

  // Return cleanup function
  return () => {
    element.removeEventListener('keydown', handleKeyDown);
  };
}

/**
 * Get all focusable elements within a container
 *
 * @param container - The container to search within
 * @returns Array of focusable HTML elements
 */
export function getFocusableElements(
  container: HTMLElement,
): HTMLElement[] {
  const selector =
    'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';

  return Array.from(container.querySelectorAll<HTMLElement>(selector));
}

/**
 * Calculate relative luminance of a color
 *
 * Used for checking color contrast ratios per WCAG guidelines.
 *
 * @param rgb - RGB color values [r, g, b] (0-255)
 * @returns Relative luminance value (0-1)
 */
function getRelativeLuminance(rgb: [number, number, number]): number {
  const [r, g, b] = rgb.map((value) => {
    const sRGB = value / 255;
    return sRGB <= 0.03928
      ? sRGB / 12.92
      : Math.pow((sRGB + 0.055) / 1.055, 2.4);
  });

  return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}

/**
 * Parse hex color to RGB
 *
 * @param hex - Hex color string (#RRGGBB or #RGB)
 * @returns RGB values [r, g, b]
 */
function hexToRgb(hex: string): [number, number, number] | null {
  const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
  if (!result) {
    // Try 3-digit hex
    const shortResult = /^#?([a-f\d])([a-f\d])([a-f\d])$/i.exec(hex);
    if (!shortResult) return null;
    return [
      parseInt(shortResult[1] + shortResult[1], 16),
      parseInt(shortResult[2] + shortResult[2], 16),
      parseInt(shortResult[3] + shortResult[3], 16),
    ];
  }
  return [
    parseInt(result[1], 16),
    parseInt(result[2], 16),
    parseInt(result[3], 16),
  ];
}

/**
 * Get contrast ratio between two colors
 *
 * WCAG requirements:
 * - Normal text: 4.5:1 minimum (AA), 7:1 recommended (AAA)
 * - Large text (18pt+ or 14pt+ bold): 3:1 minimum (AA), 4.5:1 recommended (AAA)
 *
 * @param color1 - First color (hex format)
 * @param color2 - Second color (hex format)
 * @returns Contrast ratio (1-21)
 */
export function getContrastRatio(color1: string, color2: string): number | null {
  const rgb1 = hexToRgb(color1);
  const rgb2 = hexToRgb(color2);

  if (!rgb1 || !rgb2) return null;

  const l1 = getRelativeLuminance(rgb1);
  const l2 = getRelativeLuminance(rgb2);

  const lighter = Math.max(l1, l2);
  const darker = Math.min(l1, l2);

  return (lighter + 0.05) / (darker + 0.05);
}

/**
 * Check if color contrast meets WCAG AA standards
 *
 * @param foreground - Foreground color (hex)
 * @param background - Background color (hex)
 * @param isLargeText - Whether the text is large (18pt+ or 14pt+ bold)
 * @returns Object with pass/fail status and actual ratio
 */
export function meetsContrastRequirements(
  foreground: string,
  background: string,
  isLargeText: boolean = false,
): { passes: boolean; ratio: number | null; required: number } {
  const ratio = getContrastRatio(foreground, background);
  const required = isLargeText ? 3 : 4.5;

  return {
    passes: ratio !== null && ratio >= required,
    ratio,
    required,
  };
}

/**
 * Generate a unique ID for accessibility attributes
 *
 * Useful for linking labels to inputs, or descriptions to elements.
 *
 * @param prefix - Optional prefix for the ID
 * @returns Unique ID string
 */
export function generateA11yId(prefix: string = 'a11y'): string {
  return `${prefix}-${Math.random().toString(36).substr(2, 9)}`;
}

/**
 * Check if an element is visible and focusable
 *
 * @param element - The element to check
 * @returns true if element is visible and can receive focus
 */
export function isElementFocusable(element: HTMLElement): boolean {
  if (!element) return false;

  // Check if element is hidden
  if (element.offsetParent === null) return false;
  if (window.getComputedStyle(element).visibility === 'hidden') return false;
  if (window.getComputedStyle(element).display === 'none') return false;

  // Check if element can receive focus
  const tabindex = element.getAttribute('tabindex');
  if (tabindex && parseInt(tabindex) < 0) return false;

  return true;
}

/**
 * Focus an element with optional scroll behavior
 *
 * @param element - Element to focus
 * @param scrollIntoView - Whether to scroll element into view
 */
export function focusElement(
  element: HTMLElement | null,
  scrollIntoView: boolean = true,
): void {
  if (!element) return;

  element.focus({ preventScroll: !scrollIntoView });

  if (scrollIntoView) {
    element.scrollIntoView({
      behavior: prefersReducedMotion() ? 'auto' : 'smooth',
      block: 'nearest',
    });
  }
}
