<script setup lang="ts">
import { computed, onMounted, reactive, ref } from 'vue'
import {
  Bell,
  Check,
  EditPen,
  Filter,
  Message,
  Promotion,
  Refresh,
  Search,
  User,
} from '@element-plus/icons-vue'
import { Ckeditor } from '@ckeditor/ckeditor5-vue'
import {
  AutoLink,
  BlockQuote,
  Bold,
  ClassicEditor,
  Essentials,
  Heading,
  Image,
  ImageCaption,
  ImageInsert,
  ImageInsertViaUrl,
  ImageResize,
  ImageStyle,
  ImageToolbar,
  Italic,
  Link,
  LinkImage,
  List,
  Paragraph,
  Table,
  TableCaption,
  TableCellProperties,
  TableColumnResize,
  TableProperties,
  TableToolbar,
  type EditorConfig,
} from 'ckeditor5'
import 'ckeditor5/ckeditor5.css'
import { useRouter } from 'vue-router'
import http, { apiErrorMessage } from '../api/http'
import RichMessageContent from '../components/RichMessageContent.vue'
import { useAuthStore } from '../stores/auth'

const router = useRouter()
const auth = useAuthStore()
const senderRoles = ['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Counselor', 'Teacher']
const canSend = computed(() =>
  auth.user?.roles.some(role => senderRoles.includes(role)) ?? false)

const activeTab = ref<'inbox' | 'compose' | 'sent'>('inbox')
const notifications = ref<any[]>([])
const sentMessages = ref<any[]>([])
const unreadCount = ref(0)
const total = ref(0)
const sentTotal = ref(0)
const loading = ref(false)
const sending = ref(false)
const composerLoading = ref(false)
const recipientLoading = ref(false)
const page = ref(1)
const sentPage = ref(1)
const recipientPage = ref(1)
const pageSize = 20
const recipientPageSize = 30
const unreadOnly = ref(false)
const category = ref<string>()
const keyword = ref('')
const composer = ref<any>(null)
const recipientMode = ref<'Scope' | 'Filtered' | 'Selected'>('Scope')
const recipientItems = ref<any[]>([])
const recipientTotal = ref(0)
const selectedRecipients = ref<any[]>([])
const selectedNotification = ref<any>(null)

const messageForm = reactive({
  teachingTaskId: undefined as string | undefined,
  title: '',
  content: '',
})

const recipientFilters = reactive({
  collegeId: undefined as string | undefined,
  role: undefined as string | undefined,
  administrativeClassId: undefined as string | undefined,
  teachingTaskId: undefined as string | undefined,
  keyword: '',
})

const editorConfig: EditorConfig = {
  licenseKey: import.meta.env.VITE_CKEDITOR_LICENSE_KEY || 'GPL',
  plugins: [
    Essentials,
    Paragraph,
    Heading,
    Bold,
    Italic,
    Link,
    AutoLink,
    List,
    BlockQuote,
    Image,
    ImageCaption,
    ImageInsert,
    ImageInsertViaUrl,
    ImageResize,
    ImageStyle,
    ImageToolbar,
    LinkImage,
    Table,
    TableCaption,
    TableCellProperties,
    TableColumnResize,
    TableProperties,
    TableToolbar,
  ],
  toolbar: {
    items: [
      'heading',
      '|',
      'bold',
      'italic',
      'link',
      '|',
      'insertTable',
      'insertImage',
      '|',
      'bulletedList',
      'numberedList',
      'blockQuote',
      '|',
      'undo',
      'redo',
    ],
    shouldNotGroupWhenFull: false,
  },
  heading: {
    options: [
      { model: 'paragraph', title: '正文', class: 'ck-heading_paragraph' },
      { model: 'heading2', view: 'h2', title: '标题', class: 'ck-heading_heading2' },
      { model: 'heading3', view: 'h3', title: '小标题', class: 'ck-heading_heading3' },
    ],
  },
  link: {
    addTargetToExternalLinks: true,
    defaultProtocol: 'https://',
  },
  image: {
    insert: {
      integrations: ['insertImageViaUrl'],
    },
    toolbar: [
      'toggleImageCaption',
      'imageTextAlternative',
      '|',
      'imageStyle:inline',
      'imageStyle:wrapText',
      'imageStyle:breakText',
      '|',
      'resizeImage',
      'linkImage',
    ],
  },
  table: {
    contentToolbar: [
      'tableColumn',
      'tableRow',
      'mergeTableCells',
      'toggleTableCaption',
      'tableProperties',
      'tableCellProperties',
    ],
  },
}

const categories: Record<string, { label: string; className: string }> = {
  General: { label: '一般通知', className: 'general' },
  Approval: { label: '审核待办', className: 'approval' },
  Schedule: { label: '课程变动', className: 'schedule' },
  Grade: { label: '成绩通知', className: 'grade' },
  Attendance: { label: '考勤消息', className: 'attendance' },
  CourseSelection: { label: '选课消息', className: 'selection' },
  Warning: { label: '学业预警', className: 'warning' },
}

const selectedTask = computed(() =>
  composer.value?.teachingTasks?.find(
    (task: any) => task.id === messageForm.teachingTaskId))
