<script setup lang="ts">
import { SettingsDrawer, SyncCameraResultModal } from '@/components';
import { useLineStationsQuery, useStompClient, useVerifyUserQuery } from '@/composables';
import { useAlarmStore, useSettingStore, useUserStore } from '@/stores';
import { parseErrorFeedback } from '@/utils';
import { useIsFetching, useIsMutating, useMutation } from '@tanstack/vue-query';
import { AlertFilled, CaretDownFilled, DoubleLeftOutlined, DoubleRightOutlined, EnvironmentFilled, FileTextFilled, HddFilled, LogoutOutlined, SettingOutlined } from '@vicons/antd';
import { isCancel } from 'axios';
import {
  NBadge,
  NButton,
  NDropdown,
  NFlex,
  NIcon,
  NLayout,
  NLayoutContent,
  NLayoutFooter,
  NLayoutHeader,
  NLayoutSider,
  NMenu,
  type DropdownOption,
  type DropdownProps,
  type MenuOption,
} from 'naive-ui';
import { storeToRefs } from 'pinia';
import { computed, h, ref, watchEffect, type Component, type VNode } from 'vue';
import { RouterLink, useRoute, useRouter } from 'vue-router';

const route = useRoute();
const router = useRouter();

const userStore = useUserStore();
const { userInfo } = storeToRefs(userStore);

const alarmStore = useAlarmStore();
const { unreadAlarmCount } = storeToRefs(alarmStore);

const settingStore = useSettingStore();
const { menuCollpased, offlineDev } = storeToRefs(settingStore);

const { syncCameraResult, afterCheckSyncCameraResult } = useStompClient();

useVerifyUserQuery();
useLineStationsQuery();

// 带key的query和mutation用于全局轮询 可用于渲染loading状态
const fetchingCount = useIsFetching({ predicate: (query) => !!query.options.queryKey });
const mutatingCount = useIsMutating({ predicate: (mutation) => !!mutation.options.mutationKey });
const loadingCount = computed(() => fetchingCount.value + mutatingCount.value);

const onToggleMenuCollapsed = () => {
  menuCollpased.value = !menuCollpased.value;
};

const menuOptions: MenuOption[] = [
  {
    label: () => h(RouterLink, { to: '/station' }, { default: () => '车站状态' }),
    key: '/station',
    icon: renderIcon(EnvironmentFilled),
  },
  {
    label: () => h(RouterLink, { to: '/device' }, { default: () => '设备诊断' }),
    key: '/device',
    icon: renderIcon(HddFilled),
  },
  {
    label: () => h(RouterLink, { to: '/alarm' }, { default: () => '设备告警' }),
    key: '/alarm',
    icon: renderIcon(AlertFilled),
  },
  {
    label: '系统日志',
    key: '/log',
    icon: renderIcon(FileTextFilled),
    children: [
      {
        label: () => h(RouterLink, { to: '/log/vimp-log' }, { default: () => '视频平台日志' }),
        key: '/log/vimp-log',
      },
      {
        label: () => h(RouterLink, { to: '/log/call-log' }, { default: () => '上级调用日志' }),
        key: '/log/call-log',
      },
    ],
  },
];

const dropdownOptions: DropdownOption[] = [
  {
    label: '退出登录',
    key: 'logout',
    icon: renderIcon(LogoutOutlined),
    onClick: async () => {
      try {
        await userStore.userLogout();
        router.push({ path: '/login' });
      } catch (error) {
        console.error(error);
        window.$message.error('退出登录失败');
      }
    },
  },
];

const onSelectDropdownOption: DropdownProps['onSelect'] = (value: string, option: DropdownOption) => {
  if (typeof option['onClick'] === 'function') {
    option['onClick']();
  }
};

const showSettingsDrawer = ref(false);
const openSettingsDrawer = () => {
  showSettingsDrawer.value = true;
};

const routeToRoot = () => {
  router.push({ path: '/' });
};

