<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import { useInfiniteScroll } from '@vueuse/core';

defineOptions({ name: 'NotificationBell' });

interface NotificationItem {
  id: string;
  title: string;
  timeLabel: string;
  unread: boolean;
}

const PAGE_SIZE = 10;

// 通知 mock：扩到 60 条以演示分页 / 搜索；等真接口落地后整体迁移
function buildMockNotifications(): NotificationItem[] {
  const titles = [
    '你被指派为执行「迭代 24.06」负责人',
    '任务「SSO 改造」状态变更：开发中 → 待验收',
    '需求「多币种支持」评审通过',
    '工单 #1042 已分派给你',
    '需求「订单导出」被退回，请补充材料',
    '@ 你的评论已被回复',
    '项目「客户中心 2.0」周报已生成',
    '工单 #1098 客户回复待处理',
    '执行「迭代 24.05」已结束',
    '需求「批量审批」分配给你'
  ];
  const times = ['10min 前', '30min 前', '1h 前', '2h 前', '4h 前', '昨日', '前天', '3 天前', '1 周前', '2 周前'];
  return Array.from({ length: 60 }, (_, i) => ({
    id: `m${i + 1}`,
    title: `${titles[i % titles.length]}（#${i + 1}）`,
    timeLabel: times[Math.floor(i / 6) % times.length],
    unread: i < 14
  }));
}

const notifications = ref<NotificationItem[]>(buildMockNotifications());

const unreadAll = computed(() => notifications.value.filter(n => n.unread));
const readAll = computed(() => notifications.value.filter(n => !n.unread));
const unreadCount = computed(() => unreadAll.value.length);
const badgeLabel = computed(() => (unreadCount.value > 99 ? '99+' : String(unreadCount.value)));

const drawerOpen = ref(false);
const activeTab = ref<'unread' | 'read'>('unread');
const searchKeyword = ref('');

function matchesKeyword(item: NotificationItem) {
  const kw = searchKeyword.value.trim();
  if (!kw) return true;
  return item.title.toLowerCase().includes(kw.toLowerCase());
}

const filteredUnread = computed(() => unreadAll.value.filter(matchesKeyword));
const filteredRead = computed(() => readAll.value.filter(matchesKeyword));

const unreadPageSize = ref(PAGE_SIZE);
const readPageSize = ref(PAGE_SIZE);

const visibleUnread = computed(() => filteredUnread.value.slice(0, unreadPageSize.value));
const visibleRead = computed(() => filteredRead.value.slice(0, readPageSize.value));

const hasMoreUnread = computed(() => unreadPageSize.value < filteredUnread.value.length);
const hasMoreRead = computed(() => readPageSize.value < filteredRead.value.length);

watch(searchKeyword, () => {
  unreadPageSize.value = PAGE_SIZE;
  readPageSize.value = PAGE_SIZE;
});

// 已读列表数量会因"标已读"动态增长 / 未读会缩小；切换 tab 不重置已展示页数，体感更自然

type ScrollbarRefValue = { wrapRef?: HTMLElement } | null;
const unreadScrollbar = ref<ScrollbarRefValue>(null);
const readScrollbar = ref<ScrollbarRefValue>(null);

useInfiniteScroll(
  () => unreadScrollbar.value?.wrapRef,
  () => {
    if (hasMoreUnread.value) unreadPageSize.value += PAGE_SIZE;
  },
  { distance: 48 }
);

useInfiniteScroll(
  () => readScrollbar.value?.wrapRef,
  () => {
    if (hasMoreRead.value) readPageSize.value += PAGE_SIZE;
  },
  { distance: 48 }
);

function openDrawer() {
  drawerOpen.value = true;
}

function closeDrawer() {
  drawerOpen.value = false;
}

function markRead(item: NotificationItem) {
  if (!item.unread) return;
  item.unread = false;
  // eslint-disable-next-line no-console
  console.warn('[notification] mark-read', item.id);
}

function markAllRead() {
  notifications.value.forEach(item => {
    item.unread = false;
  });
  // eslint-disable-next-line no-console
  console.warn('[notification] mark-all-read');
}

