/**
 * Sidebar collapse/show toggle with localStorage persistence and Ctrl+B shortcut.
 */
import { warnOnce, resolveHost } from '../core/app-dom.js';
import { isEditableTarget } from '../core/app-form-utils.js';
import { createUiStorage } from '../core/app-ui-storage.js';

const COLLAPSED_STORAGE_KEY = 'sidebar-collapsed';
const HIDDEN_STORAGE_KEY = 'sidebar-hidden';

export function initSidebarToggle(root = document, options = {}) {
  const {
    buttonSelector = '[data-sidebar-toggle]',
    asideSelector = '#app-sidebar',
    containerSelector = '.app-container',
    storageNamespace = 'app-ui',
    storageVersion = 'v1',
    storageScope = 'sidebar',
    collapsedStorageKey = COLLAPSED_STORAGE_KEY,
    hiddenStorageKey = HIDDEN_STORAGE_KEY,
  } = options;

  const host = resolveHost(root);
  const aside = host.matches?.(asideSelector) ? host : host.querySelector(asideSelector);
  if (!(aside instanceof HTMLElement)) {
    warnOnce('UI_EL_MISSING', `Missing sidebar: ${asideSelector}`, { module: 'sidebar-toggle' });
    return { destroy: () => {} };
  }
  if (aside.dataset.sidebarToggleBound === '1' && aside._sidebarToggleApi) {
    return aside._sidebarToggleApi;
  }

  const storage = createUiStorage({
    namespace: storageNamespace,
    version: storageVersion,
    scope: storageScope,
  });
  const scopedCollapsedKey = storage.buildKey('state', collapsedStorageKey);
  const scopedHiddenKey = storage.buildKey('state', hiddenStorageKey);

  const buttons = Array.from(host.querySelectorAll(buttonSelector));
  if (!buttons.length) {
    warnOnce('UI_EL_MISSING', `Missing sidebar toggle buttons: ${buttonSelector}`, { module: 'sidebar-toggle' });
  }
  const container = host.querySelector(containerSelector);
  const rootEl = document.documentElement;

  const readStored = () => {
    const scopedValue = storage.getItem(scopedCollapsedKey);
    if (scopedValue === '1' || scopedValue === '0') {
      return scopedValue;
    }
    return null;
  };

  const readHiddenStored = () => {
    const scopedValue = storage.getItem(scopedHiddenKey);
    if (scopedValue === '1' || scopedValue === '0') {
      return scopedValue;
    }
    return null;
  };

  const writeStored = (collapsed) => {
    storage.setItem(scopedCollapsedKey, collapsed ? '1' : '0');
  };

  const writeHiddenStored = (hidden) => {
    storage.setItem(scopedHiddenKey, hidden ? '1' : '0');
  };

  const applyState = (collapsed, { persist = false } = {}) => {
    aside.classList.toggle('is-collapsed', collapsed);
    if (container instanceof HTMLElement) {
      container.classList.toggle('is-sidebar-collapsed', collapsed);
    }
    rootEl.classList.toggle('sidebar-collapsed', collapsed);
    buttons.forEach((button) => {
      if (!button.getAttribute('aria-controls')) {
        button.setAttribute('aria-controls', aside.id || 'app-sidebar');
      }
      button.setAttribute('aria-expanded', collapsed ? 'false' : 'true');
    });
    if (persist) {
      writeStored(collapsed);
    }
  };

  const applyHidden = (hidden, { persist = false } = {}) => {
    aside.classList.toggle('is-hidden', hidden);
    if (container instanceof HTMLElement) {
      container.classList.toggle('is-sidebar-hidden', hidden);
    }
    rootEl.classList.toggle('sidebar-hidden', hidden);
    buttons.forEach((button) => {
      if (!button.getAttribute('aria-controls')) {
        button.setAttribute('aria-controls', aside.id || 'app-sidebar');
      }
      button.setAttribute('aria-expanded', hidden ? 'false' : button.getAttribute('aria-expanded') || 'true');
    });
    if (persist) {
      writeHiddenStored(hidden);
    }
  };

  const isCollapsed = () => aside.classList.contains('is-collapsed');
  const isHidden = () => aside.classList.contains('is-hidden');

  const getInitialState = () => {
    const stored = readStored();
    if (stored === '1' || stored === '0') {
      return stored === '1';
    }
    return rootEl.classList.contains('sidebar-collapsed');
  };

  const controller = {
    isCollapsed,
    isHidden,
    open: ({ persist = true } = {}) => applyState(false, { persist }),
    close: ({ persist = true } = {}) => applyState(true, { persist }),
    toggle: ({ persist = true } = {}) => applyState(!isCollapsed(), { persist }),
    apply: (collapsed, { persist = false } = {}) => applyState(Boolean(collapsed), { persist }),
    show: ({ persist = true } = {}) => applyHidden(false, { persist }),
    hide: ({ persist = true } = {}) => applyHidden(true, { persist }),
    toggleVisibility: ({ persist = true } = {}) => applyHidden(!isHidden(), { persist }),
    applyVisibility: (hidden, { persist = false } = {}) => applyHidden(Boolean(hidden), { persist }),
  };

  applyState(getInitialState(), { persist: false });
  const hiddenStored = readHiddenStored();
  if (hiddenStored === '1' || hiddenStored === '0') {
    applyHidden(hiddenStored === '1', { persist: false });
  }

  // ── Mobile drawer helpers ──
  const mobileQuery = window.matchMedia('(max-width: 767px)');
  const isMobileViewport = () => mobileQuery.matches;

  const FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';

  const isDrawerOpen = () => rootEl.classList.contains('drawer-open');

  const openDrawer = () => {
    rootEl.classList.add('drawer-open');
    const hamburger = document.querySelector('.app-topbar-hamburger');
    if (hamburger) {
      hamburger.setAttribute('aria-label', hamburger.dataset.closeLabel || 'Close navigation');
      hamburger.setAttribute('aria-expanded', 'true');
    }
  };

  const closeDrawer = () => {
    rootEl.classList.remove('drawer-open');
    const hamburger = document.querySelector('.app-topbar-hamburger');
    if (hamburger) {
      hamburger.setAttribute('aria-label', hamburger.dataset.openLabel || 'Open navigation');
      hamburger.setAttribute('aria-expanded', 'false');
      hamburger.focus();
    }
  };

  const toggleDrawer = () => {
    if (isDrawerOpen()) {
      closeDrawer();
    } else {
      openDrawer();
    }
  };

  const cleanupFns = [];

  // ── Focus trap (active only when drawer open) ──
  const onFocusTrap = (event) => {
    if (!isDrawerOpen() || event.key !== 'Tab') {
      return;
    }
    const drawerElements = [
      ...document.querySelectorAll('.app-topbar-hamburger'),
      ...aside.querySelectorAll(FOCUSABLE),
      ...document.querySelectorAll('.aside-icon-bar ' + FOCUSABLE),
    ];
    const focusable = drawerElements.filter((el) => el.offsetParent !== null);
    if (!focusable.length) {
      return;
    }
    const first = focusable[0];
    const last = focusable[focusable.length - 1];
    if (event.shiftKey && document.activeElement === first) {
      event.preventDefault();
      last.focus();
    } else if (!event.shiftKey && document.activeElement === last) {
      event.preventDefault();
      first.focus();
    }
  };
  document.addEventListener('keydown', onFocusTrap);
  cleanupFns.push(() => document.removeEventListener('keydown', onFocusTrap));

  // ── ESC to close drawer ──
  const onEscDrawer = (event) => {
    if (event.key === 'Escape' && isDrawerOpen()) {
      event.preventDefault();
      closeDrawer();
    }
  };
  document.addEventListener('keydown', onEscDrawer);
  cleanupFns.push(() => document.removeEventListener('keydown', onEscDrawer));

  // ── Backdrop click to close drawer ──
  const onBackdropClick = (event) => {
    if (isDrawerOpen() && event.target.classList.contains('app-drawer-backdrop')) {
      closeDrawer();
    }
  };
  document.addEventListener('click', onBackdropClick);
  cleanupFns.push(() => document.removeEventListener('click', onBackdropClick));

  // ── matchMedia listener: sync state on viewport change ──
  const onViewportChange = (mql) => {
    if (!mql.matches) {
      // Crossed to desktop — close drawer, restore desktop sidebar state
      if (isDrawerOpen()) {
        closeDrawer();
      }
      const storedCollapsed = readStored();
      if (storedCollapsed === '1' || storedCollapsed === '0') {
        applyState(storedCollapsed === '1', { persist: false });
      }
      const storedHidden = readHiddenStored();
      if (storedHidden === '1' || storedHidden === '0') {
        applyHidden(storedHidden === '1', { persist: false });
      }
    } else {
      // Crossed to mobile — remove desktop sidebar classes that would interfere
      rootEl.classList.remove('sidebar-collapsed', 'sidebar-hidden');
      aside.classList.remove('is-collapsed', 'is-hidden');
      if (container instanceof HTMLElement) {
        container.classList.remove('is-sidebar-collapsed', 'is-sidebar-hidden');
      }
    }
  };
  mobileQuery.addEventListener('change', onViewportChange);
  cleanupFns.push(() => mobileQuery.removeEventListener('change', onViewportChange));

  buttons.forEach((button) => {
    const onClick = (event) => {
      event.preventDefault();
      if (isMobileViewport()) {
        toggleDrawer();
        return;
      }
      const action = (button.dataset.sidebarAction || '').toLowerCase();
      if (action === 'visibility') {
        controller.toggleVisibility({ persist: true });
        return;
      }
      controller.toggle({ persist: true });
    };
    button.addEventListener('click', onClick);
    cleanupFns.push(() => button.removeEventListener('click', onClick));
  });

  const onSidebarEvent = (event) => {
    const detail = event.detail || {};
    const action = detail.action;
    const persist = detail.persist ?? false;
    if (isMobileViewport()) {
      if (action === 'show' || action === 'open') {
        openDrawer();
        return;
      }
      if (action === 'hide' || action === 'close') {
        closeDrawer();
        return;
      }
      if (action === 'toggle' || action === 'toggle-visibility') {
        toggleDrawer();
        return;
      }
      return;
    }
    if (action === 'open') { controller.open({ persist }); }
    if (action === 'close') { controller.close({ persist }); }
    if (action === 'toggle') { controller.toggle({ persist }); }
    if (action === 'show') { controller.show({ persist }); }
    if (action === 'hide') { controller.hide({ persist }); }
    if (action === 'toggle-visibility') { controller.toggleVisibility({ persist }); }
  };
  document.addEventListener('app:sidebar', onSidebarEvent);
  cleanupFns.push(() => document.removeEventListener('app:sidebar', onSidebarEvent));

  const onShortcut = (event) => {
    if (isMobileViewport()) {
      return;
    }
    if (!(event.metaKey || event.ctrlKey)) {
      return;
    }
    if (event.altKey) {
      return;
    }
    if (event.key.toLowerCase() !== 'b') {
      return;
    }
    if (isEditableTarget(event.target)) {
      return;
    }
    event.preventDefault();
    controller.toggleVisibility({ persist: true });
  };
  document.addEventListener('keydown', onShortcut);
  cleanupFns.push(() => document.removeEventListener('keydown', onShortcut));

  const destroy = () => {
    cleanupFns.forEach((cleanup) => cleanup());
    if (isDrawerOpen()) {
      rootEl.classList.remove('drawer-open');
    }
    delete aside.dataset.sidebarToggleBound;
    delete aside._sidebarToggleApi;
  };

  const api = { ...controller, destroy };
  aside.dataset.sidebarToggleBound = '1';
  aside._sidebarToggleApi = api;
  return api;
}