const routeToAlarmPage = () => {
  alarmStore.clearUnreadAlarms();
  if (route.path !== '/alarm') {
    router.push({ path: '/alarm' });
  }
};

const { mutate: getUserInfo } = useMutation({
  mutationFn: async (params?: { signal?: AbortSignal }) => {
    const { signal } = params ?? {};
    await userStore.userGetInfo({ signal });
  },
  onError: (error) => {
    if (isCancel(error)) return;
    console.error(error);
    const errorFeedback = parseErrorFeedback(error);
    window.$message.error(errorFeedback);
  },
});

// 判断是否为离线开发模式 决定是否自动发送获取用户信息请求
watchEffect((onCleanup) => {
  if (offlineDev.value) return;
  const abortController = new AbortController();
  getUserInfo({ signal: abortController.signal });
  onCleanup(() => abortController.abort());
});

function renderIcon(icon: Component): () => VNode {
  return () => h(NIcon, null, { default: () => h(icon) });
}
</script>

<template>
  <NLayout has-sider style="min-width: 1440px">
    <!-- 左侧菜单 -->
    <NLayoutSider bordered :collapsed="menuCollpased" collapse-mode="width" :collapsed-width="64" @update:collapsed="onToggleMenuCollapsed">
      <NFlex vertical justify="space-between" :size="0" style="height: 100%">
        <NMenu :collapsed="menuCollpased" :collapsed-width="64" :collapsed-icon-size="20" :value="route.path" :options="menuOptions" />
        <NButton block quaternary :focusable="false" @click="onToggleMenuCollapsed">
          <template #icon>
            <NIcon :component="menuCollpased ? DoubleRightOutlined : DoubleLeftOutlined" />
          </template>
        </NButton>
      </NFlex>
    </NLayoutSider>
    <!-- 主体内容区域 -->
    <NLayout>
      <NLayoutHeader bordered class="app-layout-header">
        <NFlex justify="space-between" align="center" :size="8" style="width: 100%; height: 100%">
          <NFlex align="center">
            <h3 style="margin: 0 0 0 16px; cursor: pointer" @click="routeToRoot">网络设备管理平台</h3>
            <NButton text size="tiny" :loading="loadingCount > 0"></NButton>
          </NFlex>
          <NFlex align="center" :size="0" style="height: 100%">
            <NDropdown trigger="hover" show-arrow :options="dropdownOptions" @select="onSelectDropdownOption">
              <NButton :focusable="false" quaternary icon-placement="right" style="height: 100%">
                <template #default>
                  <span>{{ userInfo?.nickName ?? '' }}</span>
                </template>
                <template #icon>
                  <NIcon :component="CaretDownFilled" />
                </template>
              </NButton>
            </NDropdown>
            <NButton quaternary :focusable="false" style="height: 100%" @click="openSettingsDrawer">
              <template #icon>
                <NIcon :component="SettingOutlined" />
              </template>
            </NButton>
          </NFlex>
        </NFlex>
      </NLayoutHeader>
      <NLayoutContent class="app-layout-content">
        <RouterView />
      </NLayoutContent>
      <NLayoutFooter bordered class="app-layout-footer">
        <NFlex :align="'center'" style="height: 100%; margin: 0 16px">
          <NBadge :value="unreadAlarmCount">
            <NButton secondary strong @click="routeToAlarmPage">
              <template #icon>
                <NIcon :component="AlertFilled" />
              </template>
            </NButton>
          </NBadge>
        </NFlex>
      </NLayoutFooter>
    </NLayout>
  </NLayout>

  <SettingsDrawer v-model:show="showSettingsDrawer" />

  <SyncCameraResultModal :sync-camera-result="syncCameraResult" @after-leave="afterCheckSyncCameraResult" />
</template>

<style scoped lang="scss">
$layout-header-height: 48px;
$layout-footer-height: 48px;

.app-layout-header {
  height: $layout-header-height;
}

.app-layout-content {
  height: calc(100vh - $layout-header-height - $layout-footer-height);
}

.app-layout-footer {
  height: $layout-footer-height;
}
</style>
