import { warnOnce } from '../core/app-dom.js';

/**
 * Async Flash Messages & Loading State
 *
 * Renders flash messages into the .app-toast-stack for JS-triggered
 * notifications. The stack container is rendered server-side by
 * templates/partials/app-toast-stack.phtml (mounted once in default.phtml);
 * this module only appends/removes toasts inside it.
 *
 * Provides loading cursor state management.
 */

/**
 * Set global loading state (cursor + pointer-events)
 * @param {boolean} loading - Whether to show loading state
 */
export function setLoading(loading) {
  if (loading) {
    document.body.classList.add('is-loading');
  } else {
    document.body.classList.remove('is-loading');
  }
}

/**
 * Execute an async function with loading state
 * @param {Function} fn - Async function to execute
 * @returns {Promise<any>} - Result of the function
 */
export async function withLoading(fn) {
  setLoading(true);
  try {
    return await fn();
  } finally {
    setLoading(false);
  }
}

const defaultTimeouts = {
  success: 5000,
  info: 6000,
  warning: 7000,
  error: 0,
};

const MAX_VISIBLE_TOASTS = 5;

const VARIANT_ICONS = {
  success: 'bi-check-circle-fill',
  error: 'bi-x-octagon-fill',
  warning: 'bi-exclamation-triangle-fill',
  info: 'bi-info-circle-fill',
};

/**
 * Resolve the server-rendered toast stack container.
 * Returns null if the partial is missing — callers must handle that case.
 * @returns {HTMLElement|null}
 */
function getToastStack() {
  const stack = document.querySelector('.app-toast-stack');
  return stack instanceof HTMLElement ? stack : null;
}

/**
 * Dismiss a toast with slide-out animation.
 * @param {HTMLElement} notice
 */
function dismissToast(notice) {
  const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
  if (prefersReducedMotion) {
    notice.remove();
    return;
  }
  notice.classList.add('toast-dismissing');
  notice.addEventListener('animationend', () => notice.remove(), { once: true });
  // Fallback removal if animation doesn't fire
  setTimeout(() => { if (notice.parentNode) notice.remove(); }, 300);
}

/**
 * Enforce max visible toasts — dismiss oldest when limit exceeded.
 * @param {HTMLElement} stack
 */
function enforceMaxVisible(stack) {
  const notices = stack.querySelectorAll('.notice:not(.toast-dismissing)');
  if (notices.length > MAX_VISIBLE_TOASTS) {
    for (let i = 0; i < notices.length - MAX_VISIBLE_TOASTS; i++) {
      dismissToast(notices[i]);
    }
  }
}

/**
 * Show an async flash message
 * @param {string} type - Message type: 'success', 'info', 'warning', 'error'
 * @param {string} message - The message text
 * @param {number} [timeout] - Auto-dismiss timeout in ms (0 = no auto-dismiss)
 * @returns {HTMLElement|null}
 */
export function showAsyncFlash(type, message, timeout) {
  const stack = getToastStack();
  if (!stack) {
    warnOnce('UI_EL_MISSING', 'Missing .app-toast-stack — partial not mounted?', {
      module: 'async-flash',
    });
    return null;
  }

  const effectiveTimeout = timeout ?? defaultTimeouts[type] ?? 5000;

  const notice = document.createElement('div');
  notice.className = 'notice';
  notice.setAttribute('data-variant', type);
  const isAssertive = type === 'error' || type === 'warning';
  notice.setAttribute('role', isAssertive ? 'alert' : 'status');
  notice.setAttribute('aria-live', isAssertive ? 'assertive' : 'polite');
  notice.setAttribute('aria-atomic', 'true');

  const iconSpan = document.createElement('span');
  iconSpan.className = 'notice-icon';
  iconSpan.setAttribute('aria-hidden', 'true');
  const iconElement = document.createElement('i');
  iconElement.className = `bi ${VARIANT_ICONS[type] || VARIANT_ICONS.info}`;
  iconSpan.appendChild(iconElement);
  notice.appendChild(iconSpan);

  const messageSpan = document.createElement('span');
  messageSpan.className = 'notice-content';
  messageSpan.textContent = message;
  notice.appendChild(messageSpan);

  const closeButton = document.createElement('button');
  closeButton.type = 'button';
  closeButton.className = 'notice-dismiss';
  closeButton.setAttribute('aria-label', 'Dismiss');
  const closeIcon = document.createElement('i');
  closeIcon.className = 'bi bi-x-lg';
  closeButton.appendChild(closeIcon);
  closeButton.addEventListener('click', () => dismissToast(notice));
  notice.appendChild(closeButton);

  stack.appendChild(notice);
  enforceMaxVisible(stack);

  if (effectiveTimeout > 0) {
    notice.style.setProperty('--flash-timeout', `${effectiveTimeout}ms`);
    notice.classList.add('flash-timed');

    let timer = setTimeout(() => dismissToast(notice), effectiveTimeout);

    // Pause timer on hover
    notice.addEventListener('mouseenter', () => {
      clearTimeout(timer);
    });
    notice.addEventListener('mouseleave', () => {
      timer = setTimeout(() => dismissToast(notice), 1000);
    });
  }

  return notice;
}

/**
 * Clear all async flash messages
 */
export function clearAsyncFlash() {
  const stack = document.querySelector('.app-toast-stack');
  if (stack) {
    stack.querySelectorAll('.notice').forEach((n) => dismissToast(n));
  }
}
