<template>
  <div
    ref="uLayoutRef" class="u-layout" :class="{
      'u-layout-default': config.layout === 'default',
      'u-layout-classic': config.layout === 'classic',
      'u-layout-topBar': config.layout === 'topBar'
    }"
  >
    <LayoutDefault v-if="config.layout === 'default'">
      <template #logo>
        <slot name="logo" />
      </template>
      <template #user>
        <slot name="user" />
      </template>
      <template #footer>
        <slot name="footer" />
      </template>
      <template #leftTool>
        <slot name="leftTool" />
      </template>
      <template #rightTool>
        <slot name="rightTool" />
      </template>
      <slot />
    </LayoutDefault>
    <LayoutTop v-else-if="config.layout === 'topBar'">
      <slot />
      <template #multiEnter>
        <slot name="multiEnter" />
      </template>
      <template #logo>
        <slot name="logo" />
      </template>
      <template #user>
        <slot name="user" />
      </template>
      <template #footer>
        <slot name="footer" />
      </template>
      <template #leftTool>
        <slot name="leftTool" />
      </template>
      <template #rightTool>
        <slot name="rightTool" />
      </template>
    </LayoutTop>
    <LayoutClassic v-else-if="config.layout === 'classic'">
      <template #multiEnter>
        <slot name="multiEnter" />
      </template>
      <template #logo>
        <slot name="logo" />
      </template>
      <template #user>
        <slot name="user" />
      </template>
      <template #footer>
        <slot name="footer" />
      </template>
      <template #leftTool>
        <slot name="leftTool" />
      </template>
      <template #rightTool>
        <slot name="rightTool" />
      </template>
      <slot />
    </LayoutClassic>
  </div>
</template>

<script lang="ts" setup>
import { computed, h, onMounted, onUnmounted, provide, ref, watch } from 'vue';
import { useFullscreen, useThrottleFn } from '@vueuse/core';
import { Icon } from '@iconify/vue';
import type {AntMenu, LayoutConfig, NavStyle, RouteMeta, ThemeColor} from '../types';
import {
  avatarKey,
  clickedMenusKey,
  collapsedKey,
  configKey,
  emitsKey,
  handleMenuClickKey,
  ipadFlagKey,
  isDarkKey,
  localeKey,
  menusKey,
  openKeysKey,
  phoneFlagKey,
  routeKey,
  routerKey,
  selectedKeysKey,
  themeColorKey,
  userIdKey,
  userToolsKey,
  usernameKey, navStyleKey
} from '../utils/injectedKeys';
import LayoutDefault from './layout/LayoutDefault.vue';
import LayoutClassic from './layout/LayoutClassic.vue';
import LayoutTop from './layout/LayoutTop.vue';

const props = withDefaults(
  defineProps<{
    config?: LayoutConfig;
    router: any;
    menus: AntMenu[];
    collapsed?: boolean;
    selectedKeys?: string[];
    openKeys?: string[];
    username: string;
    avatar?: string;
    userId: string;
  }>(),
  {
    collapsed: false,
    menus: () => [],
    menuIndent: 52,
    selectedKeys: () => [],
    openKeys: () => []
  }
);

const emits = defineEmits<{
  (e: 'update:collapsed', value: boolean): void;
  (e: 'update:config', value: LayoutConfig): void;
  (e: 'navStyleChange', value: NavStyle): void;
  (e: 'exit', value: boolean): void;
  (e: 'menuClick', value: any): void;
  (e: 'fullscreen', value: boolean): void;
  (e: 'toggleDark', value: boolean): void;
  (e: 'toggleLang', value: string): void;
  (e: 'downloadApp', value: void): void;
  (e: 'settings', value: boolean): void;
}>();

const uLayoutRef = ref<HTMLDivElement | null>(null);
const defaultConfig: LayoutConfig = {
  // global
  layout: 'default',
  isLogo: false,
  themeColor: {
    primary: '#247291',
    nav: '#262F3E'
  },
  // sidebar
  isSidebar: true,
  isMenuAccordion: false,
  menuIndent: 24,
  menuCollapsedWidth: '52px',
  menuStyle: 'smooth',
  navStyle: 'light',
  // header
  isNavbar: true,
  isHeaderMenuIcon: true,
  isBreadcrumb: true,
  isTagView: true,
  isToolbar: true,
  tagViewStyle: '卡片',
  isTagViewIcon: true,
  userTools: {
    fullscreen: true,
    dark: true,
    lang: true
  }
  // main
};

const config = ref({ ...defaultConfig, ...props.config });
provide(configKey, { config });
watch(
  () => config.value,
  () => {
    setRootPrimaryColor();
    // emits('update:config', config.value);
  }
);

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

const setRootPrimaryColor = () => {
  if (config.value.themeColor?.primary) {
    const rootEl = document.documentElement;
    rootEl.style.setProperty('--color-primary', config.value.themeColor?.primary);
  }
};

onMounted(setRootPrimaryColor);

/**
 * --------------------------- global variables ---------------------------
 */
const router = ref(props.router);
provide(routerKey, { router });
const username = ref(props.username);
provide(usernameKey, { username });
const avatar = ref(props.avatar);
provide(avatarKey, { avatar });
const userId = ref(props.userId);
provide(userIdKey, { userId });
const route = computed<Record<string, any>>(() => {
  return router.value.currentRoute;
});
provide(routeKey, { route });

const navStyleChange = (mode: NavStyle) => {
  emits('navStyleChange', mode);
};
provide(navStyleKey, { navStyleChange });

const themeColor = ref({ ...defaultConfig.themeColor!, ...(props.config?.themeColor || {}) });
const setThemeColor = (color: Partial<ThemeColor>) => {
  themeColor.value = { ...themeColor.value, ...color };
  config.value.themeColor = themeColor.value;
};
provide(themeColorKey, { themeColor, setThemeColor });

