/**
 * Global search command-palette dialog.
 *
 * Opens via Ctrl+K / Cmd+K or topbar trigger. Fetches live results from the
 * preview API and renders them grouped by category with keyboard navigation.
 */
import { warnOnce, resolveHost } from '../core/app-dom.js';
import { getJson } from '../core/app-http.js';
import { isEditableTarget } from '../core/app-form-utils.js';

const ICON_MAP = {
  'users': 'bi-person-badge',
  'tenants': 'bi-buildings',
  'departments': 'bi-diagram-3',
  'roles': 'bi-people',
  'permissions': 'bi-shield-lock',
  'scheduled-jobs': 'bi-calendar-check',
  'pages': 'bi-file-text',
  'mail-log': 'bi-envelope-paper',
  'docs': 'bi-journal-text',
  'hotkeys': 'bi-keyboard',
  'contacts': 'bi-person-lines-fill',
  'bookmarks': 'bi-bookmark',
  'api-audit': 'bi-journal-code',
  'system-audit': 'bi-journal-check',
};

export function initGlobalSearch(root = document, options = {}) {
  const host = resolveHost(root);
  const dialog = host.matches?.('[data-app-search-dialog]') ? host : host.querySelector('[data-app-search-dialog]');
  if (!dialog) {return { destroy: () => {} };}

  if (dialog.dataset.globalSearchBound === '1' && dialog._globalSearchApi) {
    return dialog._globalSearchApi;
  }
  dialog.dataset.globalSearchBound = '1';

  const input = dialog.querySelector('#search-dialog-input');
  const resultsEl = dialog.querySelector('[data-search-dialog-results]');
  const emptyStateEl = dialog.querySelector('[data-search-dialog-empty]');
  const loadingEl = dialog.querySelector('[data-search-dialog-loading]');
  const errorEl = dialog.querySelector('[data-search-dialog-error]');
  const footerEl = dialog.querySelector('[data-search-dialog-footer]');
  const viewAllLink = dialog.querySelector('[data-search-dialog-view-all]');
  const closeButton = dialog.querySelector('[data-app-search-dialog-close]');
  const triggerButton = document.querySelector('[data-app-search-trigger]');
  const triggerShortcutEl = document.querySelector('[data-search-trigger-shortcut]');

  const appBase = String(options.appBase || document.baseURI || window.location.origin).trim() || document.baseURI;
  const endpointAttr = dialog.getAttribute('data-search-endpoint') || 'admin/search/data';
  const resultsPageAttr = dialog.getAttribute('data-search-results-page') || 'search';
  const endpoint = new URL(endpointAttr, appBase);
  const resultsPage = new URL(resultsPageAttr, appBase);

  const minLength = 2;
  let pending = null;
  let activeIndex = -1;
  let flatItems = [];
  let abortController = null;

  const shortcutLabel = /(Mac|iPhone|iPad|iPod)/i.test(navigator.platform || navigator.userAgent)
    ? '⌘K'
    : 'Ctrl+K';

  if (triggerShortcutEl) {triggerShortcutEl.textContent = shortcutLabel;}

  // --- State management ---

  const setState = (state) => {
    if (emptyStateEl) {emptyStateEl.hidden = state !== 'empty';}
    if (loadingEl) {loadingEl.hidden = state !== 'loading';}
    if (errorEl) {errorEl.hidden = state !== 'error';}
    if (resultsEl) {resultsEl.hidden = state !== 'success';}
    if (footerEl) {footerEl.hidden = state !== 'success';}
  };

  const clearHighlight = () => {
    activeIndex = -1;
    resultsEl.querySelectorAll('[role="option"]').forEach((el) => {
      el.removeAttribute('aria-selected');
      el.classList.remove('app-search-dialog-item-active');
    });
    if (input) {input.removeAttribute('aria-activedescendant');}
  };

  const setHighlight = (index) => {
    clearHighlight();
    if (index < 0 || index >= flatItems.length) {return;}
    activeIndex = index;
    const item = flatItems[index];
    item.setAttribute('aria-selected', 'true');
    item.classList.add('app-search-dialog-item-active');
    item.scrollIntoView({ block: 'nearest' });
    if (input) {input.setAttribute('aria-activedescendant', item.id);}
  };

  // --- Rendering ---

  const iconForKey = (key) => ICON_MAP[key] || 'bi-search';

  const render = (categories, query) => {
    resultsEl.innerHTML = '';
    flatItems = [];
    let itemIndex = 0;
    let hasResults = false;

    categories.forEach((category) => {
      if (category.count === 0 && (!category.items || category.items.length === 0)) {return;}
      hasResults = true;

      const group = document.createElement('li');
      group.setAttribute('role', 'presentation');
      group.classList.add('app-search-dialog-category');

      const heading = document.createElement('div');
      heading.classList.add('app-search-dialog-category-heading');
      heading.textContent = category.label || category.key;
      group.appendChild(heading);

      const list = document.createElement('ul');
      list.classList.add('app-search-dialog-category-items');
      list.setAttribute('role', 'group');

      if (Array.isArray(category.items) && category.items.length > 0) {
        category.items.forEach((item) => {
          if (!item || !item.url) {return;}
          const li = document.createElement('li');
          li.setAttribute('role', 'option');
          li.setAttribute('id', `search-item-${itemIndex}`);
          li.classList.add('app-search-dialog-item');
          li.dataset.searchUrl = item.url;

          const link = document.createElement('a');
          link.href = item.url;
          link.tabIndex = -1;

          const itemIcon = item.icon || iconForKey(category.key);
          const icon = document.createElement('i');
          icon.className = `bi ${safeCssClass(itemIcon)} app-search-dialog-item-icon`;
          icon.setAttribute('aria-hidden', 'true');
          link.appendChild(icon);

          const textWrap = document.createElement('span');
          textWrap.classList.add('app-search-dialog-item-text');
          const titleEl = document.createElement('span');
          titleEl.classList.add('app-search-dialog-item-title');
          highlightText(titleEl, item.label || '', query);
          textWrap.appendChild(titleEl);
          if (item.subtitle) {
            const subtitleEl = document.createElement('span');
            subtitleEl.classList.add('app-search-dialog-item-subtitle');
            highlightText(subtitleEl, item.subtitle, query);
            textWrap.appendChild(subtitleEl);
          }
          link.appendChild(textWrap);

          li.appendChild(link);
          list.appendChild(li);

          flatItems.push(li);
          itemIndex++;
        });
      }

      if (category.url && category.count > (category.items?.length || 0)) {
        const viewLi = document.createElement('li');
        viewLi.classList.add('app-search-dialog-item', 'app-search-dialog-item-view-all');
        const viewLink = document.createElement('a');
        viewLink.href = category.url;
        viewLink.tabIndex = -1;

        const viewIcon = document.createElement('i');
        viewIcon.className = 'bi bi-arrow-right app-search-dialog-item-icon';
        viewIcon.setAttribute('aria-hidden', 'true');
        viewLink.appendChild(viewIcon);

        const viewText = document.createElement('span');
        viewText.classList.add('app-search-dialog-item-text');
        const viewTitle = document.createElement('span');
        viewTitle.classList.add('app-search-dialog-item-title');
        viewTitle.textContent = `${category.label || category.key} — ${category.count}`;
        viewText.appendChild(viewTitle);
        viewLink.appendChild(viewText);

        viewLi.appendChild(viewLink);
        list.appendChild(viewLi);
      }

      group.appendChild(list);

      resultsEl.appendChild(group);
    });

    if (hasResults) {
      setState('success');
      if (viewAllLink) {
        const url = new URL(resultsPage.toString());
        url.searchParams.set('search', query);
        viewAllLink.href = url.toString();
      }
    } else {
      setState('empty');
      if (emptyStateEl) {
        const p = emptyStateEl.querySelector('p');
        if (p) {
          p.textContent = emptyStateEl.dataset.noResultsText || '';
        }
      }
    }
    clearHighlight();
  };

  // --- Fetch ---

  const fetchResults = async (value) => {
    if (abortController) {abortController.abort();}
    abortController = new AbortController();
    const { signal } = abortController;

    setState('loading');
    const url = new URL(endpoint.toString());
    url.searchParams.set('q', value);

    try {
      const data = await getJson(url.toString(), { signal });
      if (Array.isArray(data)) {
        render(data, value);
      } else {
        warnOnce('FETCH_INVALID', 'Invalid response format for global search', { module: 'global-search', type: typeof data });
        setState('error');
      }
    } catch (err) {
      if (err.name === 'AbortError') {return;}
      warnOnce('FETCH_ERROR', 'Global search fetch error', { module: 'global-search', err, status: err?.status });
      setState('error');
    }
  };

  // --- Dialog open/close ---

  const openDialog = () => {
    if (dialog.open) {return;}
    dialog.showModal();
    setState('empty');
    resetEmptyState();
    window.requestAnimationFrame(() => {
      if (input) {
        input.focus();
        input.select();
      }
    });
    // If input already has a value, trigger search
    const currentValue = input ? input.value.trim() : '';
    if (currentValue.length >= minLength) {
      fetchResults(currentValue);
    }
  };

  const closeDialog = () => {
    if (!dialog.open) {return;}
    dialog.close();
    clearHighlight();
  };

  const resetEmptyState = () => {
    if (!emptyStateEl) {return;}
    const p = emptyStateEl.querySelector('p');
    if (p) {
      p.textContent = p.dataset.originalText || '';
    }
  };

  // Store original text for empty state
  if (emptyStateEl) {
    const p = emptyStateEl.querySelector('p');
    if (p) {p.dataset.originalText = p.textContent;}
  }

  // --- Navigation ---

  const navigateToItem = (index) => {
    if (index < 0 || index >= flatItems.length) {return;}
    const item = flatItems[index];
    const url = item.dataset.searchUrl || item.querySelector('a')?.href;
    if (url) {
      closeDialog();
      window.location.href = url;
    }
  };

  const submitSearch = () => {
    const value = input ? input.value.trim() : '';
    if (value.length < minLength) {return;}
    const url = new URL(resultsPage.toString());
    url.searchParams.set('search', value);
    closeDialog();
    window.location.href = url.toString();
  };

  // --- Event handlers ---

  const onInput = () => {
    const value = input.value.trim();
    if (value.length < minLength) {
      if (pending) {window.clearTimeout(pending); pending = null;}
      if (abortController) {abortController.abort(); abortController = null;}
      setState('empty');
      resetEmptyState();
      resultsEl.innerHTML = '';
      flatItems = [];
      clearHighlight();
      if (footerEl) {footerEl.hidden = true;}
      return;
    }
    if (pending) {window.clearTimeout(pending);}
    pending = window.setTimeout(() => {
      fetchResults(value).catch((err) => {
        warnOnce('FETCH_ERROR', 'Global search fetch error', { module: 'global-search', err });
        setState('error');
      });
    }, 300);
  };

  const onInputKeydown = (event) => {
    if (event.key === 'ArrowDown') {
      event.preventDefault();
      if (flatItems.length === 0) {return;}
      setHighlight(activeIndex < flatItems.length - 1 ? activeIndex + 1 : 0);
      return;
    }
    if (event.key === 'ArrowUp') {
      event.preventDefault();
      if (flatItems.length === 0) {return;}
      setHighlight(activeIndex > 0 ? activeIndex - 1 : flatItems.length - 1);
      return;
    }
    if (event.key === 'Enter') {
      event.preventDefault();
      if (activeIndex >= 0) {
        navigateToItem(activeIndex);
      } else {
        submitSearch();
      }
      return;
    }
  };

  const onDocumentKeydown = (event) => {
    if (!(event.metaKey || event.ctrlKey)) {return;}
    const key = event.key.toLowerCase();
    if (key !== 'k' && key !== '/') {return;}
    if (isEditableTarget(event.target) && event.target !== input) {return;}
    event.preventDefault();
    openDialog();
  };

  const onCloseClick = () => {
    closeDialog();
  };

  const onTriggerClick = () => {
    openDialog();
  };

  const onDialogClick = (event) => {
    // Close on backdrop click (click on <dialog> itself, not its children)
    if (event.target === dialog) {
      closeDialog();
    }
  };

  const onResultClick = (event) => {
    const item = event.target.closest('[role="option"]');
    if (!item) {return;}
    const link = item.querySelector('a');
    if (link && event.target !== link) {
      closeDialog();
      window.location.href = link.href;
      event.preventDefault();
    } else if (link) {
      closeDialog();
    }
  };

  const onFormSubmit = (event) => {
    event.preventDefault();
    if (activeIndex >= 0) {
      navigateToItem(activeIndex);
    } else {
      submitSearch();
    }
  };

  // --- Bind listeners ---

  if (input) {
    input.addEventListener('input', onInput);
    input.addEventListener('keydown', onInputKeydown);
  }
  document.addEventListener('keydown', onDocumentKeydown);
  if (closeButton) {closeButton.addEventListener('click', onCloseClick);}
  if (triggerButton) {triggerButton.addEventListener('click', onTriggerClick);}
  dialog.addEventListener('click', onDialogClick);
  if (resultsEl) {resultsEl.addEventListener('click', onResultClick);}

  const form = input ? input.closest('form') : null;
  if (form) {form.addEventListener('submit', onFormSubmit);}

  // --- Cleanup ---

  const destroy = () => {
    if (pending) {window.clearTimeout(pending);}
    if (abortController) {abortController.abort();}
    if (input) {
      input.removeEventListener('input', onInput);
      input.removeEventListener('keydown', onInputKeydown);
    }
    document.removeEventListener('keydown', onDocumentKeydown);
    if (closeButton) {closeButton.removeEventListener('click', onCloseClick);}
    if (triggerButton) {triggerButton.removeEventListener('click', onTriggerClick);}
    dialog.removeEventListener('click', onDialogClick);
    if (resultsEl) {resultsEl.removeEventListener('click', onResultClick);}
    if (form) {form.removeEventListener('submit', onFormSubmit);}
    delete dialog.dataset.globalSearchBound;
    delete dialog._globalSearchApi;
  };

  const api = { destroy, openDialog, closeDialog };
  dialog._globalSearchApi = api;
  return api;
}

function escapeHtml(text) {
  const div = document.createElement('div');
  div.appendChild(document.createTextNode(text));
  return div.innerHTML;
}

function safeCssClass(value) {
  return String(value).replace(/[^a-z0-9_-]/gi, '');
}

function highlightText(el, text, query) {
  if (!query || query.length < 2) {
    el.textContent = text;
    return;
  }
  const escaped = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
  const parts = text.split(new RegExp(`(${escaped})`, 'gi'));
  if (parts.length === 1) {
    el.textContent = text;
    return;
  }
  const queryLower = query.toLowerCase();
  parts.forEach((part) => {
    if (part.toLowerCase() === queryLower) {
      const mark = document.createElement('mark');
      mark.textContent = part;
      el.appendChild(mark);
    } else if (part !== '') {
      el.appendChild(document.createTextNode(part));
    }
  });
}