const isAdministratorComposer = computed(() => composer.value?.canFilterRecipients === true)
const recipientCount = computed(() => {
  if (composer.value?.audienceType === 'TeachingTask') {
    return selectedTask.value?.recipientCount ?? 0
  }
  if (recipientMode.value === 'Filtered') return recipientTotal.value
  if (recipientMode.value === 'Selected') return selectedRecipients.value.length
  return composer.value?.recipientCount ?? 0
})
const audienceName = computed(() => {
  if (composer.value?.audienceType === 'TeachingTask') {
    return selectedTask.value
      ? `${selectedTask.value.taskNumber} · ${selectedTask.value.courseName}`
      : '请选择教学班'
  }
  if (recipientMode.value === 'Filtered') return '当前筛选结果'
  if (recipientMode.value === 'Selected') {
    return selectedRecipients.value.length
      ? `指定的 ${selectedRecipients.value.length} 名收件人`
      : '尚未选择收件人'
  }
  return composer.value?.audienceName ?? '正在读取发信范围'
})
const visibleClasses = computed(() =>
  (composer.value?.administrativeClasses ?? []).filter(
    (item: any) => !recipientFilters.collegeId ||
      item.collegeId === recipientFilters.collegeId))
const visibleTasks = computed(() =>
  (composer.value?.teachingTasks ?? []).filter(
    (item: any) => !recipientFilters.collegeId ||
      item.collegeId === recipientFilters.collegeId))
const selectedRecipientIds = computed(() =>
  new Set(selectedRecipients.value.map(item => item.id)))
const editorTextLength = computed(() => {
  const holder = document.createElement('div')
  holder.innerHTML = messageForm.content
  return (holder.textContent ?? '').replace(/\u00a0/g, ' ').trim().length
})

function categoryLabel(value: string) {
  return categories[value]?.label ?? '系统消息'
}

function categoryClass(value: string) {
  return categories[value]?.className ?? 'general'
}

async function load(reset = false) {
  if (reset) page.value = 1
  loading.value = true
  try {
    const { data } = await http.get('/notifications', {
      params: {
        page: page.value,
        pageSize,
        unreadOnly: unreadOnly.value || undefined,
        category: category.value || undefined,
        keyword: keyword.value.trim() || undefined,
      },
    })
    notifications.value = data.items
    total.value = data.total
    unreadCount.value = data.unreadCount
  } catch (error) {
    ElMessage.error(apiErrorMessage(error))
  } finally {
    loading.value = false
  }
}

async function loadSent(reset = false) {
  if (reset) sentPage.value = 1
  loading.value = true
  try {
    const { data } = await http.get('/notifications/sent', {
      params: { page: sentPage.value, pageSize },
    })
    sentMessages.value = data.items
    sentTotal.value = data.total
  } catch (error) {
    ElMessage.error(apiErrorMessage(error))
  } finally {
    loading.value = false
  }
}

async function loadComposer() {
  if (!canSend.value || composer.value) return
  composerLoading.value = true
  try {
    composer.value = (await http.get('/notifications/composer')).data
    if (composer.value?.canFilterRecipients) {
      await loadRecipients(true)
    }
  } catch (error) {
    ElMessage.error(apiErrorMessage(error))
  } finally {
    composerLoading.value = false
  }
}

function recipientParams() {
  return {
    collegeId: recipientFilters.collegeId || undefined,
    role: recipientFilters.role || undefined,
    administrativeClassId: recipientFilters.administrativeClassId || undefined,
    teachingTaskId: recipientFilters.teachingTaskId || undefined,
    keyword: recipientFilters.keyword.trim() || undefined,
    page: recipientPage.value,
    pageSize: recipientPageSize,
  }
}

async function loadRecipients(reset = false) {
  if (!isAdministratorComposer.value) return
  if (reset) recipientPage.value = 1
  recipientLoading.value = true
  try {
    const { data } = await http.get('/notifications/recipients', {
      params: recipientParams(),
    })
    recipientItems.value = data.items
    recipientTotal.value = data.total
  } catch (error) {
    ElMessage.error(apiErrorMessage(error))
  } finally {
    recipientLoading.value = false
  }
}

async function changeRecipientMode(value: 'Scope' | 'Filtered' | 'Selected') {
  recipientMode.value = value
  if (value !== 'Scope') await loadRecipients(true)
}

function onCollegeChange() {
  recipientFilters.administrativeClassId = undefined
  recipientFilters.teachingTaskId = undefined
  loadRecipients(true)
}

function isRecipientSelected(recipient: any) {
  return selectedRecipientIds.value.has(recipient.id)
}

function toggleRecipient(recipient: any) {
  if (isRecipientSelected(recipient)) {
    selectedRecipients.value = selectedRecipients.value.filter(
      item => item.id !== recipient.id)
    return
  }
  const maximum = composer.value?.maximumSelectedRecipients ?? 500
  if (selectedRecipients.value.length >= maximum) {
    ElMessage.warning(`单次最多指定 ${maximum} 名收件人`)
    return
  }
  selectedRecipients.value = [...selectedRecipients.value, recipient]
}

function removeSelectedRecipient(recipientId: string) {
  selectedRecipients.value = selectedRecipients.value.filter(
    item => item.id !== recipientId)
}

async function changeTab(tab: 'inbox' | 'compose' | 'sent') {
  activeTab.value = tab
  if (tab === 'compose') await loadComposer()
  if (tab === 'sent') await loadSent(true)
}

