<script setup lang="ts">
import { computed, inject, onBeforeUnmount, onMounted, ref, useTemplateRef } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { message } from 'ant-design-vue';
import Simplebar from 'simplebar-vue';

import ConfirmModal from '@/components/ConfirmModal.vue';
import SvgIcon from '@/components/SvgIcon.vue';
import { useRequest } from '@/hooks/request';
import { useUserInfoStore } from '@/stores/user-info';
import { dataCenterRoutes, opsCenterRoutes } from '@/router';
import { t } from '@/i18n';
import {
  addRevisePassword,
  getDownloadLogo,
  getLoginData,
  getNoticePageList,
  getPageList,
  getUnreadNotifications,
} from '@/api';
import { translateNavigation } from '@/utils';
import { removeToken } from '@/utils/auth';
import { SET_COLOR_PRIMARY } from '@/constants/inject-key';

import type { RouteRecordRaw } from 'vue-router';
import type { FormInstance, Rule } from 'ant-design-vue/es/form';
import type { MenuInfo } from 'ant-design-vue/es/menu/src/interface';
import type { ChangePasswordForm, DeviceGroupItem, NoticePageItem, PageParams } from '@/types';

const router = useRouter();
const route = useRoute();
const { permission, resetToken } = useUserInfoStore();
const setColorPrimary = inject(SET_COLOR_PRIMARY, undefined);
const menuRef = useTemplateRef('menu');
const selectedKeys = ref<string[]>([route.path]);
const openKeys = ref<string[]>([]);
const deviceGroupList = ref<DeviceGroupItem[]>([]);
const messageOpen = ref<boolean>(false);
const changePasswordOpen = ref<boolean>(false);
const messageList = ref<NoticePageItem[]>([]);
const formRef = ref<FormInstance>();
const imageUrl = ref<string>();
const modalComponentRef = useTemplateRef('modalComponent');
const userName = ref<string>('');
const changePasswordForm = ref<ChangePasswordForm>({
  rawPassword: '',
  oldRawPassword: '',
  confirmPassword: '',
});
const messageParam = ref<PageParams>({
  pageIndex: 1,
  pageSize: 10,
});
const messageTotal = ref<number>();
const { handleRequest } = useRequest();
let timer: number | null = null; // 保存定时器ID
const countNumber = ref<number>();

// 递归过滤函数
const filterRoutes = (routeList: Readonly<RouteRecordRaw[]>) => {
  return routeList.filter((route) => {
    const routeHasPermission =
      route.meta &&
      permission
        ?.split(',')
        .map(Number)
        .includes(route.meta.permission as number);
    let childrenHavePermission = false;

    // 如果有子路由，递归过滤
    if (routeHasPermission) {
      if (route.children && route.children.length > 0) {
        const filteredChildren: RouteRecordRaw[] = filterRoutes(route.children);
        childrenHavePermission = filteredChildren.length > 0;

        // 更新子路由为过滤后的结果
        if (childrenHavePermission) {
          route.children = filteredChildren;
        }
      }
    }

    // 只有当路由自身有权限或子路由有权限才保留
    return routeHasPermission || childrenHavePermission;
  });
};
const menuGroupList = computed(() => {
  return [
    {
      category: t('common.dataCenter'),
      routes: filterRoutes(dataCenterRoutes),
    },
    {
      category: t('common.opsCenter'),
      routes: filterRoutes(opsCenterRoutes),
    },
  ];
});

const rules: Record<string, Rule[]> = {
  rawPassword: [{ required: true, message: t('common.cannotEmpty'), trigger: 'change' }],
  oldRawPassword: [{ required: true, message: t('common.cannotEmpty'), trigger: 'change' }],
  confirmPassword: [
    { required: true, message: t('common.cannotEmpty'), trigger: 'change' },
    {
      validator: (_rule: unknown, value: string) => {
        if (changePasswordForm.value.rawPassword !== value) {
          return Promise.reject(t('hcacAside.newPasswordError'));
        }
        return Promise.resolve();
      },
    },
  ],
};

