import type { Menu } from '@/core/types/menu'

export type MenuList = (string | Menu)[]

function isMenuVisible(menu: Menu, hasPermission: (permission: string) => boolean): boolean {
  if (menu.sub_menu?.length) {
    return menu.sub_menu.some((item) => isMenuVisible(item, hasPermission))
  }

  if (!menu.permission) {
    return true
  }

  return hasPermission(menu.permission)
}

function filterMenuItem(menu: Menu, hasPermission: (permission: string) => boolean): Menu | null {
  if (menu.sub_menu?.length) {
    const subMenu = menu.sub_menu
      .map((item) => filterMenuItem(item, hasPermission))
      .filter((item): item is Menu => item !== null)

    if (!subMenu.length) {
      return null
    }

    return { ...menu, sub_menu: subMenu }
  }

  return isMenuVisible(menu, hasPermission) ? menu : null
}

function sectionHasVisibleItems(
  menu: MenuList,
  startIndex: number,
  hasPermission: (permission: string) => boolean,
): boolean {
  for (let index = startIndex + 1; index < menu.length; index += 1) {
    const entry = menu[index]

    if (typeof entry === 'string') {
      break
    }

    if (entry && isMenuVisible(entry, hasPermission)) {
      return true
    }
  }

  return false
}

export function filterMenuByPermission(
  menu: MenuList,
  hasPermission: (permission: string) => boolean,
): MenuList {
  const filtered: MenuList = []

  menu.forEach((entry, index) => {
    if (typeof entry === 'string') {
      if (sectionHasVisibleItems(menu, index, hasPermission)) {
        filtered.push(entry)
      }

      return
    }

    const visibleItem = filterMenuItem(entry, hasPermission)
    if (visibleItem) {
      filtered.push(visibleItem)
    }
  })

  return filtered
}