async function markRead(notification: any) {
  if (notification.isRead) return
  try {
    await http.post(`/notifications/${notification.id}/read`)
    notification.isRead = true
    unreadCount.value = Math.max(0, unreadCount.value - 1)
  } catch {
    // Keep navigation available even when the read receipt cannot be saved.
  }
}

async function openNotification(notification: any) {
  await markRead(notification)
  if (notification.linkUrl) {
    await router.push(notification.linkUrl)
    return
  }
  selectedNotification.value = notification
}

async function markAllRead() {
  try {
    await http.post('/notifications/read-all')
    notifications.value.forEach(notification => {
      notification.isRead = true
    })
    unreadCount.value = 0
    ElMessage.success('全部消息已标为已读')
  } catch (error) {
    ElMessage.error(apiErrorMessage(error))
  }
}

async function sendMessage() {
  if (!messageForm.title.trim()) {
    ElMessage.warning('请填写消息标题')
    return
  }
  if (!editorTextLength.value) {
    ElMessage.warning('请填写消息正文')
    return
  }
  if (composer.value?.audienceType === 'TeachingTask' &&
      !messageForm.teachingTaskId) {
    ElMessage.warning('请选择接收消息的教学班')
    return
  }
  if (!recipientCount.value) {
    ElMessage.warning('当前范围内没有可接收消息的账号')
    return
  }

  try {
    await ElMessageBox.confirm(
      `消息将发送给“${audienceName.value}”的 ${recipientCount.value} 个账号，发送后不可撤回。`,
      '确认发送消息',
      {
        type: 'warning',
        confirmButtonText: '确认发送',
        cancelButtonText: '继续编辑',
      },
    )
    sending.value = true
    const { data } = await http.post('/notifications/send', {
      title: messageForm.title,
      content: messageForm.content,
      teachingTaskId: messageForm.teachingTaskId || null,
      recipientMode: isAdministratorComposer.value
        ? recipientMode.value
        : 'Scope',
      recipientFilter: recipientMode.value === 'Filtered'
        ? {
            collegeId: recipientFilters.collegeId || null,
            role: recipientFilters.role || null,
            administrativeClassId:
              recipientFilters.administrativeClassId || null,
            teachingTaskId: recipientFilters.teachingTaskId || null,
            keyword: recipientFilters.keyword.trim() || null,
          }
        : null,
      recipientUserIds: recipientMode.value === 'Selected'
        ? selectedRecipients.value.map(item => item.id)
        : [],
    })
    ElMessage.success(`已发送给 ${data.recipientCount} 个账号`)
    messageForm.title = ''
    messageForm.content = ''
    selectedRecipients.value = []
    await changeTab('sent')
  } catch (error: any) {
    if (error !== 'cancel' && error !== 'close') {
      ElMessage.error(apiErrorMessage(error))
    }
  } finally {
    sending.value = false
  }
}

onMounted(() => load())
</script>

