<template>
  <a-menu
    v-model:openKeys="state.openKeys"
    v-model:selectedKeys="state.selectedKeys"
    :mode="mode"
    :theme="theme"
    :inline-collapsed="collapsed"
    :inline-indent="config.menuIndent"
    :items="items"
    :class="{
      'u-layout-menu-flat': config.menuStyle === 'flat',
      'u-layout-menu-dark': config.navStyle === 'dark' && config.layout === 'default'
    }"
    class="u-layout-menu"
    @click="menuClick"
    @open-change="onOpenChange"
  >
  </a-menu>
</template>

<script lang="ts" setup>
import type { ComputedRef, Ref } from 'vue';
import { computed, inject, reactive, ref, watch } from 'vue';
import type { AntMenu, LayoutConfig, ThemeColor } from '../../types';
import { generateEachPathByPath } from '../../utils';
import {
  clickedMenusKey,
  configKey,
  handleMenuClickKey,
  openKeysKey,
  phoneFlagKey,
  routeKey,
  routerKey,
  selectedKeysKey,
  themeColorKey
} from '../../utils/injectedKeys';
import { colorLighten } from '@unyu/utils';

const props = withDefaults(
  defineProps<{
    mode?: 'horizontal' | 'vertical' | 'inline';
    items: AntMenu[];
    theme?: NavStyle;
    collapsed?: boolean;
  }>(),
  {
    collapsed: false
  }
);
const emits = defineEmits<{
  (e: 'update:collapsed', value: boolean): void;
}>();
const { router } = inject<{ router: Ref<any> }>(routerKey)!;
const { route } = inject<{ route: ComputedRef<Record<string, any>> }>(routeKey)!;

const { config } = inject<{ config: Ref<LayoutConfig> }>(configKey)!;
const mode = ref(props.mode || (config.value.layout === 'topBar' ? 'horizontal' : 'inline'));
const { themeColor } = inject<{ themeColor: Ref<ThemeColor> }>(themeColorKey)!;

const items = ref(props.items);

const { pushClickedMenus } = inject<{
  clickedMenus: Ref<AntMenu[]>;
  pushClickedMenus: (menu: AntMenu) => void;
}>(clickedMenusKey)!;
const { handleMenuClick } = inject<{ handleMenuClick: (value: any) => void }>(handleMenuClickKey)!;
const { phoneFlag } = inject<{ phoneFlag: Ref<boolean> }>(phoneFlagKey)!;
const { selectedKeys } = inject<{ selectedKeys: Ref<string[]> }>(selectedKeysKey)!;
const { openKeys } = inject<{ openKeys: Ref<string[]> }>(openKeysKey)!;

const collapsed = ref(!phoneFlag.value && props.collapsed && config.value.layout !== 'topBar');

const menuActiveColor = computed(() => themeColor.value.menuActiveColor || themeColor.value.primary);
const secondMenuActiveColor = computed(() => colorLighten(menuActiveColor.value || '#fff', 0.91));

const state = reactive({
  selectedKeys: selectedKeys.value,
  openKeys: openKeys.value,
  preOpenKeys: [] as string[]
});

const menuClick = ({ item, key, keyPath }: { item: Record<string, any>; key: string; keyPath: string[] }) => {
  if (key === route.value.fullPath) return;
  const { originItemValue } = item;
  handleMenuClick({ menu: originItemValue, item, key, keyPath });
  if (originItemValue.meta?.isLink) {
    window.open(originItemValue.meta.isLink, '_blank');
    return;
  }
  const path = keyPath.at(-1);
  pushClickedMenus(originItemValue);
  router.value.push({
    path
  });
};

const onOpenChange = (openKeys: string[]) => {
  if (!config.value.isMenuAccordion) return;
  const latestOpenKey = openKeys.at(-1);
  if (latestOpenKey) {
    state.openKeys = openKeys.filter(
      openKey => openKey === latestOpenKey || openKey.split('/').length < latestOpenKey.split('/').length
    );
  }
};

/**
 * 根据路由路径查找菜单项
 * @param menus - 菜单列表
 * @param path - 要查找的路径
 */