const messageColumns = [
  {
    title: t('alarmManage.alarmContent'),
    dataIndex: 'content',
    key: 'content',
    ellipsis: true,
  },
  {
    title: t('hcacAside.deviceEventName'),
    dataIndex: 'name',
    key: 'name',
    ellipsis: true,
  },
  {
    title: t('hcacAside.deviceMonitoringPoint'),
    dataIndex: 'dev',
    key: 'dev',
    ellipsis: true,
  },
  {
    title: t('common.time'),
    dataIndex: 'createTime',
    key: 'createTime',
    ellipsis: true,
  },
];

const getNoticeNumber = () => {
  handleRequest(async () => {
    countNumber.value = await getUnreadNotifications();
  });
};

// 启动定时器
const startTimer = () => {
  timer = setInterval(() => {
    getNoticeNumber();
  }, 10000) as unknown as number;
};

onMounted(async () => {
  try {
    const data = await getPageList();
    const { userName: name, orgLogoPath, orgThemeColor } = await getLoginData();
    if (orgThemeColor) {
      setColorPrimary?.(orgThemeColor);
    }

    if (orgLogoPath) {
      const data = await getDownloadLogo(orgLogoPath);
      imageUrl.value = URL.createObjectURL(data as Blob);
    }
    userName.value = name[0];
    deviceGroupList.value = data.filter((item) => item.deviceGroupChilds.length > 0);
  } catch (err) {
    if (err instanceof Error) {
      message.error(err.message);
    }

    console.error(err);
  }
  getNoticeNumber();
  startTimer();
  openSelectedSubMenu();
});

// 组件卸载前清理定时器
onBeforeUnmount(() => {
  if (timer) clearInterval(timer);
});

const openSelectedSubMenu = () => {
  setTimeout(() => {
    const menuEl = menuRef.value?.$el as HTMLElement | undefined;
    const selectedSubMenuEl = menuEl?.querySelector<HTMLElement>('.ant-menu-submenu-selected');
    const selectedSubMenuId = selectedSubMenuEl?.dataset.submenuId;

    if (selectedSubMenuId) {
      openKeys.value.push(selectedSubMenuId);
    }
  }, 0);
};

const DEVICE_GROUP_ROUTE_PATH = '/ai-smart-ctrl/device-group';

const getDeviceGroupMenuKey = (id: number) => {
  return `${DEVICE_GROUP_ROUTE_PATH}/${id}`;
};

const handleMenuClick = (menu: MenuInfo) => {
  const key = menu.key as string;

  if (key.includes(DEVICE_GROUP_ROUTE_PATH)) {
    router.push({
      path: key,
      query: {
        ...route.query,
      },
    });

    return;
  }

  router.push(key);
};

const collapsed = ref<boolean>(false);
const getNoticeList = () => {
  handleRequest(async () => {
    const { records, total } = await getNoticePageList(messageParam.value);
    messageList.value = records;
    messageTotal.value = total;
  });
};
const toggleCollapsed = () => {
  collapsed.value = !collapsed.value;
};
const addInformation = () => {
  getNoticeList();
  messageOpen.value = true;
};
const messageChange = () => {
  getNoticeList();
};
const clickNavigateMonitor = (monitorId: number, parentDevGroupId: number, devGroupId: number) => {
  if (monitorId) {
    router.push({
      path: '/env-monitor/index',
      query: { monitorId, parentDevGroupId, devGroupId }, // 查询参数
    });
    messageOpen.value = false;
  }
};
const clickNavigateGroup = (devGroupId: number) => {
  if (devGroupId) {
    router.push(`/ai-smart-ctrl/device-group/${devGroupId}`);
    messageOpen.value = false;
  }
};
const clickExit = () => {
  modalComponentRef.value?.showView();
};
const confirm = () => {
  removeToken();
  resetToken();
  router.push('/login');
  setColorPrimary?.('#32BAC0');
  modalComponentRef.value?.hideView();
};