<template>
  <div class="page-stack message-center">
    <section class="page-intro message-intro">
      <div>
        <span class="section-kicker">MESSAGE CENTER</span>
        <h2>消息中心</h2>
        <p>系统通知、审核待办、课程变动与成绩消息统一汇总，重要信息不再散落在业务页面。</p>
      </div>
      <div class="intro-actions">
        <div class="unread-summary" :class="{ quiet: !unreadCount }">
          <span>未读消息</span>
          <b>{{ unreadCount }}</b>
        </div>
        <el-button
          v-if="canSend"
          type="primary"
          :icon="EditPen"
          @click="changeTab('compose')"
        >
          发消息
        </el-button>
        <el-button :icon="Refresh" @click="activeTab === 'sent' ? loadSent() : load()">
          刷新
        </el-button>
      </div>
    </section>

    <section class="center-switcher" aria-label="消息中心功能">
      <button
        type="button"
        :class="{ active: activeTab === 'inbox' }"
        @click="changeTab('inbox')"
      >
        <el-icon><Bell /></el-icon>
        收件箱
        <span v-if="unreadCount">{{ unreadCount }}</span>
      </button>
      <button
        v-if="canSend"
        type="button"
        :class="{ active: activeTab === 'compose' }"
        @click="changeTab('compose')"
      >
        <el-icon><Promotion /></el-icon>
        发消息
      </button>
      <button
        v-if="canSend"
        type="button"
        :class="{ active: activeTab === 'sent' }"
        @click="changeTab('sent')"
      >
        <el-icon><Message /></el-icon>
        已发送
      </button>
    </section>

    <template v-if="activeTab === 'inbox'">
      <section class="message-toolbar">
        <el-select
          v-model="category"
          clearable
          placeholder="全部类型"
          @change="load(true)"
        >
          <el-option
            v-for="(meta, value) in categories"
            :key="value"
            :label="meta.label"
            :value="value"
          />
        </el-select>
        <el-input
          v-model="keyword"
          clearable
          :prefix-icon="Search"
          placeholder="搜索标题或正文"
          @keyup.enter="load(true)"
          @clear="load(true)"
        />
        <el-checkbox v-model="unreadOnly" @change="load(true)">仅看未读</el-checkbox>
        <el-button v-if="unreadCount" :icon="Check" text @click="markAllRead">
          全部已读
        </el-button>
      </section>

      <section v-loading="loading" class="message-list">
        <article
          v-for="notification in notifications"
          :key="notification.id"
          class="message-card"
          role="button"
          tabindex="0"
          :class="[
            categoryClass(notification.category),
            { unread: !notification.isRead, linked: notification.linkUrl },
          ]"
          @click="openNotification(notification)"
          @keyup.enter="openNotification(notification)"
        >
          <div class="category-rail" />
          <div class="message-main">
            <header>
              <div class="message-labels">
                <span class="category-label">{{ categoryLabel(notification.category) }}</span>
                <span v-if="notification.isManual" class="manual-label">人工发送</span>
                <span v-if="!notification.isRead" class="unread-label">未读</span>
              </div>
              <time>{{ new Date(notification.createdAt).toLocaleString('zh-CN') }}</time>
            </header>
            <h3>{{ notification.title }}</h3>
            <div class="message-preview">
              <RichMessageContent
                :content="notification.content"
                :rich="notification.isManual"
              />
            </div>
            <footer>
              <span>来自 {{ notification.senderName }}</span>
              <span v-if="notification.audienceName">
                接收范围：{{ notification.audienceName }}
              </span>
              <span v-if="notification.linkUrl" class="open-link">查看详情 →</span>
            </footer>
          </div>
        </article>
        <el-empty
          v-if="!loading && !notifications.length"
          :description="unreadOnly ? '当前筛选范围内没有未读消息' : '当前筛选范围内没有消息'"
        />
      </section>

      <div v-if="total > pageSize" class="message-pager">
        <el-pagination
          v-model:current-page="page"
          :page-size="pageSize"
          :total="total"
          layout="prev, pager, next, total"
          @current-change="load()"
        />
      </div>
    </template>

    <section
      v-else-if="activeTab === 'compose'"
      v-loading="composerLoading"
      class="compose-shell"
    >
      <header class="compose-overview">
        <div>
          <span class="panel-kicker">AUTHORIZED DELIVERY</span>
          <h3>编写并发送消息</h3>
          <p>收件范围始终受当前账号的数据权限约束，发送后不可撤回。</p>
        </div>
        <div class="delivery-summary">
          <span>{{ audienceName }}</span>
          <b>{{ recipientCount }}</b>
          <small>个接收账号</small>
        </div>
      </header>

      <div class="compose-workspace">
        <section class="recipient-panel">
          <div class="workspace-heading">
            <div>
              <el-icon><User /></el-icon>
              <span>01</span>
            </div>
            <h3>选择收件人</h3>
          </div>

          <template v-if="isAdministratorComposer">
            <el-segmented
              :model-value="recipientMode"
              class="recipient-mode"
              :options="[
                { label: '权限范围群发', value: 'Scope' },
                { label: '按条件群发', value: 'Filtered' },
                { label: '指定收件人', value: 'Selected' },
              ]"
              @change="changeRecipientMode"
            />

            <div v-if="recipientMode !== 'Scope'" class="recipient-filter">
              <div class="filter-heading">
                <el-icon><Filter /></el-icon>
                <span>筛选候选账号</span>
                <small>共 {{ recipientTotal }} 个匹配账号</small>
              </div>
              <div class="filter-grid">
                <el-select
                  v-if="composer?.colleges?.length > 1"
                  v-model="recipientFilters.collegeId"
                  clearable
                  filterable
                  placeholder="全部学院"
                  @change="onCollegeChange"
                >
                  <el-option
                    v-for="college in composer.colleges"
                    :key="college.id"
                    :value="college.id"
                    :label="college.name"
                  />
                </el-select>
                <el-select
                  v-model="recipientFilters.role"
                  clearable
                  placeholder="全部身份"
                  @change="loadRecipients(true)"
                >
                  <el-option
                    v-for="role in composer.roles"
                    :key="role.value"
                    :value="role.value"
                    :label="role.label"
                  />
                </el-select>
                <el-select
                  v-model="recipientFilters.administrativeClassId"
                  clearable
                  filterable
                  placeholder="全部行政班"
                  @change="loadRecipients(true)"
                >
                  <el-option
                    v-for="item in visibleClasses"
                    :key="item.id"
                    :value="item.id"
                    :label="`${item.grade}级 · ${item.name}`"
                  />
                </el-select>
                <el-select
                  v-model="recipientFilters.teachingTaskId"
                  clearable
                  filterable
                  placeholder="全部教学班"
                  @change="loadRecipients(true)"
                >
                  <el-option
                    v-for="task in visibleTasks"
                    :key="task.id"
                    :value="task.id"
                    :label="`${task.taskNumber} · ${task.courseName}`"
                  />
                </el-select>
                <el-input
                  v-model="recipientFilters.keyword"
                  clearable
                  :prefix-icon="Search"
                  placeholder="姓名、学号、工号或账号"
                  @keyup.enter="loadRecipients(true)"
                  @clear="loadRecipients(true)"
                />
                <el-button :icon="Search" @click="loadRecipients(true)">
                  查询
                </el-button>
              </div>
            </div>

            <div v-if="recipientMode === 'Scope'" class="scope-notice">
              <strong>{{ composer.audienceName }}</strong>
              <span>
                将覆盖当前权限范围内全部已启用账号；如无需群发，请切换到条件筛选或指定收件人。
              </span>
            </div>

            <div
              v-else
              v-loading="recipientLoading"
              class="recipient-results"
            >
              <div class="recipient-list">
                <button
                  v-for="recipient in recipientItems"
                  :key="recipient.id"
                  type="button"
                  class="recipient-row"
                  :class="{ selected: isRecipientSelected(recipient) }"
                  :disabled="recipientMode === 'Filtered'"
                  @click="toggleRecipient(recipient)"
                >
                  <span class="recipient-avatar">
                    {{ recipient.displayName.slice(0, 1) }}
                  </span>
                  <span class="recipient-identity">
                    <b>{{ recipient.displayName }}</b>
                    <small>
                      {{ recipient.number || recipient.userName }}
                      <template v-if="recipient.className">
                        · {{ recipient.className }}
                      </template>
                    </small>
                  </span>
                  <span class="recipient-meta">
                    {{ recipient.collegeName || '校级账号' }}
                  </span>
                  <span v-if="recipientMode === 'Selected'" class="recipient-check">
                    {{ isRecipientSelected(recipient) ? '已选择' : '选择' }}
                  </span>
                </button>
                <el-empty
                  v-if="!recipientLoading && !recipientItems.length"
                  description="没有符合条件的可用账号"
                />
              </div>
              <el-pagination
                v-if="recipientTotal > recipientPageSize"
                v-model:current-page="recipientPage"
                small
                background
                :page-size="recipientPageSize"
                :total="recipientTotal"
                layout="prev, pager, next"
                @current-change="loadRecipients()"
              />
            </div>

            <div
              v-if="recipientMode === 'Selected' && selectedRecipients.length"
              class="selected-recipient-strip"
            >
              <header>
                <b>已选 {{ selectedRecipients.length }} 人</b>
                <button type="button" @click="selectedRecipients = []">清空</button>
              </header>
              <div>
                <span
                  v-for="recipient in selectedRecipients.slice(0, 12)"
                  :key="recipient.id"
                >
                  {{ recipient.displayName }}
                  <button
                    type="button"
                    :aria-label="`移除 ${recipient.displayName}`"
                    @click="removeSelectedRecipient(recipient.id)"
                  >
                    ×
                  </button>
                </span>
                <small v-if="selectedRecipients.length > 12">
                  另有 {{ selectedRecipients.length - 12 }} 人
                </small>
              </div>
            </div>
          </template>

          <el-form-item
            v-else
            label="接收教学班"
            required
          >
            <el-select
              v-model="messageForm.teachingTaskId"
              filterable
              placeholder="选择本人任教的教学班"
            >
              <el-option
                v-for="task in composer?.teachingTasks ?? []"
                :key="task.id"
                :value="task.id"
                :label="`${task.taskNumber} · ${task.courseName}（${task.recipientCount} 人）`"
              >
                <div class="task-option">
                  <span>{{ task.taskNumber }} · {{ task.courseName }}</span>
                  <small>{{ task.termName }} · {{ task.recipientCount }} 人</small>
                </div>
              </el-option>
            </el-select>
            <small class="field-help">
              任课教师只能向本人所带教学班的有效学生账号发送消息。
            </small>
          </el-form-item>
        </section>

        <el-form class="compose-form" label-position="top">
          <div class="workspace-heading">
            <div>
              <el-icon><EditPen /></el-icon>
              <span>02</span>
            </div>
            <h3>编辑消息内容</h3>
          </div>

          <div class="compose-heading">
          <div>
              <span>普通通知</span>
              <small>成绩、选课等业务通知仍由对应流程自动发送</small>
          </div>
          </div>

          <el-form-item label="消息标题" required>
            <el-input
              v-model="messageForm.title"
              maxlength="200"
              show-word-limit
              placeholder="用一句话说明需要关注的事项"
            />
          </el-form-item>

          <el-form-item label="消息正文" required class="editor-form-item">
            <Ckeditor
              v-model="messageForm.content"
              :editor="ClassicEditor"
              :config="editorConfig"
            />
            <div class="editor-footnote">
              <span>支持表格、超链接和外链图片；图片请填写 HTTPS 地址，不上传本地文件。</span>
              <b :class="{ warning: messageForm.content.length > 18000 }">
                {{ editorTextLength }} 字 · 源码 {{ messageForm.content.length }}/20000
              </b>
            </div>
          </el-form-item>

          <div class="compose-actions">
            <span>
              将发送给 <b>{{ recipientCount }}</b> 个账号
              <small>{{ audienceName }}</small>
            </span>
            <el-button
              type="primary"
              :icon="Promotion"
              :loading="sending"
              @click="sendMessage"
            >
              确认发送
            </el-button>
          </div>
        </el-form>
      </div>
    </section>

    <template v-else>
      <section v-loading="loading" class="sent-list">
        <article v-for="item in sentMessages" :key="item.id" class="sent-card">
          <header>
            <span :class="['category-pill', categoryClass(item.category)]">
              {{ categoryLabel(item.category) }}
            </span>
            <time>{{ new Date(item.createdAt).toLocaleString('zh-CN') }}</time>
          </header>
          <h3>{{ item.title }}</h3>
          <RichMessageContent :content="item.content" rich />
          <footer>
            <span>{{ item.audienceName }}</span>
            <b>{{ item.recipientCount }} 个接收账号</b>
          </footer>
        </article>
        <el-empty v-if="!loading && !sentMessages.length" description="还没有发送记录" />
      </section>

      <div v-if="sentTotal > pageSize" class="message-pager">
        <el-pagination
          v-model:current-page="sentPage"
          :page-size="pageSize"
          :total="sentTotal"
          layout="prev, pager, next, total"
          @current-change="loadSent()"
        />
      </div>
    </template>

    <el-dialog
      v-model="selectedNotification"
      width="min(680px, 92vw)"
      class="message-detail-dialog"
      destroy-on-close
    >
      <template #header>
        <div v-if="selectedNotification" class="message-detail-heading">
          <span :class="['category-pill', categoryClass(selectedNotification.category)]">
            {{ categoryLabel(selectedNotification.category) }}
          </span>
          <h3>{{ selectedNotification.title }}</h3>
          <small>
            {{ selectedNotification.senderName }} ·
            {{ new Date(selectedNotification.createdAt).toLocaleString('zh-CN') }}
          </small>
        </div>
      </template>
      <RichMessageContent
        v-if="selectedNotification"
        :content="selectedNotification.content"
        :rich="selectedNotification.isManual"
      />
    </el-dialog>
  </div>