const findMenuByPath = (menus: AntMenu[], path: string) => {
  let index = 0;
  let menu: AntMenu | undefined;
  const length = menus.length;
  while (!menu && index < length) {
    menu = menus.find(item => item.key === path);
    if (!menu && menus[index].children) {
      menu = findMenuByPath(menus[index].children!, path);
    }
    index++;
  }
  return menu;
};

watch(
  () => state.openKeys,
  (_val, oldVal) => {
    state.preOpenKeys = oldVal;
  }
);
watch(
  () => props.collapsed,
  () => {
    collapsed.value = props.collapsed;
  }
);

watch(
  () => [phoneFlag.value, collapsed.value],
  ([, newVal2]) => {
    if (!phoneFlag.value) {
      collapsed.value = newVal2;
      emits('update:collapsed', newVal2);
    }
  },
  {
    immediate: true
  }
);

watch(
  () => config.value.layout,
  () => {
    if (config.value.layout === 'topBar') {
      openKeys.value = [openKeys.value.pop() || ''];
    }
  }
);

watch(
  () => props.items,
  () => {
    items.value = props.items;
  }
);

watch(
  () => [route.value.path, items.value],
  () => {
    const menu = findMenuByPath(props.items, route.value.path);
    if (menu) {
      state.selectedKeys = [menu.key!];
      if (config.value.layout === 'topBar' || window.innerWidth <= 768 || collapsed.value) {
        openKeys.value = [];
      } else {
        const keys = generateEachPathByPath(menu.key!, 0);
        keys.pop();
        keys?.forEach(key => {
          openKeys.value.push(key);
        });
      }
      pushClickedMenus(menu!);
    } else {
      state.selectedKeys = [];
    }
  },
  {
    immediate: true
  }
);
</script>

<style scoped>
.u-layout-menu {
  height: 100%;
}

.u-layout-menu.ant-menu-inline-collapsed :deep(.ant-menu-item-selected):after {
  width: 0 !important;
  height: 0 !important;
  background-color: transparent !important;
}

.u-layout-menu.ant-menu-inline.u-layout-menu-dark :deep(.ant-menu-item-selected) {
  color: white !important;
  background-color: v-bind("menuActiveColor") !important;
}
.u-layout-menu.ant-menu-inline.u-layout-menu-dark :deep(.ant-menu-item-selected):after, .u-layout-menu.ant-menu-inline.u-layout-menu-dark :deep(.ant-menu-item-selected):before {
  width: 0 !important;
  height: 0 !important;
  background-color: transparent !important;
}

.u-layout-menu.ant-menu-inline :deep(.ant-menu-item-selected):after {
  content: "";
  position: absolute;
  z-index: 20;
  width: 3px;
  height: 100%;
  right: 0;
  top: 0;
  background-color: v-bind("menuActiveColor");
}