const changePassword = () => {
  changePasswordOpen.value = true;
};
const handleClose = () => {
  changePasswordForm.value = {
    rawPassword: '',
    oldRawPassword: '',
    confirmPassword: '',
  };
  formRef.value?.resetFields();
};
const bindingPassword = () => {
  formRef.value?.validate().then(() => {
    handleRequest(async () => {
      const { rawPassword, oldRawPassword } = changePasswordForm.value;
      await addRevisePassword({
        rawPassword,
        oldRawPassword,
      });
      message.success(t('common.modifySuccess'));
      confirm();
      changePasswordOpen.value = false;
    });
  });
};
</script>

<template>
  <ALayoutSider
    class="aside-container"
    v-model:collapsed="collapsed"
    collapsible
    :collapsed-width="64"
    :trigger="null"
    :width="246"
  >
    <div class="aside-header">
      <img v-if="imageUrl" class="aside-header-logo" :src="imageUrl" />
      <img v-else class="aside-header-logo" src="@/assets/img/logo.png" />
      <span v-show="!collapsed" class="aside-header-title">{{ $t('common.unimatIoT') }}</span>
    </div>
    <Simplebar class="aside-scroll">
      <AMenu
        ref="menu"
        class="aside-menu"
        v-model:selected-keys="selectedKeys"
        v-model:open-keys="openKeys"
        mode="inline"
        @click="handleMenuClick"
      >
        <div class="aside-menu-category">{{ $t('common.aiCtrl') }}</div>
        <template v-for="item in deviceGroupList" :key="item.id">
          <ASubMenu
            v-if="item.deviceGroupChilds?.length"
            :key="getDeviceGroupMenuKey(item.id)"
            :title="item.groupName"
            popup-class-name="aside-menu-submenu-popup"
          >
            <template #icon>
              <SvgIcon name="air-conditioning" />
            </template>
            <AMenuItem v-for="subItem in item.deviceGroupChilds" :key="getDeviceGroupMenuKey(subItem.id)">
              {{ subItem.groupName }}
            </AMenuItem>
          </ASubMenu>
          <template v-else>
            <AMenuItem :key="getDeviceGroupMenuKey(item.id)" :title="item.groupName">
              <template #icon>
                <SvgIcon name="air-conditioning" />
              </template>
              {{ item.groupName }}
            </AMenuItem>
          </template>
        </template>
        <template v-for="(item, index) in menuGroupList" :key="index">
          <div class="aside-menu-category">{{ item.category }}</div>
          <template v-for="{ path, meta, children } in item.routes" :key="path">
            <template v-if="meta && !meta.hideInMenu">
              <ASubMenu
                v-if="!meta.hideSubMenu && children"
                :key="path"
                :title="translateNavigation(meta.title)"
                popup-class-name="aside-menu-submenu-popup"
              >
                <template #icon>
                  <SvgIcon v-if="meta.icon" :name="meta.icon" />
                </template>
                <AMenuItem v-for="{ path: subPath, meta } in children" :key="`${path}/${subPath}`">
                  {{ translateNavigation(meta?.title) }}
                </AMenuItem>
              </ASubMenu>
              <AMenuItem v-else :key="`${path}/index`" :title="translateNavigation(meta.title)">
                <template #icon>
                  <SvgIcon v-if="meta.icon" :name="meta.icon" />
                </template>
                {{ translateNavigation(meta.title) }}
              </AMenuItem>
            </template>
          </template>
        </template>
      </AMenu>
    </Simplebar>
    <div v-show="collapsed" class="aside-collapsed-icons">
      <ABadge :count="countNumber">
        <SvgIcon name="information" @click="addInformation" />
      </ABadge>

      <SvgIcon name="setting" />
      <SvgIcon name="unfold" @click="toggleCollapsed" />
    </div>
    <div class="aside-footer">
      <ADropdown
        overlay-class-name="hvac-dropdown"
        placement="topLeft"
        :align="{ offset: [0, -30] }"
        :trigger="['click']"
      >
        <div class="aside-footer-avatar" @click.prevent>
          <span class="aside-footer-text">{{ userName }}</span>
        </div>
        <template #overlay>
          <AMenu>
            <AMenuItem>
              <div @click="changePassword">
                <AFlex class="hvac-item" justify="space-between" align="center">
                  <AFlex align="center"
                    ><SvgIcon class="icon-style" name="edit-o" /> {{ t('hcacAside.changePassword') }}</AFlex
                  >
                  <SvgIcon name="right"
                /></AFlex>
              </div>
            </AMenuItem>
            <AMenuDivider />
            <AMenuItem>
              <div @click="clickExit">
                <AFlex class="hvac-item" align="center">
                  <SvgIcon name="a-log-out" class="icon-style" /> {{ t('hcacAside.logout') }}
                </AFlex>
              </div>
            </AMenuItem>
          </AMenu>
        </template>
      </ADropdown>

      <div v-show="!collapsed">
        <SvgIcon name="setting" />
        <span @click="addInformation">
          <ABadge :count="countNumber">
            <SvgIcon name="information" />
          </ABadge>
        </span>

        <SvgIcon name="fold" @click="toggleCollapsed" />
      </div>
    </div>
  </ALayoutSider>
  <AModal
    v-model:open="messageOpen"
    :title="t('hcacAside.messageCenter')"
    width="926px"
    :footer="null"
    :keyboard="false"
  >
    <ATable class="table-margin" :columns="messageColumns" :data-source="messageList" :pagination="false">
      <template #bodyCell="{ column, record }">
        <template v-if="column.key === 'name'">
          <span class="text-style" v-if="record.type === 1" @click="clickNavigateGroup(record.devGroupId)">{{
            record.groupName
          }}</span>
        </template>
        <template v-if="column.key === 'dev'">
          <span
            class="text-style"
            v-if="record.type === 1"
            @click="clickNavigateMonitor(record.monitorId, record.parentDevGroupId, record.devGroupId)"
            >{{ record.monitorName }}</span
          >
        </template>
      </template>
    </ATable>
    <AFlex justify="flex-end" class="footer">
      <APagination
        v-model:current="messageParam.pageIndex"
        v-model:page-size="messageParam.pageSize"
        :total="messageTotal"
        :show-size-changer="true"
        @change="messageChange"
        show-quick-jumper
        :show-total="(total) => $t('common.pageTotal', { total })"
      />
    </AFlex>
  </AModal>
  <AModal
    v-model:open="changePasswordOpen"
    :title="t('hcacAside.changePassword')"
    width="460px"
    :footer="null"
    :mask-closable="false"
    :keyboard="false"
    :after-close="handleClose"
  >
    <AForm
      ref="formRef"
      :colon="false"
      label-align="left"
      :model="changePasswordForm"
      :rules="rules"
      :label-col="{ span: 6 }"
      class="form-style"
    >
      <AFlex :vertical="true">
        <AFormItem :label="t('hcacAside.originalPassword')" name="oldRawPassword">
          <AInput
            class="input-width"
            v-model:value="changePasswordForm.oldRawPassword"
            :placeholder="t('hcacAside.pleaseEnterOriginalPassword')"
          />
        </AFormItem>
        <AFormItem :label="t('hcacAside.newPassword')" name="rawPassword">
          <AInput
            class="input-width"
            v-model:value="changePasswordForm.rawPassword"
            :placeholder="t('hcacAside.pleaseEnterNewPassword')"
          />
        </AFormItem>
        <AFormItem :label="t('hcacAside.confirmNewPassword')" name="confirmPassword">
          <AInput
            class="input-width"
            v-model:value="changePasswordForm.confirmPassword"
            :placeholder="t('hcacAside.pleaseEnterNewPassword')"
          />
        </AFormItem>
      </AFlex>
    </AForm>

    <AFlex justify="flex-end">
      <AButton class="button-style" type="primary" ghost @click="changePasswordOpen = false">{{
        $t('common.cancel')
      }}</AButton>
      <AButton class="button-style" type="primary" @click="bindingPassword">{{ t('common.certain') }}</AButton>
    </AFlex>
  </AModal>
  <ConfirmModal
    ref="modalComponent"
    :title="t('common.exitConfirmation')"
    :description-text="t('common.confirmExitPrompt')"
    :icon="{ name: 'a-log-out' }"
    :icon-bg-color="'#F56C6C'"
    @confirm="confirm"
  />