function openItem(item: NotificationItem) {
  markRead(item);
  // eslint-disable-next-line no-console
  console.warn('[notification] open', item.id);
}

function onDrawerClosed() {
  searchKeyword.value = '';
}
</script>

<template>
  <button
    class="notification-bell__trigger"
    type="button"
    :aria-label="unreadCount > 0 ? `通知，${unreadCount} 条未读` : '通知'"
    @click="openDrawer"
  >
    <SvgIcon icon="mdi:bell-outline" class="notification-bell__icon" />
    <span v-if="unreadCount > 0" class="notification-bell__badge">{{ badgeLabel }}</span>
  </button>

  <ElDrawer v-model="drawerOpen" size="480px" :with-header="false" @closed="onDrawerClosed">
    <div class="notification-bell__panel">
      <header class="notification-bell__header">
        <span class="notification-bell__title">
          通知
          <span v-if="unreadCount > 0" class="notification-bell__title-count">未读 {{ unreadCount }}</span>
        </span>
        <span class="notification-bell__header-actions">
          <ElButton v-if="unreadCount > 0" link size="small" @click="markAllRead">全部已读</ElButton>
          <button class="notification-bell__close" type="button" aria-label="关闭" @click="closeDrawer">
            <SvgIcon icon="mdi:close" />
          </button>
        </span>
      </header>

      <div class="notification-bell__search">
        <ElInput v-model="searchKeyword" placeholder="搜索通知" clearable>
          <template #prefix>
            <SvgIcon icon="mdi:magnify" />
          </template>
        </ElInput>
      </div>

      <ElTabs v-model="activeTab" class="notification-bell__tabs">
        <ElTabPane name="unread">
          <template #label>
            <span class="notification-bell__tab-label">
              未读
              <span class="notification-bell__tab-count">{{ filteredUnread.length }}</span>
            </span>
          </template>
          <ElScrollbar ref="unreadScrollbar" class="notification-bell__scroll">
            <ul v-if="visibleUnread.length > 0" class="notification-bell__list">
              <li
                v-for="row in visibleUnread"
                :key="row.id"
                class="notification-bell__row is-unread"
                @click="openItem(row)"
              >
                <span class="notification-bell__row-dot" />
                <div class="notification-bell__row-body">
                  <div class="notification-bell__row-title">{{ row.title }}</div>
                  <div class="notification-bell__row-time">{{ row.timeLabel }}</div>
                </div>
              </li>
            </ul>
            <div v-else class="notification-bell__empty">
              {{ searchKeyword ? '没有匹配的通知' : '暂无未读通知' }}
            </div>
            <div v-if="visibleUnread.length > 0" class="notification-bell__footer-hint">
              {{ hasMoreUnread ? '滚动加载更多…' : '— 已经到底了 —' }}
            </div>
          </ElScrollbar>
        </ElTabPane>

        <ElTabPane name="read">
          <template #label>
            <span class="notification-bell__tab-label">
              已读
              <span class="notification-bell__tab-count">{{ filteredRead.length }}</span>
            </span>
          </template>
          <ElScrollbar ref="readScrollbar" class="notification-bell__scroll">
            <ul v-if="visibleRead.length > 0" class="notification-bell__list">
              <li v-for="row in visibleRead" :key="row.id" class="notification-bell__row" @click="openItem(row)">
                <span class="notification-bell__row-dot" />
                <div class="notification-bell__row-body">
                  <div class="notification-bell__row-title">{{ row.title }}</div>
                  <div class="notification-bell__row-time">{{ row.timeLabel }}</div>
                </div>
              </li>
            </ul>
            <div v-else class="notification-bell__empty">
              {{ searchKeyword ? '没有匹配的通知' : '暂无已读通知' }}
            </div>
            <div v-if="visibleRead.length > 0" class="notification-bell__footer-hint">
              {{ hasMoreRead ? '滚动加载更多…' : '— 已经到底了 —' }}
            </div>
          </ElScrollbar>
        </ElTabPane>
      </ElTabs>
    </div>
  </ElDrawer>
</template>

