/**
 * Initializes tab navigation with chevron-based overflow scrolling.
 *
 * Markup (author writes):
 *   <div data-app-component="tabs" data-tabs>
 *     <div class="app-tabs-nav">
 *       <button data-tab="system" data-tab-default>System</button>
 *       ...
 *     </div>
 *     <div data-tab-panel="system">...</div>
 *   </div>
 *
 * JS wraps the nav in `.app-tabs-nav-wrap` and injects chevron buttons when
 * the strip overflows. No-JS fallback: native horizontal scrollbar stays.
 */
import { createUiStorage } from '../core/app-ui-storage.js';
import { resolveHost } from '../core/app-dom.js';
import { belongsToForm } from '../core/app-form-utils.js';

const SCROLL_STEP = 160; // px per click
const SCROLL_THRESHOLD = 2; // tolerance for "at edge" detection

const resolveContainers = (host, selector) => {
  const containers = [];
  if (host instanceof HTMLElement && host.matches(selector)) {
    containers.push(host);
  }
  containers.push(...Array.from(host.querySelectorAll(selector)));
  return containers;
};

export function initTabs(root = document, config = {}) {
  const host = resolveHost(root);
  const selector = String(config.selector || '[data-app-component="tabs"]').trim() || '[data-app-component="tabs"]';
  const storage = createUiStorage({
    namespace: config.storageNamespace || 'app-ui',
    version: config.storageVersion || 'v1',
    scope: 'tabs',
  });
  const containers = resolveContainers(host, selector);
  const instances = [];

  containers.forEach((container) => {
    if (!(container instanceof HTMLElement)) {
      return;
    }
    if (container.dataset.tabsBound === '1' && container._tabsApi) {
      instances.push(container._tabsApi);
      return;
    }

    const cleanupFns = [];
    const addCleanup = (cleanup) => {
      if (typeof cleanup === 'function') {
        cleanupFns.push(cleanup);
      }
    };

    container.dataset.tabsBound = '1';
    container.dataset.tabsReady = '0';

    const tabs = Array.from(container.querySelectorAll('[data-tab]'));
    const panels = Array.from(container.querySelectorAll('[data-tab-panel]'));

    if (!tabs.length || !panels.length) {
      const api = {
        destroy: () => {
          delete container.dataset.tabsBound;
          delete container.dataset.tabsReady;
          delete container._tabsApi;
        },
      };
      container._tabsApi = api;
      instances.push(api);
      return;
    }

    const storageKey = String(container.dataset.tabsStorageKey || (container.id ? `tabs:${container.id}` : '')).trim();
    const scopedStorageKey = storage.buildKey('state', storageKey);
    const urlParamName = String(container.dataset.tabsParam || config.urlParam || 'tab').trim() || 'tab';

    const getUrlParam = (name) => {
      const params = new URLSearchParams(window.location.search);
      return params.get(name);
    };

    const setUrlParam = (name, value) => {
      const url = new URL(window.location.href);
      url.searchParams.set(name, value);
      window.history.replaceState({}, '', url);
    };

    const readStoredTab = () => {
      if (storageKey === '' || container.hasAttribute('data-tabs-ignore-storage')) {
        return '';
      }
      const scopedValue = scopedStorageKey ? storage.getItem(scopedStorageKey) : null;
      if (typeof scopedValue === 'string' && scopedValue.trim() !== '') {
        return scopedValue.trim();
      }
      return '';
    };

    const persistTab = (tabName) => {
      if (storageKey === '' || container.hasAttribute('data-tabs-ignore-storage')) {
        return;
      }
      if (scopedStorageKey) {
        storage.setItem(scopedStorageKey, tabName);
      }
    };

    let activeTab = null;
    const urlTab = getUrlParam(urlParamName);
    if (urlTab && panels.some((panel) => panel.dataset.tabPanel === urlTab)) {
      activeTab = urlTab;
    } else {
      const storedTab = readStoredTab();
      if (storedTab && panels.some((panel) => panel.dataset.tabPanel === storedTab)) {
        activeTab = storedTab;
      }
    }
    if (!activeTab) {
      const defaultTab = container.querySelector('[data-tab-default]');
      activeTab = defaultTab?.dataset.tab || tabs[0]?.dataset.tab || null;
    }

    const nav = container.querySelector('.app-tabs-nav');
    const tabIdPrefix = container.id || `tabs-${Math.random().toString(36).slice(2, 8)}`;

    const scrollTabIntoView = (tabEl) => {
      if (!nav || !(tabEl instanceof HTMLElement)) {
        return;
      }
      tabEl.scrollIntoView({ block: 'nearest', inline: 'nearest', behavior: 'smooth' });
    };

    const activateTab = (tabName) => {
      tabs.forEach((tab) => {
        const isActive = tab.dataset.tab === tabName;
        tab.classList.toggle('is-active', isActive);
        tab.setAttribute('aria-selected', isActive ? 'true' : 'false');
        tab.setAttribute('tabindex', isActive ? '0' : '-1');
      });

      panels.forEach((panel) => {
        const isVisible = panel.dataset.tabPanel === tabName;
        panel.hidden = !isVisible;
        panel.setAttribute('aria-hidden', isVisible ? 'false' : 'true');
      });

      setUrlParam(urlParamName, tabName);
      persistTab(tabName);
    };

    tabs.forEach((tab) => {
      const tabName = tab.dataset.tab;
      const tabId = `${tabIdPrefix}-tab-${tabName}`;
      const panelId = `${tabIdPrefix}-panel-${tabName}`;
      tab.setAttribute('role', 'tab');
      tab.id = tabId;
      tab.setAttribute('aria-controls', panelId);

      const onClick = (event) => {
        event.preventDefault();
        activateTab(tab.dataset.tab);
        scrollTabIntoView(tab);
      };
      tab.addEventListener('click', onClick);
      addCleanup(() => tab.removeEventListener('click', onClick));
    });

    panels.forEach((panel) => {
      const panelName = panel.dataset.tabPanel;
      const panelId = `${tabIdPrefix}-panel-${panelName}`;
      const tabId = `${tabIdPrefix}-tab-${panelName}`;
      panel.setAttribute('role', 'tabpanel');
      panel.id = panelId;
      panel.setAttribute('aria-labelledby', tabId);
    });

    if (nav) {
      nav.setAttribute('role', 'tablist');
      const onNavKeyDown = (event) => {
        const currentIndex = tabs.indexOf(event.target);
        if (currentIndex === -1) {
          return;
        }

        let nextIndex = -1;
        if (event.key === 'ArrowRight') {
          nextIndex = (currentIndex + 1) % tabs.length;
        } else if (event.key === 'ArrowLeft') {
          nextIndex = (currentIndex - 1 + tabs.length) % tabs.length;
        } else if (event.key === 'Home') {
          nextIndex = 0;
        } else if (event.key === 'End') {
          nextIndex = tabs.length - 1;
        }

        if (nextIndex >= 0) {
          event.preventDefault();
          tabs[nextIndex].focus();
          activateTab(tabs[nextIndex].dataset.tab);
          scrollTabIntoView(tabs[nextIndex]);
        }
      };
      nav.addEventListener('keydown', onNavKeyDown);
      addCleanup(() => nav.removeEventListener('keydown', onNavKeyDown));

      const destroyChevron = initChevronScroll(nav);
      addCleanup(destroyChevron);
    }

    if (activeTab) {
      activateTab(activeTab);
    }
    container.dataset.tabsReady = '1';

    const form = container.closest('form');
    if (form instanceof HTMLFormElement) {
      const formId = String(form.id || '').trim();
      const inputSelector = 'input,select,textarea';

      const clearInvalidMarkers = () => {
        tabs.forEach((tab) => tab.classList.remove('has-invalid'));
      };

      const findInvalidPanels = () => {
        clearInvalidMarkers();
        let firstInvalidTab = null;

        panels.forEach((panel) => {
          const fields = panel.querySelectorAll(inputSelector);
          const hasInvalid = Array.from(fields).some(
            (field) => belongsToForm(field, form) && !field.disabled && !field.checkValidity()
          );
          if (!hasInvalid) {
            return;
          }
          const tabName = panel.dataset.tabPanel;
          const tab = tabs.find((candidate) => candidate.dataset.tab === tabName);
          if (tab) {
            tab.classList.add('has-invalid');
          }
          if (!firstInvalidTab) {
            firstInvalidTab = tabName;
          }
        });

        return firstInvalidTab;
      };

      const onFormClickCapture = (event) => {
        const target = event.target;
        if (!(target instanceof Element)) {
          return;
        }

        const trigger = target.closest('button, [type="submit"]');
        if (!trigger) {
          return;
        }

        const triggerFormAttr = trigger.getAttribute('form');
        const isInternalSubmit = !triggerFormAttr
          && form.contains(trigger)
          && (trigger.type === 'submit' || (!trigger.type && trigger.tagName === 'BUTTON'));
        const isExternalSubmit = Boolean(triggerFormAttr) && Boolean(formId) && triggerFormAttr === formId && trigger.type === 'submit';
        if (!isInternalSubmit && !isExternalSubmit) {
          return;
        }

        const firstInvalidTab = findInvalidPanels();
        if (!firstInvalidTab) {
          return;
        }

        const activePanel = panels.find((panel) => !panel.hidden);
        const needsTabSwitch = activePanel?.dataset.tabPanel !== firstInvalidTab;

        const invalidFields = form.querySelectorAll(inputSelector);
        for (const field of invalidFields) {
          if (!belongsToForm(field, form) || field.disabled || field.checkValidity()) {
            continue;
          }
          let element = field.parentElement;
          while (element && element !== form) {
            if (element.tagName === 'DETAILS' && !element.open) {
              element.open = true;
            }
            element = element.parentElement;
          }
        }

        if (!needsTabSwitch) {
          return;
        }

        event.preventDefault();
        activateTab(firstInvalidTab);
        requestAnimationFrame(() => {
          form.reportValidity();
        });
      };

      const onFormInput = (event) => {
        const panel = event.target.closest('[data-tab-panel]');
        if (!panel) {
          return;
        }
        const tabName = panel.dataset.tabPanel;
        const tab = tabs.find((candidate) => candidate.dataset.tab === tabName);
        if (!tab) {
          return;
        }
        const fields = panel.querySelectorAll(inputSelector);
        if (Array.from(fields).every((field) => !belongsToForm(field, form) || field.disabled || field.checkValidity())) {
          tab.classList.remove('has-invalid');
        }
      };

      document.addEventListener('click', onFormClickCapture, true);
      form.addEventListener('input', onFormInput);
      form.addEventListener('change', onFormInput);
      addCleanup(() => {
        document.removeEventListener('click', onFormClickCapture, true);
        form.removeEventListener('input', onFormInput);
        form.removeEventListener('change', onFormInput);
      });
    }

    const onPopState = () => {
      const nextTab = getUrlParam(urlParamName);
      if (nextTab && panels.some((panel) => panel.dataset.tabPanel === nextTab)) {
        activateTab(nextTab);
      }
    };
    window.addEventListener('popstate', onPopState);
    addCleanup(() => window.removeEventListener('popstate', onPopState));

    const destroy = () => {
      cleanupFns.forEach((cleanup) => cleanup());
      cleanupFns.length = 0;
      delete container.dataset.tabsBound;
      delete container.dataset.tabsReady;
      delete container._tabsApi;
    };

    const api = { activateTab, destroy };
    container._tabsApi = api;
    instances.push(api);
  });

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

function initChevronScroll(nav) {
  if (!(nav instanceof HTMLElement) || !nav.parentNode) {
    return () => {};
  }

  const wrap = document.createElement('div');
  wrap.className = 'app-tabs-nav-wrap';
  nav.parentNode.insertBefore(wrap, nav);
  wrap.appendChild(nav);

  const btnStart = createChevronBtn('start', 'bi-chevron-left');
  const btnEnd = createChevronBtn('end', 'bi-chevron-right');
  wrap.appendChild(btnStart);
  wrap.appendChild(btnEnd);

  const cleanupFns = [];
  const bind = (target, eventName, handler, options = undefined) => {
    target.addEventListener(eventName, handler, options);
    cleanupFns.push(() => target.removeEventListener(eventName, handler, options));
  };

  const scrollStart = () => nav.scrollBy({ left: -SCROLL_STEP, behavior: 'smooth' });
  const scrollEnd = () => nav.scrollBy({ left: SCROLL_STEP, behavior: 'smooth' });
  bind(btnStart, 'click', scrollStart);
  bind(btnEnd, 'click', scrollEnd);

  const destroyLongPressStart = addLongPress(btnStart, scrollStart);
  const destroyLongPressEnd = addLongPress(btnEnd, scrollEnd);
  cleanupFns.push(destroyLongPressStart, destroyLongPressEnd);

  const update = () => {
    const hasOverflow = nav.scrollWidth > nav.clientWidth + SCROLL_THRESHOLD;
    wrap.classList.toggle('has-overflow', hasOverflow);
    if (!hasOverflow) {
      btnStart.disabled = true;
      btnEnd.disabled = true;
      return;
    }
    const atStart = nav.scrollLeft <= SCROLL_THRESHOLD;
    const atEnd = nav.scrollLeft + nav.clientWidth >= nav.scrollWidth - SCROLL_THRESHOLD;
    btnStart.disabled = atStart;
    btnEnd.disabled = atEnd;
    wrap.classList.toggle('at-start', atStart);
    wrap.classList.toggle('at-end', atEnd);
  };

  bind(nav, 'scroll', update, { passive: true });
  let resizeObserver = null;
  if (typeof ResizeObserver !== 'undefined') {
    resizeObserver = new ResizeObserver(update);
    resizeObserver.observe(nav);
  }
  requestAnimationFrame(update);

  return () => {
    cleanupFns.forEach((cleanup) => cleanup());
    cleanupFns.length = 0;
    if (resizeObserver) {
      resizeObserver.disconnect();
      resizeObserver = null;
    }
    if (wrap.parentNode) {
      wrap.parentNode.insertBefore(nav, wrap);
      wrap.remove();
    }
  };
}

function createChevronBtn(direction, iconClass) {
  const button = document.createElement('button');
  button.type = 'button';
  button.className = `app-tabs-chevron app-tabs-chevron--${direction}`;
  button.setAttribute('aria-hidden', 'true');
  button.setAttribute('tabindex', '-1');
  const icon = document.createElement('i');
  icon.className = `bi ${iconClass}`;
  icon.setAttribute('aria-hidden', 'true');
  button.appendChild(icon);
  return button;
}

function addLongPress(button, callback) {
  let interval = null;
  const start = () => {
    interval = setInterval(callback, 180);
  };
  const stop = () => {
    if (interval) {
      clearInterval(interval);
      interval = null;
    }
  };

  button.addEventListener('pointerdown', start);
  button.addEventListener('pointerup', stop);
  button.addEventListener('pointerleave', stop);
  button.addEventListener('pointercancel', stop);

  return () => {
    stop();
    button.removeEventListener('pointerdown', start);
    button.removeEventListener('pointerup', stop);
    button.removeEventListener('pointerleave', stop);
    button.removeEventListener('pointercancel', stop);
  };
}