</template>

<style lang="scss">
.aside-menu-submenu-popup.ant-menu-submenu-popup {
  .ant-menu-item:not(.ant-menu-item-selected):hover {
    color: var(--antd-color-primary);
    background-color: initial;
  }

  .ant-menu-item-selected {
    background-color: initial;
  }
}
</style>

<style lang="scss" scoped>
.form-style {
  margin-top: 24px;
}

.button-style {
  width: 76px;
  margin-left: 16px;
}

.input-width {
  width: 256px;
}

.hvac-item {
  width: 190px;
  height: 50px;
}

.icon-style {
  margin-right: 12px;
}

.hvac-dropdown > ul > li {
  width: 190px;
  margin-left: 10px !important;
}

.hvac-dropdown {
  bottom: 20px;
}

.text-style {
  font-size: 14px;
  font-style: normal;
  font-weight: 400;
  line-height: 22px;
  color: var(--antd-color-primary);
  text-align: left;
  text-decoration-line: underline;
  cursor: pointer;
}

:deep(.aside-collapsed-icons) {
  .ant-badge .ant-badge-count {
    min-width: 15px;
    height: 15px;
    margin-top: 10px;
    margin-right: 14px;
    font-size: 10px;
    line-height: 15px;
    cursor: pointer;
  }
}

