import { reactive } from 'vue';

import type { ContextMenuItem, ContextMenuState } from './types';

const MENU_WIDTH = 180;
const MENU_ITEM_HEIGHT = 36;
const MENU_DIVIDER_HEIGHT = 9;
const SUBMENU_GAP = 4;

let listenersBound = false;

export const contextMenuState = reactive<ContextMenuState>({
  visible: false,
  x: 0,
  y: 0,
  items: [],
  payload: null,
});

export const submenuState = reactive({
  activeKey: null as string | null,
  items: [] as ContextMenuItem[],
  left: 0,
  top: 0,
});

function bindGlobalListeners() {
  if (listenersBound) {
    return;
  }
  listenersBound = true;

  const hide = () => hideContextMenu();

  document.addEventListener('click', hide, true);
  document.addEventListener('scroll', hide, true);
  window.addEventListener('resize', hide);
}

export function resetSubmenu() {
  submenuState.activeKey = null;
  submenuState.items = [];
  submenuState.left = 0;
  submenuState.top = 0;
}

export function showSubmenu(key: string, items: ContextMenuItem[], left: number, top: number) {
  submenuState.activeKey = key;
  submenuState.items = items;
  submenuState.left = left;
  submenuState.top = top;
}

export function showContextMenu(
  e: MouseEvent,
  items: ContextMenuItem[],
  payload?: any,
) {
  e.preventDefault();
  hideContextMenu();

  const estimatedHeight = estimateMenuHeight(items);
  let x = e.clientX;
  let y = e.clientY;

  if (x + MENU_WIDTH > window.innerWidth) {
    x = Math.max(8, window.innerWidth - MENU_WIDTH - 8);
  }
  if (y + estimatedHeight > window.innerHeight) {
    y = Math.max(8, window.innerHeight - estimatedHeight - 8);
  }

  contextMenuState.x = x;
  contextMenuState.y = y;
  contextMenuState.items = items;
  contextMenuState.payload = payload ?? null;
  contextMenuState.visible = true;

  bindGlobalListeners();
}

export function hideContextMenu() {
  contextMenuState.visible = false;
  contextMenuState.items = [];
  contextMenuState.payload = null;
  resetSubmenu();
}

export function estimateMenuHeight(items: ContextMenuItem[]): number {
  let height = 8;
  for (const item of items) {
    height +=
      item.type === 'divider' ? MENU_DIVIDER_HEIGHT : MENU_ITEM_HEIGHT;
  }
  return height;
}

export function getSubmenuPosition(
  parentRect: DOMRect,
  childCount: number,
): { left: number; top: number } {
  const submenuHeight = childCount * MENU_ITEM_HEIGHT + 8;
  let left = parentRect.right + SUBMENU_GAP;
  let top = parentRect.top;

  if (left + MENU_WIDTH > window.innerWidth) {
    left = parentRect.left - MENU_WIDTH - SUBMENU_GAP;
  }
  if (top + submenuHeight > window.innerHeight) {
    top = Math.max(8, window.innerHeight - submenuHeight - 8);
  }

  return { left, top };
}

export { MENU_WIDTH, MENU_ITEM_HEIGHT };
