<script lang="ts" setup>
import type { BreadcrumbProps } from 'ant-design-vue';
import { cloneDeep } from 'lodash-es';
import { computed, defineModel, h, onMounted, onUnmounted, provide, ref, useSlots, watch } from 'vue';
import { useFullscreen, useThrottleFn } from '@vueuse/core';
import { Icon } from '@iconify/vue';
import { generateEachPathByPath } from '../../utils';
import {
  accountKey,
  avatarKey,
  breadcrumbKey,
  collapsedKey,
  configKey,
  emitsKey,
  handleMenuClickKey,
  ipadFlagKey,
  isDarkKey,
  localeKey,
  menusKey,
  navStyleKey,
  openKeysKey,
  phoneFlagKey,
  routeKey,
  routerKey,
  selectedKeysKey,
  tagsKey,
  themeColorKey, userIdKey, userToolsKey, usernameKey
} from '../../utils/injectedKeys';
import type { AntMenu, LayoutConfig, NavStyle, RouteMeta, ThemeColor } from './types';
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[];
    tags?: AntMenu[];
    collapsed?: boolean;
    selectedKeys?: string[];
    openKeys?: string[];
    username: string;
    account: string;
    avatar?: string;
    userId: string;
  }>(),
  {
    collapsed: false,
    menus: () => [],
    tags: () => [],
    menuIndent: 52,
    selectedKeys: () => [],
    openKeys: () => []
  }
);

const emits = defineEmits<{
  (e: 'update:collapsed', value: boolean): void;
  (e: 'update:tags', value: AntMenu[]): 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: 'updatePwd', value: void): void;
  (e: 'settings', value: boolean): void;
}>();

const slots = useSlots();
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,
  () => {
    console.log('config.value');
    setRootPrimaryColor();
    // emits('update:config', config.value);
  }
);

watch(() => props.config, () => {
  console.log('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 account = ref(props.account);
provide(accountKey, { account });
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 = '') => {
  const cloneMenus = cloneDeep(menuList);
  return cloneMenus
    .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 = computed(() => formatMenus(props.menus));
provide(menusKey, menus);

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 = defineModel('collapsed', {
  required: false,
  default: false
});
const setCollapsed = (_collapsed: boolean) => {
  collapsed.value = _collapsed;
};
provide(collapsedKey, { collapsed, setCollapsed });

/**
 * --------------------------- nav variables ---------------------------
 */
const tags = ref(props.tags);
watch(() => props.tags, () => {
  tags.value = props.tags;
});
watch(() => tags.value, () => {
  emits('update:tags', tags.value);
});
const pushTags = (menu: AntMenu) => {
  if (!menu) return;
  const exist = tags.value.some(item => item.key === menu.key);
  !exist && tags.value.push(menu);
};
const removeOtherTags = (menu: AntMenu) => {
  router.value.push(menu.key);
  tags.value = tags.value.filter(item => item.key === menu.key);
};
const filterTags = (menu: AntMenu) => {
  const index = tags.value.findIndex(item => item.key === menu.key);
  if (index === tags.value.length - 1) {
    router.value.push(tags.value[index - 1].key);
  } else {
    router.value.push(tags.value[index + 1].key);
  }
  tags.value = tags.value.filter(item => item.key !== menu.key);
};
provide(tagsKey, { tags, pushTags, filterTags, removeOtherTags });

const breadcrumb = ref<BreadcrumbProps['routes']>([]);
const setBreadcrumb = (_breadcrumb: BreadcrumbProps['routes']) => {
  breadcrumb.value = _breadcrumb;
};
provide(breadcrumbKey, { breadcrumb, setBreadcrumb });
const currentMenu = computed(() => tags.value?.find(menu => menu.key === route.value.path));

const updateBreadcrumb = () => {
  setBreadcrumb([]);
  if (!route.value) return;
  const routes: BreadcrumbProps['routes'] = [];
  const paths = generateEachPathByPath(route.value.path, 0);
  routes.push({
    path: '/',
    breadcrumbName: currentMenu.value?.label || ''
  });
  paths.forEach((path, index) => {
    const labelPath = currentMenu.value?.labelPath;
    const labelPathSplit = labelPath?.split('/').filter(i => i);
    const keyPath = currentMenu.value?.key;
    const keyPathSplit = keyPath?.split('/').filter(i => i);
    const breadcrumbName = labelPathSplit?.[index] || keyPathSplit?.[index];
    if (!breadcrumbName) return;
    routes!.push({
      path,
      breadcrumbName
    });
  });
  setBreadcrumb(routes);
};
watch(
  () => currentMenu.value,
  () => {
    updateBreadcrumb();
  },
  { immediate: 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');
};

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

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

watch(
  () => props.config,
  () => {
    console.log('update themeColor');
    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,
  addTag: pushTags,
  tags
});
const screenAdapterThrottle = useThrottleFn(screenAdapter, 300);

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

<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 v-for="(item, key) in slots" :key="key" #[key]="slotProps">
        <slot :name="key" v-bind="slotProps" :breadcrumb="breadcrumb" />
      </template>
    </LayoutDefault>
    <LayoutTop v-else-if="config.layout === 'topBar'">
      <template v-for="(item, key) in slots" :key="key" #[key]="slotProps">
        <slot :name="key" v-bind="slotProps" :breadcrumb="breadcrumb" />
      </template>
    </LayoutTop>
    <LayoutClassic v-else-if="config.layout === 'classic'">
      <template v-for="(item, key) in slots" :key="key" #[key]="slotProps">
        <slot :name="key" v-bind="slotProps" :breadcrumb="breadcrumb" />
      </template>
    </LayoutClassic>
  </div>
</template>

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