:deep(.aside-footer) {
  .ant-badge .ant-badge-count {
    min-width: 15px;
    height: 15px;
    font-size: 10px;
    line-height: 15px;
    cursor: pointer;
  }

  .ant-badge {
    margin: 0 15px;
  }
}

.table-margin {
  margin: 24px 0;
}

.aside-container {
  --aside-border-radius: 18px;
  --aside-padding: 12px;

  background-color: var(--hvac-layout-bg);

  :deep(.ant-layout-sider-children) {
    display: flex;
    flex-direction: column;
  }

  :deep(.aside-menu) {
    flex: 1;
    border-inline-end: none;

    & > .ant-menu-item,
    .ant-menu-submenu-title {
      padding-left: 12px !important;
    }

    .ant-menu-item,
    .ant-menu-submenu-title {
      width: calc(100%);
      height: 40px;
      padding-inline: 12px;
      margin-block: 0;
      margin-inline: 0;
      overflow: hidden;
      line-height: 40px;
      text-overflow: ellipsis;

      .ant-menu-item-icon {
        font-size: 16px;

        + span {
          margin-inline-start: 8px;
        }
      }
    }

    & > .ant-menu-submenu,
    & > .ant-menu-item {
      & + .ant-menu-submenu,
      & + .ant-menu-item {
        margin-top: 2px;
      }
    }

    .ant-menu-submenu-title + .ant-menu-sub > li:first-child {
      margin-top: 8px;
    }

    .ant-menu-submenu-arrow {
      right: var(--aside-padding);

      &::before,
      &::after {
        height: 1px;
      }
    }

    .ant-menu-sub.ant-menu-inline {
      background: var(--antd-color-bg-base);
    }

    .ant-menu-sub .ant-menu-item {
      padding-left: 34px !important;

      &::before {
        position: absolute;
        left: 16px;
        height: 100%;
        content: '';
        border: 1px solid var(--antd-color-primary-opacity-15);
      }

      &.ant-menu-item-selected::before {
        border-color: var(--antd-color-primary);
      }
    }

    .ant-menu-item-selected {
      background-color: var(--antd-color-bg-base);
    }

    .ant-menu-submenu-selected > .ant-menu-submenu-title {
      background-color: var(--antd-color-primary-opacity-15);
    }

    .ant-menu-item:not(.ant-menu-item-selected):hover,
    .ant-menu-submenu-title:hover {
      color: var(--antd-color-primary);
      background-color: initial;
    }

    & > .ant-menu-item.ant-menu-item-selected {
      background-color: var(--antd-color-primary-opacity-15);
    }

    .ant-menu-title-content {
      display: flex;
      align-items: center;
      font-weight: 500;
    }
  }
}

