/**
 * Initializes MultiSelect.js instances from select[multiple] elements.
 */
import { warnOnce, resolveHost } from '../core/app-dom.js';

let multiSelectLoader = null;

const resolveScriptUrl = (script) => {
  if (script) {
    return script;
  }
  const root = document.documentElement;
  const assetBase = String(root?.dataset?.assetBase || '').trim() || document.baseURI;
  return new URL('vendor/multi-select/MultiSelect.js', assetBase).toString();
};

const ensureMultiSelect = async (script) => {
  if (typeof window.MultiSelect !== 'undefined') {
    return window.MultiSelect;
  }
  if (!multiSelectLoader) {
    multiSelectLoader = new Promise((resolve, reject) => {
      const tag = document.createElement('script');
      tag.src = resolveScriptUrl(script);
      tag.async = true;
      tag.onload = () => resolve(window.MultiSelect);
      tag.onerror = (event) => reject(event);
      document.head.appendChild(tag);
    });
  }
  return multiSelectLoader;
};

const resolveElements = (target, host) => {
  if (typeof target === 'string') {
    return Array.from(host.querySelectorAll(target));
  }
  if (target instanceof Element) {
    return [target];
  }
  if (target && typeof target.length === 'number') {
    return Array.from(target);
  }
  return [];
};

const destroyElementInstance = (element, instance = null) => {
  if (!(element instanceof HTMLElement)) {
    return;
  }
  const cleanup = element._multiSelectCleanup;
  if (typeof cleanup === 'function') {
    cleanup();
  }
  delete element._multiSelectCleanup;
  delete element.dataset.multiSelectInit;

  const resolvedInstance = instance || element._multiSelectInstance || null;
  if (resolvedInstance && typeof resolvedInstance.destroy === 'function') {
    resolvedInstance.destroy();
  }
  delete element._multiSelectInstance;
};

export const initMultiSelect = async (target = '[data-multi-select]', root = document) => {
  const host = resolveHost(root);
  const elements = resolveElements(target, host);
  if (!elements.length) {
    return [];
  }

  const MultiSelect = await ensureMultiSelect();
  const instances = [];
  elements.forEach((element) => {
    if (!(element instanceof HTMLElement) || element.dataset.multiSelectInit === '1') {
      return;
    }

    const cleanupFns = [];
    const syncTarget = element.dataset.syncTarget;
    const syncInput = syncTarget ? document.querySelector(syncTarget) : null;
    const options = {};
    const placeholder = element.dataset.placeholder;
    if (placeholder) {
      options.placeholder = placeholder;
    }
    if (element.dataset.search !== undefined) {
      options.search = element.dataset.search === 'true';
    }
    if (element.dataset.selectAll !== undefined) {
      options.selectAll = element.dataset.selectAll === 'true';
    }
    if (element.dataset.disabled !== undefined) {
      options.disabled = element.dataset.disabled === 'true';
    }
    if (element.disabled) {
      options.disabled = true;
    }
    if (element.dataset.listAll !== undefined) {
      options.listAll = element.dataset.listAll === 'true';
    }
    if (element.dataset.selectedLabel) {
      options.selectedLabel = element.dataset.selectedLabel;
    }
    if (element.dataset.searchPlaceholder) {
      options.searchPlaceholder = element.dataset.searchPlaceholder;
    }
    if (element.dataset.selectAllLabel) {
      options.selectAllLabel = element.dataset.selectAllLabel;
    }

    let isSyncing = false;
    let instance;
    try {
      instance = new MultiSelect(element, options);
    } catch (error) {
      warnOnce('UI_INIT_FAIL', 'multi-select init failed', { module: 'multi-select', element, error });
      return;
    }

    if (element.dataset.disabled === 'true' || element.disabled) {
      instance.disable();
    }
    element.dataset.multiSelectInit = '1';
    element._multiSelectInstance = instance;
    instances.push(instance);

    if (syncInput instanceof HTMLInputElement) {
      syncInput._multiSelectInstance = instance;
      const syncValues = () => {
        if (isSyncing) {
          return;
        }
        isSyncing = true;
        const values = instance.selectedValues || [];
        syncInput.value = Array.isArray(values) ? values.join(',') : '';
        syncInput.dispatchEvent(new Event('change', { bubbles: true }));
        isSyncing = false;
      };
      let boundElement = null;
      const bindSyncListener = () => {
        if (!instance?.element || instance.element === boundElement) {
          return;
        }
        instance.element.addEventListener('multi-select:change', syncValues);
        cleanupFns.push(() => instance.element?.removeEventListener('multi-select:change', syncValues));
        boundElement = instance.element;
      };
      bindSyncListener();

      if (typeof instance.refresh === 'function' && instance._syncRefreshWrapped !== true) {
        const originalRefresh = instance.refresh.bind(instance);
        instance.refresh = (...args) => {
          const result = originalRefresh(...args);
          bindSyncListener();
          return result;
        };
        instance._syncRefreshWrapped = true;
      }

      const values = instance.selectedValues || [];
      syncInput.value = Array.isArray(values) ? values.join(',') : '';
      const onSyncInputChange = () => {
        if (isSyncing) {
          return;
        }
        const nextValues = syncInput.value
          ? syncInput.value.split(',').map((item) => item.trim()).filter(Boolean)
          : [];
        instance.setValues(nextValues);
      };
      syncInput.addEventListener('change', onSyncInputChange);
      cleanupFns.push(() => syncInput.removeEventListener('change', onSyncInputChange));
    }

    element._multiSelectCleanup = () => {
      cleanupFns.forEach((cleanup) => cleanup());
      cleanupFns.length = 0;
      if (syncInput instanceof HTMLInputElement && syncInput._multiSelectInstance === instance) {
        delete syncInput._multiSelectInstance;
      }
    };
  });

  return instances;
};

export const getMultiSelectInstance = (target, root = document) => {
  const host = resolveHost(root);
  const element = typeof target === 'string' ? host.querySelector(target) : target;
  if (!element) {
    return null;
  }
  if (element._multiSelectInstance) {
    return element._multiSelectInstance;
  }
  const syncTarget = element.dataset?.syncTarget;
  if (syncTarget) {
    const syncElement = document.querySelector(syncTarget);
    if (syncElement && syncElement._multiSelectInstance) {
      return syncElement._multiSelectInstance;
    }
  }
  return null;
};

export function initMultiSelectComponent(root = document, options = {}) {
  const selector = String(options.selector || '[data-multi-select]').trim() || '[data-multi-select]';
  const host = resolveHost(root);
  let destroyed = false;
  let instances = [];

  initMultiSelect(selector, host)
    .then((initialized) => {
      if (destroyed) {
        initialized.forEach((instance) => {
          const element = instance?.element;
          if (element instanceof HTMLElement) {
            destroyElementInstance(element, instance);
          }
        });
        return;
      }
      instances = initialized;
    })
    .catch((error) => {
      warnOnce('UI_INIT_FAIL', 'multi-select runtime init failed', { module: 'multi-select', error });
    });

  return {
    destroy: () => {
      destroyed = true;
      instances.forEach((instance) => {
        const element = instance?.element;
        if (element instanceof HTMLElement) {
          destroyElementInstance(element, instance);
        }
      });
      instances = [];
    },
  };
}
