import { h } from '../core/dom';
import { store } from '../core/store';
import { icon } from '../ui/icons';
import { allFeatures, CATEGORY_LABELS, type FeatureCategory, type ToolContext } from '../features/registry';
import { openAutofillSettings } from '../ui/autofill-settings';

export interface MenubarCallbacks {
  onOpen: () => void;
  onExport: () => void;
  onPrint: () => void;
  onHome: () => void;
  onMeta: () => void;
  onZoom: (dir: number) => void;
  onFit: () => void;
  onActualSize: () => void;
  onInsertBlank: () => void;
  toolCtx: ToolContext;
}

interface MenuItem {
  label: string;
  icon?: string;
  action?: () => void;
  shortcut?: string;
  divider?: boolean;
  header?: string;
  disabled?: boolean;
}

// menuButton() registers a document/window-level listener per dropdown (for
// click-outside + resize dismissal) that outlives the button itself — tracked
// here so buildMenubar() can remove the previous mount's before adding new ones.
let globalMenuListeners: Array<() => void> = [];

export function buildMenubar(cb: MenubarCallbacks): HTMLElement {
  // Dropdown panels live in <body> (see menuButton); clear out any left behind
  // by a previous editor mount so they don't accumulate across open/close cycles.
  document.querySelectorAll('.menu-panel').forEach((n) => { n.remove(); });
  globalMenuListeners.forEach((off) => { off(); });
  globalMenuListeners = [];

  const filenamePill = h('div.menu-file-pill', store.doc?.name ?? 'document.pdf');
  store.on('doc', () => { filenamePill.textContent = store.doc?.name ?? 'document.pdf'; });

  // Menus are rebuilt fresh from items() every time they open, so gating on
  // store.doc here always reflects the current state — no separate sync
  // needed. Only items that act on the open document/canvas are disabled;
  // self-sufficient flows (Tools menu, Insert > Page numbers/Watermark — both
  // have their own dropzone and work with no document open, same as "Convert
  // image to PDF") stay enabled regardless.
  const menus: { label: string; items: () => MenuItem[] }[] = [
    {
      label: 'File',
      items: () => [
        { label: 'Open…', icon: 'upload', action: cb.onOpen, shortcut: '⌘O' },
        { label: 'Export PDF', icon: 'download', action: cb.onExport, shortcut: '⌘S', disabled: !store.doc },
        { label: 'Print', icon: 'file', action: cb.onPrint, shortcut: '⌘P', disabled: !store.doc },
        { divider: true, label: '' },
        { label: 'Close document', icon: 'x', action: cb.onHome, disabled: !store.doc },
      ],
    },
    {
      label: 'Edit',
      items: () => [
        { label: 'Undo', icon: 'undo', action: () => store.undo(), shortcut: '⌘Z', disabled: !store.canUndo() },
        { label: 'Redo', icon: 'redo', action: () => store.redo(), shortcut: '⌘⇧Z', disabled: !store.canRedo() },
        { divider: true, label: '' },
        { label: 'Delete selection', icon: 'trash', action: () => { if (store.selection.length) { store.checkpoint(); store.removeAnnos(store.selection); } }, disabled: !store.selection.length },
        { label: 'Select all on page', icon: 'cursor', action: () => selectAllOnPage(), disabled: !store.doc },
        { divider: true, label: '' },
        { label: 'Document properties…', icon: 'meta', action: cb.onMeta, disabled: !store.doc },
        { label: 'Autofill…', icon: 'field', action: () => openAutofillSettings() },
      ],
    },
    {
      label: 'View',
      items: () => [
        { label: 'Zoom in', icon: 'zoomIn', action: () => cb.onZoom(1), shortcut: '⌘+', disabled: !store.doc },
        { label: 'Zoom out', icon: 'zoomOut', action: () => cb.onZoom(-1), shortcut: '⌘−', disabled: !store.doc },
        { label: 'Fit width', icon: 'fit', action: cb.onFit, disabled: !store.doc },
        { label: 'Actual size', icon: 'file', action: cb.onActualSize, disabled: !store.doc },
        { divider: true, label: '' },
        { label: 'Uniform scaling (lock aspect ratio)', icon: 'fit', action: () => store.setUniformScale(!store.uniformScale), shortcut: store.uniformScale ? '✓' : '' },
        { label: 'Switch to cursor after adding', icon: 'cursor', action: () => store.setAutoCursor(!store.autoCursor), shortcut: store.autoCursor ? '✓' : '' },
      ],
    },
    {
      label: 'Insert',
      items: () => [
        { label: 'Text box', icon: 'text', action: () => store.setTool('text'), disabled: !store.doc },
        { label: 'Image', icon: 'image', action: () => store.setTool('image'), disabled: !store.doc },
        { label: 'Signature', icon: 'signature', action: () => store.setTool('signature'), disabled: !store.doc },
        { label: 'Link', icon: 'link', action: () => store.setTool('link'), disabled: !store.doc },
        { divider: true, label: '' },
        { label: 'Blank page', icon: 'plus', action: cb.onInsertBlank, disabled: !store.doc },
        { label: 'Page numbers…', icon: 'numbers', action: () => runFeature('page-numbers', cb.toolCtx) },
        { label: 'Watermark…', icon: 'watermark', action: () => runFeature('watermark', cb.toolCtx) },
      ],
    },
    {
      label: 'Tools',
      items: () => toolsMenu(cb.toolCtx),
    },
  ];

  const menuScroll = h('div.menu-scroll', ...menus.map((m) => menuButton(m.label, m.items)), filenamePill);
  // an open dropdown is positioned (in viewport coords) relative to its trigger
  // button, so scrolling this strip would leave it stranded — just close it.
  menuScroll.addEventListener('scroll', () => closeAllMenus());

  const exportBtn = h('button.btn btn-primary btn-sm', { onclick: cb.onExport }, 'Export') as HTMLButtonElement;

  const bar = h(
    'div.menubar',
    h(
      'div.menu-brand',
      { onclick: cb.onHome, title: 'Back to home' },
      h('img.menu-logo', { src: '/favicon.svg', alt: 'YeetPDF logo', width: 24, height: 24 }),
      h('span', 'YeetPDF'),
    ),
    menuScroll,
    h('div.menu-right',
      h(
        'span.saved-badge',
        h('span.saved-icon', {
          html:
            '<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M20 16.5A4.5 4.5 0 0 0 16 8a6 6 0 0 0-11.5 1.5A4 4 0 0 0 5 17"></path><path d="M9 13l2 2 4-4"></path></svg>',
        }),
        h('span.saved-text', 'Saved locally'),
      ),
      exportBtn,
    ),
  );
  store.on('doc', () => { exportBtn.disabled = !store.doc; });
  exportBtn.disabled = !store.doc;
  return bar;
}