</template>

<style scoped>
.message-center {
  --message-blue: var(--indigo);
  --message-ink: var(--ink);
  --message-line: var(--line);
}

.message-intro {
  align-items: center;
}

.intro-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}

.unread-summary {
  display: grid;
  grid-template-columns: auto auto;
  align-items: baseline;
  gap: 8px;
  min-width: 108px;
  padding: 8px 12px;
  color: #1d4ed8;
  background: #f3f5fa;
  border: 1px solid var(--message-line);
  border-left: 3px solid var(--teal);
  border-radius: var(--el-border-radius-base);
}

.unread-summary span {
  font-size: 12px;
}

.unread-summary b {
  font-size: 24px;
  line-height: 1;
}

.unread-summary.quiet {
  color: #64748b;
  background: #f8fafc;
  border-color: #e2e8f0;
  border-left-color: #aab2c0;
}

.center-switcher {
  display: flex;
  gap: 4px;
  padding: 4px;
  width: fit-content;
  background: #f1f3f6;
  border: 1px solid var(--message-line);
  border-radius: var(--el-border-radius-base);
}

.center-switcher button {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 36px;
  padding: 0 15px;
  color: #64748b;
  background: transparent;
  border: 0;
  border-radius: 5px;
  cursor: pointer;
}

.center-switcher button.active {
  color: var(--message-ink);
  background: #fff;
  box-shadow: 0 1px 3px rgb(15 23 42 / 10%);
  font-weight: 600;
}

