import {useAccessProfileStore} from "~/stores/accessProfile";
import {useAbility} from "@casl/vue";
import {useOrganizationProfileStore} from "~/stores/organizationProfile";
import {MenuGroup, MenuItem} from "~/types/layout";
import {MENU_LINK_TYPE} from "~/types/enum/layout";
import {AccessProfile} from "~/types/interfaces";
import {useLayoutStore} from "~/stores/layout";
import MenuComposer from "~/services/layout/menuComposer";

/**
 * Renvoie des méthodes pour interagir avec les menus
 *
 * Exemple d'usage :
 *
 *     const { buildMenu, hasMenu } = useMenuBuilder()
 *     const menu = buildMenu('Main')
 *
 *     console.log(hasMenu('Main')) // true
 */
export const useMenu = () => {
  const runtimeConfig = useRuntimeConfig()
  const ability = useAbility()
  const organizationProfile = useOrganizationProfileStore()
  const accessProfile = useAccessProfileStore()
  const layoutState = useLayoutStore()

  /**
   * Construct all Menus
   */
  const buildAllMenu = () => {
    MenuComposer.build(runtimeConfig, ability, organizationProfile, accessProfile as AccessProfile, layoutState)
  }

  /**
   * Retourne le menu depuis le store ou retourne null
   * si le menu n'a pas été construit pour l'utilisateur courant
   *
   * @param name
   */
  const getMenu = (name: string): MenuGroup | MenuItem | null => {
    return layoutState.menus[name] || null
  }

  /**
   * L'utilisateur en cours a-t-il accès au menu portant ce nom ?
   *
   * @param name
   */
  const hasMenu = (name: string): boolean => {
    return getMenu(name) !== null
  }

  /**
   * Soulève une erreur si aucun menu portant ce nom n'est enregistré dans le store
   *
   * @param name
   */
  const assertExists = (name: string) => {
    if (getMenu(name) === null) {
      throw new Error('Unknown menu : ' + name)
    }
  }

  /**
   * Retourne vrai si le menu est actuellement ouvert / déplié
   *
   * @param name
   */
  const isMenuOpened = (name: string): boolean => {
    assertExists(name)
    return layoutState.menusOpened[name]
  }

  /**
   * Met à jour l'état du menu portant ce nom
   *
   * @param name
   * @param state
   */
  const setMenuState = (name: string, state: boolean) => {
    assertExists(name)
    layoutState.menusOpened[name] = state
  }

  /**
   * Ouvre / déplie le menu portant ce nom
   *
   * @param name
   */
  const openMenu = (name: string) => {
    setMenuState(name, true)
  }

  /**
   * Ferme / replie le menu portant ce nom
   *
   * @param name
   */
  const closeMenu = (name: string) => {
    setMenuState(name, false)
  }

  /**
   * Bascule l'état du menu entre ouvert et fermé
   *
   * @param name
   */
  const toggleMenu = (name: string) => {
    setMenuState(name, !layoutState.menusOpened[name])
  }

  /**
   * Le lien menuItem est-il un lien interne à l'application
   *
   * @param menuItem
   */
  const isInternalLink = (menuItem: MenuItem | MenuGroup): boolean => {
    return 'type' in menuItem && menuItem.type === MENU_LINK_TYPE.INTERNAL
  }

  return {
    buildAllMenu,
    getMenu,
    hasMenu,
    setMenuState,
    openMenu,
    closeMenu,
    toggleMenu,
    isMenuOpened,
    isInternalLink
  }
}