.u-layout-menu:not(.ant-menu-vertical):not(.ant-menu-dark),
.u-layout-menu.ant-menu-inline-collapsed {
  color: inherit;
  border-inline-end: none !important;
}
.u-layout-menu:not(.ant-menu-vertical):not(.ant-menu-dark) :deep(.ant-menu-item:not(.ant-menu-item-selected):active),
.u-layout-menu.ant-menu-inline-collapsed :deep(.ant-menu-item:not(.ant-menu-item-selected):active) {
  background-color: v-bind("menuActiveColor");
  color: white;
}
.u-layout-menu:not(.ant-menu-vertical):not(.ant-menu-dark) :deep(.ant-menu-submenu-title:active),
.u-layout-menu.ant-menu-inline-collapsed :deep(.ant-menu-submenu-title:active) {
  color: white;
}
.u-layout-menu:not(.ant-menu-vertical):not(.ant-menu-dark) :deep(.ant-menu-submenu-title:hover),
.u-layout-menu.ant-menu-inline-collapsed :deep(.ant-menu-submenu-title:hover) {
  color: v-bind("menuActiveColor");
}
.u-layout-menu:not(.ant-menu-vertical):not(.ant-menu-dark) :deep(.ant-menu-submenu-selected > .ant-menu-submenu-title),
.u-layout-menu:not(.ant-menu-vertical):not(.ant-menu-dark) :deep(.ant-menu-item:hover:not(.ant-menu-item-selected):not(.ant-menu-submenu-selected)),
.u-layout-menu:not(.ant-menu-vertical):not(.ant-menu-dark) :deep(.ant-menu-submenu-title:hover:not(.ant-menu-item-selected):not(.ant-menu-submenu-selected)),
.u-layout-menu.ant-menu-inline-collapsed :deep(.ant-menu-submenu-selected > .ant-menu-submenu-title),
.u-layout-menu.ant-menu-inline-collapsed :deep(.ant-menu-item:hover:not(.ant-menu-item-selected):not(.ant-menu-submenu-selected)),
.u-layout-menu.ant-menu-inline-collapsed :deep(.ant-menu-submenu-title:hover:not(.ant-menu-item-selected):not(.ant-menu-submenu-selected)) {
  color: v-bind("menuActiveColor");
}
.u-layout-menu:not(.ant-menu-vertical):not(.ant-menu-dark) :deep(.ant-menu-light:not(.ant-menu-horizontal) .ant-menu-submenu-title:active),
.u-layout-menu.ant-menu-inline-collapsed :deep(.ant-menu-light:not(.ant-menu-horizontal) .ant-menu-submenu-title:active) {
  background-color: rgba(0, 0, 0, 0.06) !important;
}

:where(.u-layout-menu).ant-menu-inline {
  width: 210px;
}

:where(.u-layout-menu).ant-menu-inline-collapsed {
  width: v-bind("config.menuCollapsedWidth");
}

:where(.u-layout-menu).ant-menu-horizontal {
  color: inherit !important;
}
:where(.u-layout-menu).ant-menu-horizontal :deep(.ant-menu-item-selected::after) {
  border-bottom-color: transparent !important;
}
:where(.u-layout-menu).ant-menu-horizontal :deep(.ant-menu-item-selected) {
  background-color: v-bind("menuActiveColor") !important;
  color: whitesmoke !important;
}

.u-layout-menu.ant-menu-light :deep(.ant-menu-item-selected) {
  color: v-bind("menuActiveColor") !important;
  background-color: v-bind("secondMenuActiveColor") !important;
}

:where(.u-layout-menu).ant-menu-light,
:where(.u-layout-menu).ant-menu-horizontal {
  background-color: transparent !important;
}

:where(.u-layout-menu).ant-menu-light:not(.ant-menu-horizontal) :deep(.ant-menu-submenu-title:active) {
  background-color: rgba(0, 0, 0, 0.06) !important;
}

:where(.u-layout-menu).ant-menu-light.ant-menu-inline :deep(.ant-menu-sub.ant-menu-inline) {
  background: rgba(0, 0, 0, 0.05);
}

.u-layout-menu-flat.ant-menu-inline :deep(.ant-menu-item),
.u-layout-menu-flat.ant-menu-vertical :deep(.ant-menu-item),
.u-layout-menu-flat.ant-menu-inline-collapsed :deep(.ant-menu-item) {
  margin-block: 0;
  margin-inline: 0;
  border-radius: 0;
  width: 100%;
  height: 46px;
  line-height: 46px;
}
.u-layout-menu-flat.ant-menu-inline :deep(.ant-menu-submenu) > .ant-menu-submenu-title,
.u-layout-menu-flat.ant-menu-vertical :deep(.ant-menu-submenu) > .ant-menu-submenu-title,
.u-layout-menu-flat.ant-menu-inline-collapsed :deep(.ant-menu-submenu) > .ant-menu-submenu-title {
  height: 46px !important;
  line-height: 46px !important;
  margin-block: 0;
  margin-inline: 0;
  border-radius: 0;
  width: 100%;
}

.u-layout-menu-flat.ant-menu-inline-collapsed :deep(.ant-menu-item) {
  padding-inline: calc(50% - 6px);
}
.u-layout-menu-flat.ant-menu-inline-collapsed :deep(.ant-menu-submenu) > .ant-menu-submenu-title {
  padding-inline: calc(50% - 6px);
}
</style>