.center-switcher button > span {
  min-width: 18px;
  padding: 1px 5px;
  color: #fff;
  background: var(--message-blue);
  border-radius: 10px;
  font-size: 11px;
  line-height: 16px;
}

.message-toolbar {
  display: grid;
  grid-template-columns: 160px minmax(220px, 420px) auto auto;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  background: #fff;
  border: 1px solid var(--message-line);
  border-radius: 0;
}

.message-list,
.sent-list {
  display: grid;
  gap: 10px;
}

.message-card {
  position: relative;
  display: flex;
  min-height: 138px;
  overflow: hidden;
  background: #fff;
  border: 1px solid var(--message-line);
  border-radius: 0;
  cursor: pointer;
  transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
}

.message-card:hover {
  border-color: #c7d2e2;
  box-shadow: 0 8px 22px rgb(15 23 42 / 7%);
  transform: translateY(-1px);
}

.message-card:focus-visible {
  outline: 2px solid var(--indigo);
  outline-offset: 2px;
}

.message-card.unread {
  background: linear-gradient(90deg, #f7faff 0, #fff 42%);
  border-color: #bfdbfe;
}

.category-rail {
  width: 5px;
  flex: 0 0 5px;
  background: #64748b;
}

.message-card.approval .category-rail { background: #d97706; }
.message-card.schedule .category-rail { background: #7c3aed; }
.message-card.grade .category-rail { background: #059669; }
.message-card.attendance .category-rail { background: #db2777; }
.message-card.selection .category-rail { background: #0891b2; }
.message-card.warning .category-rail { background: #dc2626; }

.message-main {
  display: flex;
  flex: 1;
  flex-direction: column;
  min-width: 0;
  padding: 17px 20px 15px;
  color: #526078;
}

.message-main header,
.sent-card header,
.sent-card footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.message-labels {
  display: flex;
  align-items: center;
  gap: 7px;
}

.category-label,
.manual-label,
.unread-label,
.category-pill {
  padding: 2px 7px;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 600;
}

.category-label {
  color: #334155;
  background: #eef2f7;
}

.manual-label {
  color: #075985;
  background: #e0f2fe;
}

.unread-label {
  color: #1d4ed8;
  background: #dbeafe;
}

.message-main time,
.sent-card time {
  color: #94a3b8;
  font-size: 12px;
}

.message-main h3,
.sent-card h3,
.compose-workspace h3 {
  margin: 11px 0 6px;
  color: var(--message-ink);
  font-size: 16px;
}

.message-main p,
.sent-card p {
  margin: 0;
  color: #526078;
  font-size: 13px;
  line-height: 1.7;
  white-space: pre-wrap;
}

.message-preview {
  max-height: 7.2em;
  overflow: hidden;
  position: relative;
}

.message-preview::after {
  content: "";
  position: absolute;
  right: 0;
  bottom: 0;
  width: 35%;
  height: 1.7em;
  pointer-events: none;
  background: linear-gradient(90deg, transparent, #fff 58%);
}

.message-card.unread .message-preview::after {
  background: linear-gradient(90deg, transparent, #fff 58%);
}

.message-main footer {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 18px;
  margin-top: auto;
  padding-top: 12px;
  color: #94a3b8;
  font-size: 12px;
}

.open-link {
  margin-left: auto;
  color: var(--message-blue);
  font-weight: 600;
}

.message-pager {
  display: flex;
  justify-content: center;
  padding: 8px 0;
}

.compose-shell {
  background: #fff;
  border: 1px solid var(--message-line);
}

.compose-overview {
  min-height: 98px;
  padding: 20px 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  border-bottom: 1px solid var(--message-line);
  border-left: 3px solid var(--teal);
}

.compose-overview h3 {
  margin: 5px 0 4px;
  color: var(--ink);
  font-size: 19px;
}

.compose-overview p {
  margin: 0;
  color: var(--muted);
  font-size: 11px;
}

.panel-kicker {
  color: var(--teal);
  font: 700 9px/1.2 Consolas, monospace;
  letter-spacing: .14em;
}

.delivery-summary {
  min-width: 260px;
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: baseline;
  gap: 7px;
  padding: 13px 16px;
  background: #f5f7fa;
  border: 1px solid var(--message-line);
}

.delivery-summary span {
  overflow: hidden;
  color: var(--muted);
  font-size: 11px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.delivery-summary b {
  color: var(--indigo);
  font: 700 27px/1 Consolas, monospace;
}

.delivery-summary small {
  color: var(--muted);
  font-size: 10px;
}

.compose-workspace {
  display: grid;
  grid-template-columns: minmax(420px, .95fr) minmax(480px, 1.05fr);
  min-height: 650px;
}

.recipient-panel,
.compose-form {
  min-width: 0;
  padding: 24px;
}

.recipient-panel {
  border-right: 1px solid var(--message-line);
  background: #fbfcfd;
}

.workspace-heading {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 20px;
}

.workspace-heading > div {
  width: 36px;
  height: 36px;
  position: relative;
  display: grid;
  place-items: center;
  color: var(--indigo);
  background: #edf0f8;
}

.workspace-heading > div span {
  position: absolute;
  right: -6px;
  bottom: -4px;
  padding: 1px 3px;
  color: #fff;
  background: var(--teal);
  font: 700 8px/1 Consolas, monospace;
}

.workspace-heading h3 {
  margin: 0;
  color: var(--ink);
  font-size: 15px;
}

.recipient-mode {
  width: 100%;
  margin-bottom: 16px;
  --el-segmented-item-selected-bg-color: var(--indigo);
  --el-segmented-item-selected-color: #fff;
}

.recipient-filter {
  border: 1px solid var(--message-line);
  background: #fff;
}

.filter-heading {
  min-height: 43px;
  padding: 0 13px;
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--ink);
  border-bottom: 1px solid var(--message-line);
  font-size: 11px;
  font-weight: 650;
}

.filter-heading .el-icon {
  color: var(--teal);
}

.filter-heading small {
  margin-left: auto;
  color: var(--muted);
  font-weight: 400;
}

.filter-grid {
  padding: 12px;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 9px;
}

.filter-grid .el-button {
  margin-left: 0;
}

.scope-notice {
  min-height: 108px;
  padding: 18px;
  display: grid;
  align-content: center;
  gap: 7px;
  border: 1px solid var(--message-line);
  border-left: 3px solid var(--teal);
  background: #f4f8f8;
}

.scope-notice strong {
  color: var(--ink);
  font-size: 14px;
}

.scope-notice span {
  color: var(--muted);
  font-size: 11px;
  line-height: 1.7;
}

.recipient-results {
  min-height: 280px;
  margin-top: 12px;
  display: grid;
  align-content: space-between;
  gap: 12px;
}

.recipient-list {
  display: grid;
  gap: 6px;
}

.recipient-row {
  width: 100%;
  min-width: 0;
  min-height: 52px;
  padding: 7px 9px;
  display: grid;
  grid-template-columns: 34px minmax(120px, 1fr) minmax(80px, auto) auto;
  align-items: center;
  gap: 9px;
  text-align: left;
  color: inherit;
  background: #fff;
  border: 1px solid var(--message-line);
  cursor: pointer;
}

.recipient-row:disabled {
  cursor: default;
}

.recipient-row.selected {
  border-color: var(--teal);
  box-shadow: inset 3px 0 var(--teal);
  background: #f4faf9;
}

.recipient-avatar {
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  color: var(--indigo);
  background: #edf0f8;
  font-size: 11px;
  font-weight: 700;
}

.recipient-identity {
  min-width: 0;
  display: grid;
  gap: 3px;
}

.recipient-identity b {
  overflow: hidden;
  color: var(--ink);
  font-size: 11px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.recipient-identity small,
.recipient-meta {
  overflow: hidden;
  color: var(--muted);
  font-size: 9px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.recipient-check {
  color: var(--indigo);
  font-size: 9px;
  font-weight: 650;
}

.selected-recipient-strip {
  margin-top: 12px;
  padding: 11px 12px;
  border: 1px solid var(--message-line);
  background: #fff;
}

.selected-recipient-strip header {
  margin-bottom: 8px;
  display: flex;
  justify-content: space-between;
  color: var(--ink);
  font-size: 10px;
}

.selected-recipient-strip button {
  padding: 0;
  color: var(--indigo);
  background: transparent;
  border: 0;
  cursor: pointer;
}

.selected-recipient-strip > div {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
}

.selected-recipient-strip > div > span {
  padding: 3px 6px;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: #3f4b60;
  background: #eef1f5;
  font-size: 9px;
}

.selected-recipient-strip small {
  align-self: center;
  color: var(--muted);
  font-size: 9px;
}

.compose-heading {
  display: flex;
  align-items: center;
  margin: -4px 0 18px;
  padding: 10px 12px;
  color: #3f4b60;
  background: #f5f7fa;
  border-left: 3px solid var(--teal);
}

.compose-heading > div {
  display: flex;
  align-items: baseline;
  gap: 10px;
}

.compose-heading span {
  color: var(--ink);
  font-size: 11px;
  font-weight: 650;
}

.compose-heading small {
  color: var(--muted);
  font-size: 9px;
}

.field-help {
  display: block;
  width: 100%;
  margin-top: 6px;
  color: #94a3b8;
  line-height: 1.5;
}

.task-option {
  display: flex;
  justify-content: space-between;
  gap: 20px;
}

.task-option small {
  color: #94a3b8;
}

.editor-form-item :deep(.el-form-item__content) {
  display: block;
}

.editor-form-item :deep(.ck-editor) {
  width: 100%;
}

.editor-form-item :deep(.ck.ck-toolbar) {
  border-color: var(--message-line);
  border-radius: 0;
  background: #f7f8fa;
}

.editor-form-item :deep(.ck.ck-editor__main > .ck-editor__editable) {
  min-height: 250px;
  max-height: 430px;
  color: var(--ink);
  border-color: var(--message-line);
  border-radius: 0;
  box-shadow: none;
  font-family: inherit;
  font-size: 13px;
}

.editor-form-item :deep(.ck.ck-editor__main > .ck-editor__editable.ck-focused) {
  border-color: var(--indigo);
  box-shadow: inset 0 0 0 1px var(--indigo);
}

.editor-footnote {
  width: 100%;
  margin-top: 7px;
  display: flex;
  justify-content: space-between;
  gap: 12px;
  color: var(--muted);
  font-size: 9px;
}

.editor-footnote b {
  color: #6a7384;
  font-weight: 500;
}

.editor-footnote b.warning {
  color: #b45309;
}

.compose-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: 17px;
  border-top: 1px solid #e9edf3;
}

.compose-actions span {
  color: #64748b;
  font-size: 11px;
}

.compose-actions span b {
  color: var(--indigo);
  font: 700 16px/1 Consolas, monospace;
}

.compose-actions span small {
  display: block;
  margin-top: 3px;
  color: var(--muted);
  font-size: 9px;
}

.sent-card {
  padding: 18px 20px;
  background: #fff;
  border: 1px solid var(--message-line);
  border-radius: 0;
}

.sent-card footer {
  margin-top: 15px;
  padding-top: 12px;
  color: #64748b;
  border-top: 1px solid #eef2f6;
  font-size: 12px;
}

.sent-card footer b {
  color: #334155;
}

.category-pill {
  color: #334155;
  background: #eef2f7;
}

.category-pill.grade {
  color: #047857;
  background: #d1fae5;
}

.message-detail-heading {
  padding-right: 28px;
}

.message-detail-heading h3 {
  margin: 10px 0 5px;
  color: var(--ink);
  font-size: 20px;
}

.message-detail-heading small {
  color: var(--muted);
  font-size: 10px;
}

:deep(.message-detail-dialog .el-dialog__body) {
  padding-top: 8px;
  color: #3f4b60;
  line-height: 1.75;
}

@media (max-width: 760px) {
  .message-intro,
  .intro-actions,
  .compose-actions {
    align-items: stretch;
  }

  .intro-actions {
    flex-wrap: wrap;
  }

  .unread-summary {
    flex: 1;
  }

  .center-switcher {
    width: 100%;
  }

  .center-switcher button {
    flex: 1;
    justify-content: center;
    padding: 0 8px;
  }

  .message-toolbar {
    grid-template-columns: 1fr;
  }

  .message-card {
    min-height: 0;
  }

  .message-main header,
  .sent-card header {
    align-items: flex-start;
    flex-direction: column;
  }

  .open-link {
    width: 100%;
    margin-left: 0;
  }

  .compose-overview {
    align-items: stretch;
    flex-direction: column;
  }

  .delivery-summary {
    min-width: 0;
  }

  .compose-workspace {
    grid-template-columns: 1fr;
  }

  .recipient-panel {
    padding: 18px;
    border-right: 0;
    border-bottom: 1px solid var(--message-line);
  }

  .filter-grid {
    grid-template-columns: 1fr;
  }

  .recipient-row {
    grid-template-columns: 34px minmax(0, 1fr) auto;
  }

  .recipient-meta {
    display: none;
  }

  .compose-form {
    padding: 22px 18px 24px;
  }

  .compose-actions {
    flex-direction: column;
    gap: 12px;
  }

  .compose-actions .el-button {
    width: 100%;
  }

  .editor-footnote {
    align-items: flex-start;
    flex-direction: column;
  }
}
</style>
