/**
 * Generic autocomplete lookup field.
 */
import { getJson } from '../core/app-http.js';
import { resolveHost } from '../core/app-dom.js';

const EMPTY_API = Object.freeze({ destroy: () => {} });

/**
 * @param {HTMLElement} container
 */
export function initLookupField(container) {
  if (!(container instanceof HTMLElement)) {
    return EMPTY_API;
  }

  const url = container.dataset.lookupUrl || '';
  const minChars = parseInt(container.dataset.lookupMinChars || '2', 10);
  const debounceMs = parseInt(container.dataset.lookupDebounce || '300', 10);
  const valueKey = container.dataset.lookupValueKey || 'value';
  const displayKey = container.dataset.lookupDisplayKey || 'label';
  const placeholder = container.dataset.lookupPlaceholder || '';
  const initialDisplay = container.dataset.lookupInitialDisplay || '';
  const initialValue = container.dataset.lookupInitialValue || '';

  if (!url) {
    return EMPTY_API;
  }

  const hiddenValue = container.querySelector('[data-lookup-value]');
  const hiddenDisplay = container.querySelector('[data-lookup-display-value]');

  const listenerController = new AbortController();
  const on = (target, type, handler, options = {}) => {
    if (!target || typeof target.addEventListener !== 'function') {
      return;
    }
    target.addEventListener(type, handler, { ...options, signal: listenerController.signal });
  };

  const cleanupNodes = [];
  const clearNode = (el) => {
    while (el.firstChild) {
      el.removeChild(el.firstChild);
    }
  };

  container.classList.add('app-lookup-field');

  const wrap = document.createElement('div');
  wrap.className = 'app-lookup-field-input-wrap';

  const input = document.createElement('input');
  input.type = 'text';
  input.className = 'app-lookup-field-input';
  input.placeholder = placeholder;
  input.setAttribute('role', 'combobox');
  input.setAttribute('aria-expanded', 'false');
  input.setAttribute('aria-autocomplete', 'list');
  input.setAttribute('autocomplete', 'off');

  const indicator = document.createElement('div');
  indicator.className = 'app-lookup-field-indicator';

  wrap.appendChild(input);
  wrap.appendChild(indicator);

  const dropdown = document.createElement('ul');
  dropdown.className = 'app-lookup-field-dropdown';
  dropdown.setAttribute('role', 'listbox');
  dropdown.hidden = true;

  container.appendChild(wrap);
  container.appendChild(dropdown);
  cleanupNodes.push(wrap, dropdown);

  let results = [];
  let activeIndex = -1;
  let debounceTimer = null;
  let blurTimer = null;
  let selected = false;
  let activeRequestController = null;

  if (initialValue && initialDisplay) {
    input.value = initialDisplay;
    selected = true;
    input.dataset.lookupSelected = '';
    showClear();
  }

  function showSpinner() {
    clearNode(indicator);
    const spinner = document.createElement('div');
    spinner.className = 'app-lookup-field-spinner';
    indicator.appendChild(spinner);
  }

  function showClear() {
    clearNode(indicator);
    const button = document.createElement('button');
    button.type = 'button';
    button.className = 'app-lookup-field-clear';
    button.setAttribute('aria-label', 'Clear');
    const icon = document.createElement('i');
    icon.className = 'bi bi-x-lg';
    button.appendChild(icon);
    on(button, 'click', clearSelection);
    indicator.appendChild(button);
  }

  function hideIndicator() {
    clearNode(indicator);
  }

  function openDropdown() {
    dropdown.hidden = false;
    input.setAttribute('aria-expanded', 'true');
  }

  function closeDropdown() {
    dropdown.hidden = true;
    input.setAttribute('aria-expanded', 'false');
    activeIndex = -1;
  }

  function setActive(index) {
    const options = dropdown.querySelectorAll('.app-lookup-field-option');
    options.forEach((opt, i) => {
      opt.setAttribute('aria-selected', i === index ? 'true' : 'false');
    });
    activeIndex = index;

    const active = options[index];
    if (active) {
      active.scrollIntoView({ block: 'nearest' });
    }
  }

  function selectItem(index) {
    const item = results[index];
    if (!item) {
      return;
    }

    input.value = item[displayKey] || '';
    input.dataset.lookupSelected = '';
    selected = true;

    if (hiddenValue) hiddenValue.value = item[valueKey] || '';
    if (hiddenDisplay) hiddenDisplay.value = item[displayKey] || '';

    closeDropdown();
    showClear();

    container.dispatchEvent(new CustomEvent('lookup:select', {
      detail: item,
      bubbles: true,
    }));
  }

  function clearSelection() {
    input.value = '';
    delete input.dataset.lookupSelected;
    selected = false;

    if (hiddenValue) hiddenValue.value = '';
    if (hiddenDisplay) hiddenDisplay.value = '';

    hideIndicator();
    input.focus();

    container.dispatchEvent(new CustomEvent('lookup:clear', { bubbles: true }));
  }

  function renderResults(items) {
    results = items;
    activeIndex = -1;
    clearNode(dropdown);

    if (items.length === 0) {
      const msg = document.createElement('li');
      msg.className = 'app-lookup-field-message';
      msg.textContent = container.dataset.lookupEmptyText || 'No results';
      dropdown.appendChild(msg);
      openDropdown();
      return;
    }

    items.forEach((item, index) => {
      const li = document.createElement('li');
      li.className = 'app-lookup-field-option';
      li.setAttribute('role', 'option');
      li.setAttribute('aria-selected', 'false');
      li.dataset.index = String(index);

      const label = document.createElement('span');
      label.className = 'app-lookup-field-option-label';
      label.textContent = item[displayKey] || '';
      li.appendChild(label);

      if (item.meta) {
        const meta = document.createElement('span');
        meta.className = 'app-lookup-field-option-meta';
        meta.textContent = item.meta;
        li.appendChild(meta);
      }

      on(li, 'mousedown', (event) => {
        event.preventDefault();
        selectItem(index);
      });
      on(li, 'mouseenter', () => {
        setActive(index);
      });

      dropdown.appendChild(li);
    });

    openDropdown();
  }

  function renderError() {
    clearNode(dropdown);
    const msg = document.createElement('li');
    msg.className = 'app-lookup-field-message';
    msg.textContent = container.dataset.lookupErrorText || 'Search failed';
    dropdown.appendChild(msg);
    openDropdown();
  }

  async function fetchResults(query) {
    if (activeRequestController) {
      activeRequestController.abort();
    }
    activeRequestController = new AbortController();

    showSpinner();

    try {
      const separator = url.includes('?') ? '&' : '?';
      const data = await getJson(`${url}${separator}q=${encodeURIComponent(query)}`, {
        signal: activeRequestController.signal,
      });
      hideIndicator();
      const items = Array.isArray(data) ? data : (data?.data || []);
      renderResults(items);
    } catch (error) {
      if (error instanceof Error && error.name === 'AbortError') {
        return;
      }
      hideIndicator();
      renderError();
    }
  }

  on(input, 'input', () => {
    if (selected) {
      selected = false;
      delete input.dataset.lookupSelected;
      if (hiddenValue) hiddenValue.value = '';
      if (hiddenDisplay) hiddenDisplay.value = '';
    }

    const query = input.value.trim();
    if (query.length < minChars) {
      closeDropdown();
      hideIndicator();
      return;
    }

    window.clearTimeout(debounceTimer);
    debounceTimer = window.setTimeout(() => {
      void fetchResults(query);
    }, debounceMs);
  });

  on(input, 'keydown', (event) => {
    if (dropdown.hidden) return;

    const options = dropdown.querySelectorAll('.app-lookup-field-option');
    if (!options.length) return;

    if (event.key === 'ArrowDown') {
      event.preventDefault();
      setActive(activeIndex < options.length - 1 ? activeIndex + 1 : 0);
    } else if (event.key === 'ArrowUp') {
      event.preventDefault();
      setActive(activeIndex > 0 ? activeIndex - 1 : options.length - 1);
    } else if (event.key === 'Enter') {
      event.preventDefault();
      if (activeIndex >= 0) {
        selectItem(activeIndex);
      }
    } else if (event.key === 'Escape') {
      event.preventDefault();
      closeDropdown();
    }
  });

  on(input, 'focus', () => {
    if (input.value.trim().length >= minChars && !selected && results.length > 0) {
      openDropdown();
    }
  });

  on(input, 'blur', () => {
    window.clearTimeout(blurTimer);
    blurTimer = window.setTimeout(() => closeDropdown(), 150);
  });

  return {
    input,
    clearSelection,
    destroy: () => {
      listenerController.abort();
      if (activeRequestController) {
        activeRequestController.abort();
      }
      window.clearTimeout(debounceTimer);
      window.clearTimeout(blurTimer);
      cleanupNodes.forEach((node) => {
        if (node.parentNode === container) {
          container.removeChild(node);
        }
      });
      container.classList.remove('app-lookup-field');
      delete container.dataset.lookupInitialized;
    },
  };
}

export function initLookupFields(root = document, options = {}) {
  const host = resolveHost(root);
  const selector = String(options.selector || '[data-app-lookup]').trim() || '[data-app-lookup]';
  const elements = Array.from(host.querySelectorAll(selector));
  if (elements.length === 0) {
    return EMPTY_API;
  }

  const instances = [];
  elements.forEach((el) => {
    if (el.dataset.lookupInitialized) {
      return;
    }
    el.dataset.lookupInitialized = '1';
    instances.push(initLookupField(el));
  });

  return {
    destroy: () => {
      instances.forEach((instance) => {
        if (instance && typeof instance.destroy === 'function') {
          instance.destroy();
        }
      });
    },
  };
}