<style scoped>
.notification-bell__trigger {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  padding: 0;
  margin: 0 4px;
  border: none;
  border-radius: 8px;
  background-color: transparent;
  color: var(--el-text-color-regular);
  cursor: pointer;
  transition:
    background-color 160ms ease,
    color 160ms ease;
}

.notification-bell__trigger:hover {
  background-color: var(--el-fill-color-light);
  color: var(--el-color-primary);
}

.notification-bell__trigger:focus-visible {
  outline: 2px solid var(--el-color-primary);
  outline-offset: 2px;
}

.notification-bell__icon {
  font-size: 20px;
}

.notification-bell__badge {
  position: absolute;
  top: 4px;
  right: 4px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background-color: var(--el-color-danger);
  color: #fff;
  font-size: 10px;
  font-weight: 600;
  line-height: 16px;
  text-align: center;
}

.notification-bell__panel {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.notification-bell__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--el-border-color-lighter);
}

.notification-bell__title {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--el-text-color-primary);
  font-size: 16px;
  font-weight: 600;
}

.notification-bell__title-count {
  padding: 1px 8px;
  border-radius: 999px;
  background-color: var(--el-color-danger);
  color: #fff;
  font-size: 11px;
  font-weight: 600;
}

.notification-bell__header-actions {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.notification-bell__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: none;
  border-radius: 6px;
  background-color: transparent;
  color: var(--el-text-color-secondary);
  cursor: pointer;
  font-size: 18px;
  transition:
    background-color 120ms ease,
    color 120ms ease;
}

.notification-bell__close:hover {
  background-color: var(--el-fill-color-light);
  color: var(--el-text-color-primary);
}

.notification-bell__search {
  padding: 12px 0 4px;
}

.notification-bell__tabs {
  display: flex;
  flex: 1;
  flex-direction: column;
  min-height: 0;
}

.notification-bell__tabs :deep(.el-tabs__content) {
  flex: 1;
  min-height: 0;
  overflow: hidden;
}

.notification-bell__tabs :deep(.el-tab-pane) {
  height: 100%;
}

.notification-bell__tab-label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.notification-bell__tab-count {
  padding: 0 7px;
  border-radius: 999px;
  background-color: var(--el-fill-color);
  color: var(--el-text-color-secondary);
  font-size: 10px;
  font-weight: 600;
  line-height: 16px;
}

.notification-bell__tabs :deep(.el-tabs__item.is-active) .notification-bell__tab-count {
  background-color: var(--el-color-primary-light-9);
  color: var(--el-color-primary);
}

.notification-bell__scroll {
  height: 100%;
}

.notification-bell__list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.notification-bell__row {
  position: relative;
  display: grid;
  grid-template-columns: 14px minmax(0, 1fr);
  align-items: flex-start;
  gap: 10px;
  padding: 12px 4px;
  cursor: pointer;
  border-radius: 8px;
  transition: background-color 120ms ease;
}

.notification-bell__row + .notification-bell__row {
  border-top: 1px dashed var(--el-border-color-lighter);
}

.notification-bell__row:hover {
  background-color: var(--el-fill-color-light);
}

.notification-bell__row-dot {
  width: 8px;
  height: 8px;
  margin-top: 6px;
  border-radius: 50%;
  background-color: transparent;
  justify-self: center;
}

.notification-bell__row.is-unread .notification-bell__row-dot {
  background-color: var(--el-color-primary);
}

.notification-bell__row-body {
  min-width: 0;
}

.notification-bell__row-title {
  color: var(--el-text-color-regular);
  font-size: 14px;
  line-height: 1.5;
}

.notification-bell__row.is-unread .notification-bell__row-title {
  color: var(--el-text-color-primary);
  font-weight: 500;
}

.notification-bell__row-time {
  margin-top: 4px;
  color: var(--el-text-color-secondary);
  font-size: 12px;
}

.notification-bell__empty {
  padding: 48px 16px;
  text-align: center;
  color: var(--el-text-color-secondary);
  font-size: 13px;
}

.notification-bell__footer-hint {
  padding: 12px 0 4px;
  text-align: center;
  color: var(--el-text-color-secondary);
  font-size: 12px;
  user-select: none;
}
</style>