function closeAllMenus() {
  document.querySelectorAll('.menu-item-wrap.open').forEach((n) => { n.classList.remove('open'); });
  document.querySelectorAll('.menu-panel.open').forEach((n) => { n.classList.remove('open'); });
}

function menuButton(label: string, items: () => MenuItem[]): HTMLElement {
  const panel = h('div.menu-panel');
  const btn = h('button.menu-btn', label);
  const wrap = h('div.menu-item-wrap', btn);
  // The panel must NOT live inside the menubar: backdrop-filter makes the menubar
  // the containing block for fixed-position descendants, and iOS Safari clips them
  // to it — dropdowns nested here render as a one-row sliver on iPhones. Mount in
  // <body> instead, exactly like the color picker's popover and the modals.
  document.body.appendChild(panel);

  const build = () => {
    panel.replaceChildren();
    for (const it of items()) {
      if (it.divider) { panel.appendChild(h('div.menu-divider')); continue; }
      if (it.header) { panel.appendChild(h('div.menu-header', it.header)); continue; }
      panel.appendChild(
        h('button.menu-option', { disabled: it.disabled, onclick: () => { close(); it.action?.(); } },
          it.icon ? h('span.menu-opt-icon', icon(it.icon, 15)) : h('span.menu-opt-icon'),
          h('span.menu-opt-label', it.label),
          it.shortcut ? h('span.menu-opt-key', it.shortcut) : null,
        ),
      );
    }
  };
  // Positioned in viewport coordinates (like the color picker's popover in
  // ui/color.ts) instead of CSS-anchored to the button, so it always escapes
  // .menu-scroll's overflow clipping regardless of scroll position.
  const position = () => {
    const r = btn.getBoundingClientRect();
    const pw = panel.offsetWidth;
    const left = Math.max(8, Math.min(r.left, window.innerWidth - pw - 8));
    panel.style.left = `${left}px`;
    panel.style.top = `${r.bottom + 6}px`;
  };
  const open = () => { closeAllMenus(); build(); wrap.classList.add('open'); panel.classList.add('open'); position(); };
  const close = () => { wrap.classList.remove('open'); panel.classList.remove('open'); };
  btn.addEventListener('click', (e) => { e.stopPropagation(); wrap.classList.contains('open') ? close() : open(); });
  btn.addEventListener('pointerdown', (e) => { e.stopPropagation(); });
  panel.addEventListener('pointerdown', (e) => { e.stopPropagation(); });
  // Hover-chaining between menus is mouse-only: a tap synthesizes mouseenter right
  // before click, so on touch it would chain-open the menu and the click would
  // immediately toggle it shut again.
  btn.addEventListener('pointerenter', (e) => {
    if (e.pointerType === 'mouse' && document.querySelector('.menu-item-wrap.open')) open();
  });
  // Dismissal listens for pointerdown, not click: iOS Safari doesn't deliver
  // document-level click for taps on non-interactive elements, which left menus
  // stuck open. btn/panel stop pointerdown propagation so their own interactions
  // aren't swallowed by this.
  document.addEventListener('pointerdown', close);
  window.addEventListener('resize', close);
  globalMenuListeners.push(() => {
    document.removeEventListener('pointerdown', close);
    window.removeEventListener('resize', close);
  });
  return wrap;
}

function toolsMenu(ctx: ToolContext): MenuItem[] {
  const items: MenuItem[] = [];
  const order: FeatureCategory[] = ['organize', 'convert-in', 'convert-out', 'optimize', 'security', 'forms', 'ocr', 'sign'];
  for (const cat of order) {
    const feats = allFeatures().filter((f) => f.category === cat);
    if (!feats.length) continue;
    items.push({ header: CATEGORY_LABELS[cat], label: '' });
    for (const f of feats) items.push({ label: f.title, icon: f.icon, action: () => f.run(ctx) });
  }
  if (!items.length) items.push({ label: 'Loading tools…', action: () => {} });
  return items;
}

function runFeature(id: string, ctx: ToolContext) {
  const f = allFeatures().find((x) => x.id === id);
  f?.run(ctx);
}

function selectAllOnPage() {
  const live = store.doc!.pages.filter((p) => !p.deleted);
  const ps = live[store.currentPage];
  if (!ps) return;
  store.select(store.annosForPage(ps.id).map((a) => a.id));
}