const userTools = ref({ ...defaultConfig.userTools, ...(props.config?.userTools || {}) });
provide(userToolsKey, { userTools });

/**
 * --------------------------- menu variables ---------------------------
 */
/**
 * 将传入的 menu 进行格式转换
 * @param menuList
 * @param parentNamePath
 * @param parentLabelPath
 */
const formatMenus = (menuList: AntMenu[], parentNamePath = '', parentLabelPath = '') => {
  return menuList
    .map(menu => {
      const namePath = `${parentNamePath}/${menu.name}`;
      const labelPath = `${parentLabelPath}/${menu.label}`;
      menu.key = menu.key || namePath;
      menu.labelPath = labelPath;
      const icon = menu.icon || menu.meta?.icon;
      if (icon && typeof icon === 'string') {
        Icon.name = 'UIcon';
        menu.icon = () => h(Icon, { icon });
        if (!menu.meta) menu.meta = { icon: menu.icon } as RouteMeta;
      }
      // 防止 icon 为 VNode 时 tagView v-if 不渲染
      if (typeof icon === 'function') {
        if (menu.meta) {
          menu.meta.icon = icon;
        } else {
          menu.meta = { icon } as RouteMeta;
        }
      }
      if (!icon) {
        menu.icon = () =>
          h(
            'span',
            {
              style: { width: '1em' }
            },
            // menu.label.at(0)?.toUpperCase() || ''
            ''
          );
      }
      if (menu.children?.length) menu.children = formatMenus(menu.children, namePath, labelPath);
      return menu;
    })
    .filter(i => i) as AntMenu[];
};

const menus = ref(formatMenus(props.menus));
watch(() => props.menus, () => {
  menus.value = formatMenus(props.menus);
}, { deep: true });
provide(menusKey, { menus });
const clickedMenus = ref<AntMenu[]>([]);
const pushClickedMenus = (menu: AntMenu) => {
  if (!menu) return;
  const exist = clickedMenus.value.some(item => item.key === menu.key);
  !exist && clickedMenus.value.push(menu);
};
const removeOtherClickedMenus = (menu: AntMenu) => {
  router.value.push(menu.key);
  clickedMenus.value = clickedMenus.value.filter(item => item.key === menu.key);
};
const filterClickedMenus = (menu: AntMenu) => {
  const index = clickedMenus.value.findIndex(item => item.key === menu.key);
  if (index === clickedMenus.value.length - 1) {
    router.value.push(clickedMenus.value[index - 1].key);
  } else {
    router.value.push(clickedMenus.value[index + 1].key);
  }
  clickedMenus.value = clickedMenus.value.filter(item => item.key !== menu.key);
};
provide(clickedMenusKey, { clickedMenus, pushClickedMenus, filterClickedMenus, removeOtherClickedMenus });
const selectedKeys = ref(props.selectedKeys);
provide(selectedKeysKey, { selectedKeys });
const openKeys = ref(props.openKeys);
provide(openKeysKey, { openKeys });

const handleMenuClick = (value: any) => {
  emits('menuClick', value);
};
provide(handleMenuClickKey, { handleMenuClick });

const collapsed = ref(props.collapsed);
const setCollapsed = (_collapsed: boolean) => {
  collapsed.value = _collapsed;
  emits('update:collapsed', _collapsed);
};
provide(collapsedKey, { collapsed, setCollapsed });
watch(
  () => props.collapsed,
  () => {
    collapsed.value = props.collapsed;
  },
  {
    deep: true
  }
);

/**
 * --------------------------- global events ---------------------------
 */
const exit = () => {
  emits('exit', true);
};
const { isFullscreen, toggle } = useFullscreen(uLayoutRef);
const fullscreen = async (flag: boolean) => {
  await toggle();
  flag = isFullscreen.value;
  emits('fullscreen', flag);
};
const isDark = ref(config.value.isDark);
const toggleDark = (dark: boolean) => {
  isDark.value = dark;
  emits('update:config', { ...config.value, isDark: dark });
  emits('toggleDark', dark);
};
provide(isDarkKey, { isDark });

const locale = ref(config.value.locale);
const toggleLocale = (lang: string) => {
  locale.value = lang;
  emits('update:config', { ...config.value, locale: lang });
  emits('toggleLang', lang);
};
provide(localeKey, { locale });

const downloadApp = () => {
  emits('downloadApp');
};

provide(emitsKey, { exit, fullscreen, toggleDark, toggleLocale, downloadApp });

watch(
  () => props.config,
  () => {
    if (props.config?.themeColor) themeColor.value = props.config.themeColor;
    if (props.config) config.value = props.config;
  },
  {
    deep: true
  }
);

const ipadFlag = ref(false);
provide(ipadFlagKey, { ipadFlag });
const phoneFlag = ref(false);
provide(phoneFlagKey, { phoneFlag });

const screenAdapter = () => {
  ipadFlag.value = window.innerWidth <= 768;
  phoneFlag.value = window.innerWidth <= 520;
  if (ipadFlag.value && config.value.layout !== 'topBar') setCollapsed(true);
  if (userTools.value) {
    userTools.value.lang = !phoneFlag.value;
    config.value.isBreadcrumb = !phoneFlag.value;
  }
};
screenAdapter();

defineExpose({
  ref: uLayoutRef
});
const screenAdapterThrottle = useThrottleFn(screenAdapter, 300);

window.addEventListener('resize', screenAdapterThrottle);
onUnmounted(() => window.removeEventListener('resize', () => {}));
</script>

<style scoped>
.u-layout {
  position: relative;
  display: flex;
  width: 100%;
  height: 100%;
  min-width: 375px;
  overflow: hidden;
}
</style>