.aside-header {
  display: flex;
  align-items: center;
  padding: 24px var(--aside-padding);
  background-color: var(--antd-color-bg-base);
  border-top-left-radius: var(--aside-border-radius);
  border-top-right-radius: var(--aside-border-radius);
}

.aside-header-logo {
  width: 40px;
  height: 40px;
}

.aside-header-title {
  margin-left: 12px;
  overflow: hidden;
  font-size: 16px;
  font-style: normal;
  font-weight: 600;
  line-height: 24px;
  color: var(--antd-color-text);
  text-overflow: clip;
  white-space: nowrap;
}

.aside-scroll {
  height: calc(100% - 155px);
  background-color: #fff;
}

.aside-menu {
  padding: 0 var(--aside-padding);
  color: var(--antd-color-text);
}

.aside-menu-category {
  width: 56px;
  height: 24px;
  margin-top: 24px;
  margin-bottom: 8px;
  font-size: 12px;
  font-weight: 500;
  line-height: 24px;
  color: var(--antd-color-text-secondary);
  text-align: center;
  background-color: #f5f7fa;
  border-radius: 4px;

  &:first-child {
    margin-top: 0;
  }
}

.aside-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 65px;
  padding: var(--aside-padding);
  margin-top: 2px;
  background-color: var(--antd-color-bg-base);
  border-bottom-right-radius: var(--aside-border-radius);
  border-bottom-left-radius: var(--aside-border-radius);

  i {
    font-size: 16px;
    cursor: pointer;

    + i {
      margin-left: 12px;
    }
  }
}

.aside-footer-avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  cursor: pointer;
  background: var(--antd-color-primary);
  border-radius: 50%;

  .aside-footer-text {
    font-size: 14px;
    font-weight: 500;
    line-height: 22px;
    color: #fff;
  }
}

.aside-container.ant-layout-sider-collapsed {
  .aside-menu-category {
    position: relative;
    left: -8px;
  }

  .aside-scroll {
    height: calc(100% - 304px);
  }

  :deep(.aside-menu) {
    .ant-menu-submenu,
    .ant-menu-submenu-title {
      border-radius: 4px;
    }

    .ant-menu-item,
    .ant-menu-submenu-title {
      text-overflow: initial;
    }

    .ant-menu-submenu-title {
      &::before {
        position: absolute;
        right: 6px;
        bottom: 6px;
        display: block;
        width: 4px;
        height: 4px;
        content: '';
        border: 1px solid var(--antd-color-text);
        border-top-width: 0;
        border-left-width: 0;
      }

      &:hover::before {
        border-color: var(--antd-color-primary);
      }
    }

    .ant-menu-submenu-selected {
      .ant-menu-submenu-title {
        &::before {
          border-color: var(--antd-color-primary);
        }
      }
    }
  }
}

.aside-collapsed-icons {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-top: 25px;
  background-color: #fff;

  i {
    width: 40px;
    height: 40px;
    font-size: 16px;
    line-height: 40px;
    text-align: center;
    cursor: pointer;
    transition: color 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);

    + i {
      margin-bottom: 2px;
    }

    &:hover {
      color: var(--antd-color-primary);
    }
  }
}
</style>
