<template>
  <div>
    <div v-if="isServicesPortal" class="portal-bar">
      <span class="portal-bar-title">即时通讯管理</span>
      <el-select :model-value="appKey" placeholder="选择应用" style="width:220px" @change="switchApp">
        <el-option v-for="a in portalApps" :key="a.appKey" :label="a.name" :value="a.appKey" />
      </el-select>
    </div>
    <el-page-header v-else @back="$router.back()" :content="`即时通讯管理 — ${appKey}`" style="margin-bottom:20px" />
    <el-empty v-if="isServicesPortal && !appKey" description="请选择一个应用" style="margin-top:80px" />

    <div v-if="isServicesPortal && appKey && checkingService" v-loading="true" style="min-height:200px" />

    <template v-if="isServicesPortal && appKey && serviceEnabled === false">
      <el-empty :image-size="80" description="当前应用未开通即时通讯服务" style="margin-top:60px">
        <el-button type="primary" @click="showActivationDialog = true">申请开通</el-button>
      </el-empty>
      <el-dialog v-model="showActivationDialog" title="申请开通即时通讯" width="460px">
        <el-form label-width="80px">
          <el-form-item label="服务">即时通讯</el-form-item>
          <el-form-item label="申请理由">
            <el-input v-model="activationReason" type="textarea" :rows="3" placeholder="请描述您的业务场景" />
          </el-form-item>
        </el-form>
        <template #footer>
          <el-button @click="showActivationDialog = false">取消</el-button>
          <el-button type="primary" :loading="submittingActivation" @click="submitActivation">提交申请</el-button>
        </template>
      </el-dialog>
    </template>

    <template v-if="!isServicesPortal || (appKey && serviceEnabled === true)">
    <el-row :gutter="16" class="stat-grid">
      <el-col :xs="24" :sm="12" :md="6" v-for="item in statCards" :key="item.label">
        <el-card shadow="never">
          <div class="stat-card">
            <span class="stat-value">{{ item.value }}</span>
            <span class="stat-label">{{ item.label }}</span>
          </div>
        </el-card>
      </el-col>
    </el-row>

    <el-card shadow="never">
      <el-tabs v-model="activeTab" @tab-change="handleTabChange">
        <el-tab-pane label="注册用户" name="users">
          <div class="toolbar responsive-toolbar">
            <el-button type="primary" @click="openCreateUserDialog">新增用户</el-button>
            <el-button @click="loadUsers" :loading="loadingUsers">刷新</el-button>
          </div>

          <div class="table-wrap">
          <el-table :data="users" v-loading="loadingUsers" border stripe>
            <el-table-column prop="userId" label="用户ID" width="180" />
            <el-table-column label="头像" width="90">
              <template #default="{ row }">
                <el-avatar :size="32" :src="row.avatar || undefined">
                  {{ userAvatarFallback(row) }}
                </el-avatar>
              </template>
            </el-table-column>
            <el-table-column prop="nickname" label="昵称" width="140" />
            <el-table-column prop="gender" label="性别" width="90">
              <template #default="{ row }">
                {{ genderLabel[row.gender] ?? '未知' }}
              </template>
            </el-table-column>
            <el-table-column prop="status" label="状态" width="100">
              <template #default="{ row }">
                <el-tag :type="row.status === 'ACTIVE' ? 'success' : 'danger'" size="small">
                  {{ row.status === 'ACTIVE' ? '正常' : '封禁' }}
                </el-tag>
              </template>
            </el-table-column>
            <el-table-column label="权限" width="100">
              <template #default="{ row }">
                <el-tag v-if="row.admin" type="warning" size="small">管理员</el-tag>
                <el-tag v-else type="info" size="small">普通</el-tag>
              </template>
            </el-table-column>
            <el-table-column label="在线" width="120">
              <template #default="{ row }">
                <el-tag v-if="userOnlineState[row.userId]?.online" type="success" size="small">在线</el-tag>
                <el-tag v-else-if="userOnlineState[row.userId]?.lastSeenAt" type="info" size="small">
                  {{ formatLastSeen(userOnlineState[row.userId].lastSeenAt) }}
                </el-tag>
                <el-tag v-else type="info" size="small">离线</el-tag>
              </template>
            </el-table-column>
            <el-table-column prop="createdAt" label="注册时间" width="180">
              <template #default="{ row }">
                {{ formatTime(row.createdAt) }}
              </template>
            </el-table-column>
            <el-table-column label="操作" width="180" fixed="right">
              <template #default="{ row }">
                <el-button link type="primary" size="small" @click="openEditUserDialog(row)">
                  编辑
                </el-button>
                <el-button link type="primary" size="small" @click="openUserSigDialog(row)">
                  UserSig
                </el-button>
                <el-button
                  link
                  :type="row.status === 'ACTIVE' ? 'danger' : 'success'"
                  size="small"
                  @click="toggleUserStatus(row)"
                >
                  {{ row.status === 'ACTIVE' ? '封禁' : '解封' }}
                </el-button>
              </template>
            </el-table-column>
          </el-table>
          </div>

          <el-pagination
            v-if="userTotal > userPageSize"
            style="margin-top:16px"
            layout="prev, pager, next"
            :total="userTotal"
            :page-size="userPageSize"
            :current-page="userPage + 1"
            @current-change="handleUserPageChange"
          />
        </el-tab-pane>

        <el-tab-pane label="群组列表" name="groups">
          <div class="toolbar responsive-toolbar">
            <el-button type="primary" @click="openCreateGroupDialog">创建群组</el-button>
            <el-button @click="loadGroups" :loading="loadingGroups">刷新</el-button>
          </div>

          <div class="table-wrap">
          <el-table :data="groups" v-loading="loadingGroups" border stripe>
            <el-table-column prop="id" label="群组ID" width="240" />
            <el-table-column prop="name" label="群名称" min-width="160" />
            <el-table-column prop="groupType" label="类型" width="110">
              <template #default="{ row }">
                {{ row.groupType || 'WORK' }}
              </template>
            </el-table-column>
            <el-table-column label="成员数" width="100">
              <template #default="{ row }">{{ parseIdList(row.memberIds).length }}</template>
            </el-table-column>
            <el-table-column label="管理员" width="100">
              <template #default="{ row }">{{ parseIdList(row.adminIds).length }}</template>
            </el-table-column>
            <el-table-column prop="creatorId" label="群主" width="160" />
            <el-table-column prop="announcement" label="公告" min-width="200" show-overflow-tooltip />
            <el-table-column prop="createdAt" label="创建时间" width="180">
              <template #default="{ row }">{{ formatTime(row.createdAt) }}</template>
            </el-table-column>
            <el-table-column label="操作" width="180" fixed="right">
              <template #default="{ row }">
                <el-button link type="primary" size="small" @click="openEditGroupDialog(row)">
                  编辑
                </el-button>
                <el-button link type="primary" size="small" @click="openManageGroupDialog(row)">
                  成员
                </el-button>
                <el-button link type="danger" size="small" @click="dismissGroup(row)">
                  解散
                </el-button>
              </template>
            </el-table-column>
          </el-table>
          </div>
        </el-tab-pane>

        <el-tab-pane label="内容治理" name="governance">
          <div class="section-block">
            <div class="section-head">
              <div class="section-title">全局禁言</div>
              <div class="toolbar-group">
                <el-button @click="loadGlobalMute" :loading="loadingGlobalMute">刷新</el-button>
                <el-switch
                  v-model="globalMuteEnabled"
                  :loading="savingGlobalMute"
                  inline-prompt
                  active-text="开启"
                  inactive-text="关闭"
                  @change="toggleGlobalMute"
                />
              </div>
            </div>
            <el-text type="info">开启后，当前应用的普通消息发送会被拦截。</el-text>
          </div>

          <el-divider />

          <div class="toolbar toolbar-space-between responsive-toolbar">
            <div class="toolbar-group">
              <el-button type="primary" @click="openKeywordFilterDialog">新增过滤规则</el-button>
              <el-button @click="loadKeywordFilters" :loading="loadingKeywordFilters">刷新</el-button>
            </div>
          </div>

          <div class="table-wrap">
          <el-table :data="keywordFilters" v-loading="loadingKeywordFilters" border stripe>
            <el-table-column prop="pattern" label="命中词" min-width="180" />
            <el-table-column prop="action" label="动作" width="110">
              <template #default="{ row }">
                <el-tag :type="keywordActionTagType(row.action)" size="small">
                  {{ keywordActionLabel(row.action) }}
                </el-tag>
              </template>
            </el-table-column>
            <el-table-column prop="replacement" label="替换内容" min-width="180">
              <template #default="{ row }">{{ row.replacement || '-' }}</template>
            </el-table-column>
            <el-table-column prop="enabled" label="启用" width="90">
              <template #default="{ row }">
                <el-tag :type="row.enabled ? 'success' : 'info'" size="small">
                  {{ row.enabled ? '启用' : '停用' }}
                </el-tag>
              </template>
            </el-table-column>
            <el-table-column prop="createdAt" label="创建时间" width="180">
              <template #default="{ row }">{{ formatTime(row.createdAt) }}</template>
            </el-table-column>
            <el-table-column label="操作" width="170" fixed="right">
              <template #default="{ row }">
                <el-button link type="primary" size="small" @click="openKeywordFilterDialog(row)">编辑</el-button>
                <el-button link type="danger" size="small" @click="deleteKeywordFilter(row)">删除</el-button>
              </template>
            </el-table-column>
          </el-table>
          </div>
        </el-tab-pane>

        <el-tab-pane label="消息历史" name="messages">
          <el-form :inline="true" :model="historyForm" class="toolbar" label-width="0">
            <el-form-item>
              <el-input v-model="historyForm.keyword" placeholder="关键词" clearable style="width:200px" />
            </el-form-item>
            <el-form-item>
              <el-select v-model="historyForm.chatType" placeholder="会话类型" clearable style="width:120px">
                <el-option label="单聊" value="SINGLE" />
                <el-option label="群聊" value="GROUP" />
              </el-select>
            </el-form-item>
            <el-form-item>
              <el-select v-model="historyForm.msgType" placeholder="消息类型" clearable style="width:140px">
                <el-option v-for="item in msgTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
              </el-select>
            </el-form-item>
            <el-form-item>
              <el-date-picker
                v-model="historyForm.timeRange"
                type="datetimerange"
                range-separator="至"
                start-placeholder="开始时间"
                end-placeholder="结束时间"
                value-format="YYYY-MM-DD HH:mm:ss"
                style="width:340px"
              />
            </el-form-item>
            <el-form-item>
              <el-button type="primary" :loading="loadingMessages" @click="searchMessages">
                搜索
              </el-button>
            </el-form-item>
            <el-form-item>
              <el-button @click="resetMessageSearch">重置</el-button>
            </el-form-item>
          </el-form>

          <div class="table-wrap">
          <el-table :data="messages" v-loading="loadingMessages" border stripe>
            <el-table-column prop="createdAt" label="时间" width="180">
              <template #default="{ row }">{{ formatTime(row.createdAt) }}</template>
            </el-table-column>
            <el-table-column prop="chatType" label="会话" width="90" />
            <el-table-column prop="fromUserId" label="发送者" width="160" />
            <el-table-column prop="toId" label="会话ID" width="180" />
            <el-table-column prop="msgType" label="消息类型" width="110" />
            <el-table-column prop="status" label="状态" width="100" />
            <el-table-column prop="content" label="内容" min-width="260">
              <template #default="{ row }">
                <span class="content-preview">{{ formatContent(row.content) }}</span>
              </template>
            </el-table-column>
            <el-table-column label="操作" width="110" fixed="right">
              <template #default="{ row }">
                <el-button link type="danger" size="small" @click="revokeMessage(row)">
                  撤回
                </el-button>
              </template>
            </el-table-column>
          </el-table>
          </div>

          <el-pagination
            v-if="historyTotal > historyPageSize"
            style="margin-top:16px"
            layout="prev, pager, next"
            :total="historyTotal"
            :page-size="historyPageSize"
            :current-page="historyPage + 1"
            @current-change="handleHistoryPageChange"
          />
        </el-tab-pane>

        <el-tab-pane label="操作日志" name="logs">
          <div class="toolbar responsive-toolbar">
            <el-button @click="loadOperationLogs" :loading="loadingLogs">刷新</el-button>
          </div>

          <div class="table-wrap">
          <el-table :data="operationLogs" v-loading="loadingLogs" border stripe>
            <el-table-column prop="createdAt" label="时间" width="180">
              <template #default="{ row }">{{ formatTime(row.createdAt) }}</template>
            </el-table-column>
            <el-table-column prop="operatorId" label="操作者" width="160" />
            <el-table-column prop="action" label="动作" width="180">
              <template #default="{ row }">
                <el-tag size="small" effect="plain">{{ logActionLabel(row.action) }}</el-tag>
              </template>
            </el-table-column>
            <el-table-column prop="resourceType" label="资源类型" width="120">
              <template #default="{ row }">{{ logResourceLabel(row.resourceType) }}</template>
            </el-table-column>
            <el-table-column prop="resourceId" label="资源ID" width="220" show-overflow-tooltip />
            <el-table-column prop="detail" label="详情" min-width="320">
              <template #default="{ row }">
                <span class="log-detail">{{ formatLogDetail(row.detail) }}</span>
              </template>
            </el-table-column>
          </el-table>
          </div>

          <el-pagination
            v-if="operationLogTotal > operationLogPageSize"
            style="margin-top:16px"
            layout="prev, pager, next"
            :total="operationLogTotal"
            :page-size="operationLogPageSize"
            :current-page="operationLogPage + 1"
            @current-change="handleOperationLogPageChange"
          />
        </el-tab-pane>

        <el-tab-pane label="回调配置" name="webhooks">
          <div class="toolbar toolbar-space-between">
            <div class="toolbar-group">
              <el-button type="primary" @click="openCreateWebhookDialog">新增回调</el-button>
              <el-button @click="loadWebhooks" :loading="loadingWebhooks">刷新</el-button>
            </div>
            <el-button text type="primary" @click="showWebhookGuideDialog = true">查看接入说明</el-button>
          </div>

          <div class="table-wrap">
            <el-table :data="webhooks" v-loading="loadingWebhooks" border stripe>
              <el-table-column prop="url" label="回调地址" min-width="260" show-overflow-tooltip />
              <el-table-column prop="secret" label="密钥" width="120">
                <template #default="{ row }">
                  {{ row.secret ? '******' : '-' }}
                </template>
              </el-table-column>
              <el-table-column prop="enabled" label="启用" width="100">
                <template #default="{ row }">
                  <el-tag :type="row.enabled ? 'success' : 'info'" size="small">
                    {{ row.enabled ? '启用' : '停用' }}
                  </el-tag>
                </template>
              </el-table-column>
              <el-table-column prop="createdAt" label="创建时间" width="180">
                <template #default="{ row }">{{ formatTime(row.createdAt) }}</template>
              </el-table-column>
              <el-table-column label="操作" width="180" fixed="right">
                <template #default="{ row }">
                  <el-button link type="primary" size="small" @click="openEditWebhookDialog(row)">编辑</el-button>
                  <el-button link type="danger" size="small" @click="deleteWebhook(row)">删除</el-button>
                </template>
              </el-table-column>
            </el-table>
          </div>
        </el-tab-pane>
      </el-tabs>
    </el-card>

    <el-dialog v-model="showRegisterUser" :title="editingUserId ? '编辑用户' : '注册用户'" width="520px" @closed="resetUserForm">
      <el-form :model="registerForm" label-width="84px">
        <el-form-item label="用户ID">
          <el-input v-model="registerForm.userId" placeholder="全局唯一标识" :disabled="!!editingUserId" />
        </el-form-item>
        <el-form-item label="昵称">
          <el-input v-model="registerForm.nickname" />
        </el-form-item>
        <el-form-item label="头像">
          <el-input v-model="registerForm.avatar" placeholder="头像 URL" />
        </el-form-item>
        <el-form-item label="性别">
          <el-select v-model="registerForm.gender" style="width:100%">
            <el-option label="未知" value="UNKNOWN" />
            <el-option label="男" value="MALE" />
            <el-option label="女" value="FEMALE" />
          </el-select>
        </el-form-item>
        <el-form-item label="状态">
          <el-select v-model="registerForm.status" style="width:100%">
            <el-option label="正常" value="ACTIVE" />
            <el-option label="封禁" value="BANNED" />
          </el-select>
        </el-form-item>
        <el-form-item label="管理员">
          <el-switch v-model="registerForm.admin" inline-prompt active-text="是" inactive-text="否" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="showRegisterUser = false">取消</el-button>
        <el-button type="primary" :loading="submittingRegister" @click="submitRegisterUser">
          {{ editingUserId ? '保存' : '注册' }}
        </el-button>
      </template>
    </el-dialog>

    <el-dialog v-model="showCreateGroup" title="创建群组" width="600px" @closed="resetCreateGroupForm">
      <el-form :model="createGroupForm" label-width="90px">
        <el-form-item label="群名称">
          <el-input v-model="createGroupForm.name" />
        </el-form-item>
        <el-form-item label="创建者ID">
          <el-input v-model="createGroupForm.creatorId" />
        </el-form-item>
        <el-form-item label="群类型">
          <el-select v-model="createGroupForm.groupType" style="width:100%">
            <el-option label="工作群" value="WORK" />
            <el-option label="公开群" value="PUBLIC" />
          </el-select>
        </el-form-item>
        <el-form-item label="公告">
          <el-input v-model="createGroupForm.announcement" type="textarea" :rows="3" />
        </el-form-item>
        <el-form-item label="搜索成员">
          <el-input
            v-model="memberSearchKeyword"
            :prefix-icon="Search"
            placeholder="输入昵称或用户ID搜索"
            clearable
            @input="onMemberSearchInput"
          />
          <div v-if="memberSearchResults.length > 0" class="search-results">
            <div
              v-for="user in memberSearchResults"
              :key="user.userId"
              class="search-result-item"
              @click="addMember(user)"
            >
              <span class="result-nickname">{{ user.nickname || user.userId }}</span>
              <span class="result-userid">{{ user.userId }}</span>
            </div>
          </div>
          <el-empty
            v-else-if="memberSearchKeyword && !searchingMembers && memberSearchResults.length === 0"
            description="未找到匹配用户"
            :image-size="48"
            style="padding:8px 0"
          />
        </el-form-item>
        <el-form-item label="已选成员">
          <div class="selected-members">
            <el-tag
              v-for="m in selectedMembers"
              :key="m.userId"
              closable
              type="primary"
              style="margin:3px"
              @close="removeMember(m.userId)"
            >
              {{ m.nickname || m.userId }}
            </el-tag>
            <span v-if="selectedMembers.length === 0" class="no-members-hint">暂未选择成员</span>
          </div>
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="showCreateGroup = false">取消</el-button>
        <el-button type="primary" :loading="submittingCreateGroup" @click="submitCreateGroup">创建</el-button>
      </template>
    </el-dialog>

    <el-dialog v-model="showEditGroup" title="编辑群组" width="520px" @closed="resetEditGroupForm">
      <el-form :model="editGroupForm" label-width="90px">
        <el-form-item label="群组ID">
          <el-input :model-value="editingGroup?.id ?? ''" disabled />
        </el-form-item>
        <el-form-item label="群名称">
          <el-input v-model="editGroupForm.name" />
        </el-form-item>
        <el-form-item label="群类型">
          <el-select v-model="editGroupForm.groupType" style="width:100%">
            <el-option label="工作群" value="WORK" />
            <el-option label="公开群" value="PUBLIC" />
          </el-select>
        </el-form-item>
        <el-form-item label="公告">
          <el-input v-model="editGroupForm.announcement" type="textarea" :rows="4" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="showEditGroup = false">取消</el-button>
        <el-button type="primary" :loading="submittingEditGroup" @click="submitEditGroup">保存</el-button>
      </template>
    </el-dialog>

    <el-dialog
      v-model="showGroupManageDialog"
      :title="managedGroup ? `成员管理 — ${managedGroup.name}` : '成员管理'"
      width="1120px"
      @closed="resetGroupManageDialog"
    >
      <el-descriptions v-if="managedGroup" :column="2" border style="margin-bottom: 16px">
        <el-descriptions-item label="群组ID">{{ managedGroup.id }}</el-descriptions-item>
        <el-descriptions-item label="群类型">{{ managedGroup.groupType || 'WORK' }}</el-descriptions-item>
        <el-descriptions-item label="群主">{{ managedGroup.creatorId }}</el-descriptions-item>
        <el-descriptions-item label="管理员">{{ parseIdList(managedGroup.adminIds).length }}</el-descriptions-item>
        <el-descriptions-item label="成员数">{{ parseIdList(managedGroup.memberIds).length }}</el-descriptions-item>
        <el-descriptions-item label="公告">{{ managedGroup.announcement || '-' }}</el-descriptions-item>
      </el-descriptions>

      <div v-if="managedGroup" class="toolbar" style="margin-bottom: 12px">
        <el-button type="primary" size="small" @click="openGroupAttributesDialog">扩展属性</el-button>
        <el-button type="info" size="small" @click="openReadReceiptsDialog">回执查询</el-button>
      </div>

      <el-tabs v-model="groupManageTab">
        <el-tab-pane label="成员" name="members">
          <div class="toolbar toolbar-space-between">
            <div class="toolbar-group">
              <el-input
                v-model="groupMemberSearchKeyword"
                placeholder="搜索昵称或用户ID"
                clearable
                style="width: 240px"
                @input="onGroupMemberSearchInput"
              />
              <el-button @click="loadManagedGroupMembers" :loading="loadingManagedGroupMembers">刷新</el-button>
            </div>
            <el-text type="info">点击搜索结果可直接加入群组</el-text>
          </div>

          <div v-if="groupMemberSearchResults.length > 0" class="search-results" style="margin-bottom: 12px">
            <div
              v-for="user in groupMemberSearchResults"
              :key="user.userId"
              class="search-result-item"
              @click="addManagedGroupMember(user)"
            >
              <span class="result-nickname">{{ user.nickname || user.userId }}</span>
              <span class="result-userid">{{ user.userId }}</span>
            </div>
          </div>

          <div class="table-wrap">
            <el-table :data="managedGroupMembers" v-loading="loadingManagedGroupMembers" border stripe>
              <el-table-column prop="userId" label="用户ID" width="160" />
              <el-table-column prop="nickname" label="昵称" min-width="140" />
              <el-table-column label="角色" width="120">
                <template #default="{ row }">
                  <el-tag :type="groupMemberRoleTagType(row.userId)" size="small">
                    {{ groupMemberRoleLabel(row.userId) }}
                  </el-tag>
                </template>
              </el-table-column>
              <el-table-column prop="status" label="状态" width="100">
                <template #default="{ row }">
                  <el-tag :type="row.status === 'ACTIVE' ? 'success' : 'danger'" size="small">
                    {{ row.status === 'ACTIVE' ? '正常' : '封禁' }}
                  </el-tag>
                </template>
              </el-table-column>
              <el-table-column prop="createdAt" label="加入时间" width="180">
                <template #default="{ row }">{{ formatTime(row.createdAt) }}</template>
              </el-table-column>
              <el-table-column label="操作" width="320" fixed="right">
                <template #default="{ row }">
                  <el-button
                    v-if="row.userId !== managedGroup?.creatorId"
                    link
                    type="success"
                    size="small"
                    @click="transferGroupOwner(row)"
                  >
                    转让群主
                  </el-button>
                  <el-button
                    link
                    type="primary"
                    size="small"
                    :disabled="row.userId === managedGroup?.creatorId"
                    @click="toggleManagedGroupRole(row)"
                  >
                    {{ groupMemberRoleLabel(row.userId) === '管理员' ? '降为成员' : '设为管理员' }}
                  </el-button>
                  <el-button
                    link
                    type="warning"
                    size="small"
                    :disabled="row.userId === managedGroup?.creatorId"
                    @click="muteManagedGroupMember(row)"
                  >
                    禁言
                  </el-button>
                  <el-button
                    link
                    type="danger"
                    size="small"
                    :disabled="row.userId === managedGroup?.creatorId"
                    @click="removeManagedGroupMember(row)"
                  >
                    移除
                  </el-button>
                </template>
              </el-table-column>
            </el-table>
          </div>
        </el-tab-pane>

        <el-tab-pane label="入群申请" name="requests">
          <div class="toolbar toolbar-space-between">
            <div class="toolbar-group">
              <el-button @click="loadManagedGroupJoinRequests" :loading="loadingManagedGroupJoinRequests">刷新</el-button>
            </div>
          </div>

          <div class="table-wrap">
            <el-table :data="managedGroupJoinRequests" v-loading="loadingManagedGroupJoinRequests" border stripe>
              <el-table-column prop="requesterId" label="申请人" width="160" />
              <el-table-column prop="remark" label="备注" min-width="240" show-overflow-tooltip />
              <el-table-column prop="status" label="状态" width="110">
                <template #default="{ row }">
                  <el-tag :type="groupRequestTagType(row.status)" size="small">
                    {{ groupRequestStatusLabel(row.status) }}
                  </el-tag>
                </template>
              </el-table-column>
              <el-table-column prop="createdAt" label="申请时间" width="180">
                <template #default="{ row }">{{ formatTime(row.createdAt) }}</template>
              </el-table-column>
              <el-table-column prop="reviewedAt" label="处理时间" width="180">
                <template #default="{ row }">{{ formatTime(row.reviewedAt) }}</template>
              </el-table-column>
              <el-table-column label="操作" width="170" fixed="right">
                <template #default="{ row }">
                  <el-button
                    link
                    type="primary"
                    size="small"
                    :disabled="row.status !== 'PENDING'"
                    @click="approveGroupJoin(row)"
                  >
                    同意
                  </el-button>
                  <el-button
                    link
                    type="danger"
                    size="small"
                    :disabled="row.status !== 'PENDING'"
                    @click="declineGroupJoin(row)"
                  >
                    拒绝
                  </el-button>
                </template>
              </el-table-column>
            </el-table>
          </div>
        </el-tab-pane>
      </el-tabs>
    </el-dialog>

    <el-dialog v-model="showGroupAttributesDialog" title="群扩展属性" width="560px">
      <el-form label-width="100px">
        <el-form-item label="属性 JSON">
          <el-input v-model="groupAttributesJson" type="textarea" :rows="6" placeholder='{"key": "value"}' />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="showGroupAttributesDialog = false">取消</el-button>
        <el-button type="primary" @click="submitGroupAttributes">保存</el-button>
      </template>
    </el-dialog>

    <el-dialog v-model="showReadReceiptsDialog" title="群消息已读回执" width="640px">
      <div class="toolbar">
        <el-button @click="loadGroupReadReceipts" :loading="loadingReadReceipts">刷新</el-button>
      </div>
      <el-table :data="groupReadReceipts" v-loading="loadingReadReceipts" border stripe>
        <el-table-column prop="messageId" label="消息ID" min-width="200" />
        <el-table-column prop="readCount" label="已读人数" width="100" />
        <el-table-column prop="readUserIds" label="已读用户" min-width="240">
          <template #default="{ row }">
            <span v-if="row.readUserIds && row.readUserIds.length">{{ row.readUserIds.join(', ') }}</span>
            <span v-else class="el-text el-text--info">无</span>
          </template>
        </el-table-column>
      </el-table>
    </el-dialog>

    <el-dialog
      v-model="showWebhookDialog"
      :title="editingWebhookId ? '编辑回调配置' : '新增回调配置'"
      width="520px"
      @closed="resetWebhookForm"
    >
      <el-form :model="webhookForm" label-width="90px">
        <el-form-item label="回调地址">
          <el-input v-model="webhookForm.url" placeholder="https://example.com/webhook" />
        </el-form-item>
        <el-form-item label="回调密钥">
          <el-input v-model="webhookForm.secret" placeholder="留空则不修改" />
        </el-form-item>
        <el-form-item label="启用">
          <el-switch v-model="webhookForm.enabled" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="showWebhookDialog = false">取消</el-button>
        <el-button type="primary" :loading="submittingWebhook" @click="submitWebhookForm">保存</el-button>
      </template>
    </el-dialog>

    <el-dialog v-model="showWebhookGuideDialog" title="IM 回调接入说明" width="760px">
      <el-alert
        title="回调配置用于把 IM 事件推送到你的业务系统。服务端会以 POST 方式推送到你配置的 URL，并带上签名头。"
        type="info"
        :closable="false"
        show-icon
        style="margin-bottom:16px"
      />
      <el-descriptions :column="1" border>
        <el-descriptions-item label="调用方式">POST 到你配置的回调地址，`Content-Type: application/json`。</el-descriptions-item>
        <el-descriptions-item label="请求头">`X-App-Key`、`X-App-Timestamp`、`X-App-Nonce`、`X-App-Signature`。</el-descriptions-item>
        <el-descriptions-item label="请求体">统一 envelope：`callbackId`、`callbackType`、`callbackEvent`、`requestTime`、`payload`、`appKey`。</el-descriptions-item>
        <el-descriptions-item label="签名">`HMAC-SHA256(appSecret, appKey + '\\n' + timestamp + '\\n' + nonce + '\\n' + sha256(body))`。</el-descriptions-item>
        <el-descriptions-item label="失败处理">回调发送失败只记录日志，不会中断消息发送、撤回等主流程。</el-descriptions-item>
        <el-descriptions-item label="幂等建议">接收方建议按 `callbackId` 去重。</el-descriptions-item>
      </el-descriptions>
      <div class="table-wrap" style="margin-top:16px">
        <el-table :data="webhookEvents" border stripe>
          <el-table-column prop="event" label="事件" width="180" />
          <el-table-column prop="payload" label="payload" width="220" />
          <el-table-column prop="description" label="说明" min-width="320" />
        </el-table>
      </div>
      <template #footer>
        <el-button type="primary" @click="showWebhookGuideDialog = false">关闭</el-button>
      </template>
    </el-dialog>

    <el-dialog v-model="showUserSigDialog" title="UserSig 生成与校验" width="760px" @closed="resetUserSigDialog">
      <el-descriptions v-if="selectedUserForSig" :column="2" border style="margin-bottom: 16px">
        <el-descriptions-item label="用户ID">{{ selectedUserForSig.userId }}</el-descriptions-item>
        <el-descriptions-item label="权限">{{ selectedUserForSig.admin ? '管理员' : '普通用户' }}</el-descriptions-item>
        <el-descriptions-item label="状态">{{ selectedUserForSig.status === 'ACTIVE' ? '正常' : '封禁' }}</el-descriptions-item>
        <el-descriptions-item label="应用">{{ appKey }}</el-descriptions-item>
      </el-descriptions>
      <el-form label-width="100px">
        <el-form-item label="过期秒数">
          <el-input-number v-model="userSigExpireSeconds" :min="60" :max="315360000" style="width: 240px" />
        </el-form-item>
        <el-form-item label="UserBuf">
          <el-input v-model="userSigUserBuf" placeholder="可选，用于附加业务信息" />
        </el-form-item>
        <el-form-item label="UserSig">
          <el-input
            v-model="userSigValue"
            type="textarea"
            :rows="6"
            placeholder="点击生成后这里会显示 UserSig"
          />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="showUserSigDialog = false">关闭</el-button>
        <el-button :disabled="!selectedUserForSig" @click="copyUserSig">复制</el-button>
        <el-button :loading="generatingUserSig" type="primary" @click="generateUserSig">生成</el-button>
        <el-button :loading="verifyingUserSig" @click="verifyUserSig">校验</el-button>
      </template>
    </el-dialog>

    <el-dialog
      v-model="showKeywordFilterDialog"
      :title="editingKeywordFilterId ? '编辑过滤规则' : '新增过滤规则'"
      width="560px"
      @closed="resetKeywordFilterForm"
    >
      <el-form :model="keywordFilterForm" label-width="96px">
        <el-form-item label="命中词">
          <el-input v-model="keywordFilterForm.pattern" />
        </el-form-item>
        <el-form-item label="动作">
          <el-select v-model="keywordFilterForm.action" style="width:100%">
            <el-option label="替换" value="REPLACE" />
            <el-option label="拦截" value="BLOCK" />
          </el-select>
        </el-form-item>
        <el-form-item label="替换内容">
          <el-input v-model="keywordFilterForm.replacement" />
        </el-form-item>
        <el-form-item label="启用">
          <el-switch v-model="keywordFilterForm.enabled" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="showKeywordFilterDialog = false">取消</el-button>
        <el-button type="primary" :loading="submittingKeywordFilter" @click="submitKeywordFilter">
          保存
        </el-button>
      </template>
    </el-dialog>
  </template>
  </div>
</template>

<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { appApi, type App } from '@/api/app'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Search } from '@element-plus/icons-vue'
import { formatTime } from '@/utils/date'
import { getLastServiceApp, saveLastServiceApp } from '@/utils/serviceApp'
import {
  imAdminApi,
  type GlobalMute,
  type GroupJoinRequest,
  type ImGroup,
  type ImMessage,
  type ImStats,
  type KeywordFilter,
  type ImUser,
  type OperationLog,
  type WebhookConfig,
} from '@/api/im'

const route = useRoute()
const router = useRouter()
const isServicesPortal = computed(() => route.path.startsWith('/services/'))
const portalApps = ref<App[]>([])
// IM 管理页按 appKey 作用域查询；tenant-service 的应用主键不要传到这里。
const appKey = computed(() => {
  const queryAppKey = route.query.appKey
  if (typeof queryAppKey === 'string' && queryAppKey.trim()) {
    return queryAppKey.trim()
  }
  return String(route.params['appKey'] ?? '')
})

const serviceEnabled = ref<boolean | null>(null)
const checkingService = ref(false)
const showActivationDialog = ref(false)
const activationReason = ref('')
const submittingActivation = ref(false)
let pollingTimer: ReturnType<typeof setInterval> | null = null

function clearPolling() {
  if (pollingTimer !== null) {
    clearInterval(pollingTimer)
    pollingTimer = null
  }
}

async function checkServiceEnabled(key: string) {
  checkingService.value = true
  serviceEnabled.value = null
  try {
    const res = await appApi.getServices(key)
    const enabled = res.data.data.some(s => s.serviceType === 'IM' && s.enabled)
    serviceEnabled.value = enabled
    if (enabled) {
      clearPolling()
      loadStats()
      loadUsers()
    } else if (pollingTimer === null) {
      pollingTimer = setInterval(async () => {
        const currentKey = appKey.value
        if (!currentKey) { clearPolling(); return }
        const r = await appApi.getServices(currentKey).catch(() => null)
        if (r && r.data.data.some(s => s.serviceType === 'IM' && s.enabled)) {
          serviceEnabled.value = true
          clearPolling()
          loadStats()
          loadUsers()
        }
      }, 30000)
    }
  } catch {
    serviceEnabled.value = false
  } finally {
    checkingService.value = false
  }
}

async function submitActivation() {
  if (!activationReason.value.trim()) {
    ElMessage.warning('请填写申请理由')
    return
  }
  submittingActivation.value = true
  try {
    await appApi.requestActivation(appKey.value, 'IM', activationReason.value.trim())
    ElMessage.success('申请已提交，等待运营审核')
    showActivationDialog.value = false
    activationReason.value = ''
  } catch {
    ElMessage.error('提交失败')
  } finally {
    submittingActivation.value = false
  }
}

const genderLabel: Record<string, string> = {
  UNKNOWN: '未知',
  MALE: '男',
  FEMALE: '女',
}

const msgTypeOptions = [
  'TEXT', 'IMAGE', 'VIDEO', 'AUDIO', 'FILE', 'CUSTOM', 'LOCATION', 'NOTIFY',
  'RICH_TEXT', 'CALL_AUDIO', 'CALL_VIDEO', 'FORWARD', 'QUOTE', 'MERGE', 'REVOKED',
].map(value => ({ label: value, value }))

const activeTab = ref('users')
const stats = ref<ImStats | null>(null)

const users = ref<ImUser[]>([])
const loadingUsers = ref(false)
const userPage = ref(0)
const userPageSize = 20
const userTotal = ref(0)
const userOnlineState = ref<Record<string, { online: boolean; lastSeenAt: number }>>({})

const groups = ref<ImGroup[]>([])
const loadingGroups = ref(false)

const messages = ref<ImMessage[]>([])
const loadingMessages = ref(false)
const historyPage = ref(0)
const historyPageSize = 20
const historyTotal = ref(0)

const operationLogs = ref<OperationLog[]>([])
const loadingLogs = ref(false)
const operationLogPage = ref(0)
const operationLogPageSize = 20
const operationLogTotal = ref(0)

const webhooks = ref<WebhookConfig[]>([])
const webhookEvents = [
  { event: 'message.sent', payload: 'ImMessageEntity', description: '消息发送成功后触发。' },
  { event: 'message.revoked', payload: 'ImMessageEntity', description: '消息撤回后触发。' },
  { event: 'message.edited', payload: 'ImMessageEntity', description: '文本消息编辑后触发。' },
  { event: 'message.read', payload: 'MessageReadCallbackPayload', description: '已读回执同步后触发。' },
  { event: 'group.join.request.sent', payload: 'GroupJoinRequestCallbackPayload', description: '入群申请创建后触发。' },
  { event: 'group.join.request.accepted', payload: 'GroupJoinRequestCallbackPayload', description: '入群申请通过后触发。' },
  { event: 'group.join.request.rejected', payload: 'GroupJoinRequestCallbackPayload', description: '入群申请拒绝后触发。' },
]
const loadingWebhooks = ref(false)
const showWebhookGuideDialog = ref(false)
const showWebhookDialog = ref(false)
const submittingWebhook = ref(false)
const editingWebhookId = ref<string | null>(null)
const webhookForm = ref({ url: '', secret: '', enabled: true })

const keywordFilters = ref<KeywordFilter[]>([])
const loadingKeywordFilters = ref(false)
const showKeywordFilterDialog = ref(false)
const submittingKeywordFilter = ref(false)
const editingKeywordFilterId = ref<string | null>(null)
const keywordFilterForm = ref({
  pattern: '',
  replacement: '',
  action: 'REPLACE' as 'REPLACE' | 'BLOCK',
  enabled: true,
})

const globalMute = ref<GlobalMute | null>(null)
const loadingGlobalMute = ref(false)
const savingGlobalMute = ref(false)

const groupJoinRequests = ref<GroupJoinRequest[]>([])
const loadingGroupRequests = ref(false)
const groupRequestGroupId = ref('')
const showSendGroupJoinRequest = ref(false)
const submittingSendGroupJoinRequest = ref(false)
const sendGroupJoinRequestForm = ref({ groupId: '', remark: '' })

const showGroupManageDialog = ref(false)
const managedGroup = ref<ImGroup | null>(null)
const groupManageTab = ref<'members' | 'requests'>('members')
const managedGroupMembers = ref<ImUser[]>([])
const loadingManagedGroupMembers = ref(false)
const managedGroupJoinRequests = ref<GroupJoinRequest[]>([])
const loadingManagedGroupJoinRequests = ref(false)
const groupMemberSearchKeyword = ref('')
const groupMemberSearchResults = ref<ImUser[]>([])
const searchingGroupMembers = ref(false)
let groupMemberSearchTimer: ReturnType<typeof setTimeout> | null = null

const historyForm = ref({
  keyword: '',
  chatType: '',
  msgType: '',
  timeRange: [] as string[] | null,
})

const showRegisterUser = ref(false)
const submittingRegister = ref(false)
const editingUserId = ref<string | null>(null)
const registerForm = ref({
  userId: '',
  nickname: '',
  avatar: '',
  gender: 'UNKNOWN' as 'UNKNOWN' | 'MALE' | 'FEMALE',
  status: 'ACTIVE' as 'ACTIVE' | 'BANNED',
  admin: false,
})

const showUserSigDialog = ref(false)
const selectedUserForSig = ref<ImUser | null>(null)
const userSigExpireSeconds = ref(180 * 24 * 60 * 60)
const userSigUserBuf = ref('')
const userSigValue = ref('')
const generatingUserSig = ref(false)
const verifyingUserSig = ref(false)

const showCreateGroup = ref(false)
const submittingCreateGroup = ref(false)
const createGroupForm = ref({
  name: '',
  creatorId: '',
  groupType: 'WORK' as 'WORK' | 'PUBLIC',
  announcement: '',
})

const showEditGroup = ref(false)
const submittingEditGroup = ref(false)
const editingGroup = ref<ImGroup | null>(null)
const editGroupForm = ref({
  name: '',
  groupType: 'WORK' as 'WORK' | 'PUBLIC',
  announcement: '',
})

const showGroupAttributesDialog = ref(false)
const groupAttributesJson = ref('{}')

const showReadReceiptsDialog = ref(false)
const groupReadReceipts = ref<Array<{ messageId: string; readCount: number; readUserIds: string[] }>>([])
const loadingReadReceipts = ref(false)

const memberSearchKeyword = ref('')
const memberSearchResults = ref<ImUser[]>([])
const searchingMembers = ref(false)
const selectedMembers = ref<ImUser[]>([])
let memberSearchTimer: ReturnType<typeof setTimeout> | null = null

const statCards = computed(() => [
  { label: '注册用户', value: stats.value?.totalUsers ?? '-' },
  { label: '群组数', value: stats.value?.totalGroups ?? '-' },
  { label: '消息总量', value: stats.value?.totalMessages ?? '-' },
  { label: '今日消息', value: stats.value?.todayMessages ?? '-' },
])

const globalMuteEnabled = computed({
  get: () => globalMute.value?.enabled ?? false,
  set: (enabled: boolean) => {
    if (globalMute.value) {
      globalMute.value.enabled = enabled
    }
  },
})


function formatContent(content: string) {
  if (!content) return '-'
  return content.length > 120 ? `${content.slice(0, 120)}...` : content
}

function friendRequestStatusLabel(status: string) {
  return { PENDING: '待处理', ACCEPTED: '已同意', REJECTED: '已拒绝' }[status] ?? status
}

function friendRequestTagType(status: string) {
  return { PENDING: 'warning', ACCEPTED: 'success', REJECTED: 'danger' }[status] ?? 'info'
}

function groupRequestStatusLabel(status: string) {
  return { PENDING: '待处理', ACCEPTED: '已同意', REJECTED: '已拒绝' }[status] ?? status
}

function groupRequestTagType(status: string) {
  return { PENDING: 'warning', ACCEPTED: 'success', REJECTED: 'danger' }[status] ?? 'info'
}

function keywordActionLabel(action: string) {
  return { REPLACE: '替换', BLOCK: '拦截' }[action] ?? action
}

function keywordActionTagType(action: string) {
  return { REPLACE: 'primary', BLOCK: 'danger' }[action] ?? 'info'
}

function parseIdList(value: string) {
  try {
    const parsed = JSON.parse(value)
    return Array.isArray(parsed) ? parsed : []
  } catch {
    return []
  }
}

function toLocalDateTime(value: string) {
  if (!value) return ''
  if (value.includes('T')) return value
  return value.replace(' ', 'T')
}

function onMemberSearchInput() {
  if (memberSearchTimer) clearTimeout(memberSearchTimer)
  const keyword = memberSearchKeyword.value.trim()
  if (!keyword) {
    memberSearchResults.value = []
    return
  }
  memberSearchTimer = setTimeout(async () => {
    searchingMembers.value = true
    try {
      const res = await imAdminApi.searchUsers(appKey.value, keyword)
      const selectedIds = new Set(selectedMembers.value.map(item => item.userId))
      memberSearchResults.value = res.data.data.filter(user => !selectedIds.has(user.userId))
    } catch {
      memberSearchResults.value = []
    } finally {
      searchingMembers.value = false
    }
  }, 300)
}

function addMember(user: ImUser) {
  if (!selectedMembers.value.find(item => item.userId === user.userId)) {
    selectedMembers.value.push(user)
  }
  memberSearchResults.value = memberSearchResults.value.filter(item => item.userId !== user.userId)
  memberSearchKeyword.value = ''
}

function removeMember(userId: string) {
  selectedMembers.value = selectedMembers.value.filter(item => item.userId !== userId)
}

function resetCreateGroupForm() {
  createGroupForm.value = { name: '', creatorId: '', groupType: 'WORK', announcement: '' }
  memberSearchKeyword.value = ''
  memberSearchResults.value = []
  selectedMembers.value = []
}

function openCreateUserDialog() {
  editingUserId.value = null
  registerForm.value = {
    userId: '',
    nickname: '',
    avatar: '',
    gender: 'UNKNOWN',
    status: 'ACTIVE',
    admin: false,
  }
  showRegisterUser.value = true
}

function openEditUserDialog(user: ImUser) {
  editingUserId.value = user.userId
  registerForm.value = {
    userId: user.userId,
    nickname: user.nickname ?? '',
    avatar: user.avatar ?? '',
    gender: user.gender ?? 'UNKNOWN',
    status: user.status ?? 'ACTIVE',
    admin: Boolean(user.admin),
  }
  showRegisterUser.value = true
}

function resetUserForm() {
  editingUserId.value = null
  registerForm.value = {
    userId: '',
    nickname: '',
    avatar: '',
    gender: 'UNKNOWN',
    status: 'ACTIVE',
    admin: false,
  }
}

function openUserSigDialog(user: ImUser) {
  selectedUserForSig.value = user
  userSigExpireSeconds.value = 180 * 24 * 60 * 60
  userSigUserBuf.value = ''
  userSigValue.value = ''
  showUserSigDialog.value = true
}

function resetUserSigDialog() {
  selectedUserForSig.value = null
  userSigExpireSeconds.value = 180 * 24 * 60 * 60
  userSigUserBuf.value = ''
  userSigValue.value = ''
}

async function generateUserSig() {
  if (!selectedUserForSig.value) return
  generatingUserSig.value = true
  try {
    const res = await imAdminApi.generateUserSig(appKey.value, selectedUserForSig.value.userId, {
      expireSeconds: userSigExpireSeconds.value,
      userBuf: userSigUserBuf.value.trim() || undefined,
    })
    userSigValue.value = res.data.data.userSig
    ElMessage.success('UserSig 已生成')
  } catch {
    ElMessage.error('UserSig 生成失败')
  } finally {
    generatingUserSig.value = false
  }
}

async function verifyUserSig() {
  if (!selectedUserForSig.value || !userSigValue.value.trim()) {
    ElMessage.warning('请先生成或粘贴 UserSig')
    return
  }
  verifyingUserSig.value = true
  try {
    const res = await imAdminApi.verifyUserSig(appKey.value, selectedUserForSig.value.userId, userSigValue.value.trim())
    if (res.data.data.valid) {
      ElMessage.success('UserSig 校验通过')
    } else {
      ElMessage.warning('UserSig 校验未通过')
    }
  } catch {
    ElMessage.error('UserSig 校验失败')
  } finally {
    verifyingUserSig.value = false
  }
}

async function copyUserSig() {
  if (!userSigValue.value.trim()) {
    ElMessage.warning('没有可复制的 UserSig')
    return
  }
  await navigator.clipboard.writeText(userSigValue.value.trim())
  ElMessage.success('已复制 UserSig')
}

function openCreateGroupDialog() {
  resetCreateGroupForm()
  showCreateGroup.value = true
}

function openEditGroupDialog(group: ImGroup) {
  editingGroup.value = group
  editGroupForm.value = {
    name: group.name,
    groupType: (group.groupType || 'WORK') as 'WORK' | 'PUBLIC',
    announcement: group.announcement ?? '',
  }
  showEditGroup.value = true
}

function resetEditGroupForm() {
  editingGroup.value = null
  editGroupForm.value = {
    name: '',
    groupType: 'WORK',
    announcement: '',
  }
}

function resetKeywordFilterForm() {
  editingKeywordFilterId.value = null
  keywordFilterForm.value = {
    pattern: '',
    replacement: '',
    action: 'REPLACE',
    enabled: true,
  }
}

function openKeywordFilterDialog(row?: KeywordFilter) {
  if (row) {
    editingKeywordFilterId.value = row.id
    keywordFilterForm.value = {
      pattern: row.pattern,
      replacement: row.replacement ?? '',
      action: row.action,
      enabled: row.enabled,
    }
  } else {
    resetKeywordFilterForm()
  }
  showKeywordFilterDialog.value = true
}

function resetGroupManageDialog() {
  showGroupManageDialog.value = false
  managedGroup.value = null
  groupManageTab.value = 'members'
  managedGroupMembers.value = []
  managedGroupJoinRequests.value = []
  groupMemberSearchKeyword.value = ''
  groupMemberSearchResults.value = []
}

function openManageGroupDialog(group: ImGroup) {
  managedGroup.value = group
  showGroupManageDialog.value = true
  groupManageTab.value = 'members'
  groupMemberSearchKeyword.value = ''
  groupMemberSearchResults.value = []
  loadManagedGroupMembers()
  loadManagedGroupJoinRequests()
}

function resetSendGroupJoinRequestForm() {
  sendGroupJoinRequestForm.value = { groupId: '', remark: '' }
}

function resetWebhookForm() {
  editingWebhookId.value = null
  webhookForm.value = { url: '', secret: '', enabled: true }
}

function resetMessageSearch() {
  historyForm.value = {
    keyword: '',
    chatType: '',
    msgType: '',
    timeRange: [],
  }
  messages.value = []
  historyTotal.value = 0
  historyPage.value = 0
}

function switchApp(val: string) {
  saveLastServiceApp('im', val)
  router.push(`/services/im/${val}`)
}

async function loadStats() {
  try {
    const res = await imAdminApi.getStats(appKey.value)
    stats.value = res.data.data
  } catch {
    stats.value = null
  }
}

async function loadUsers() {
  loadingUsers.value = true
  try {
    const res = await imAdminApi.listUsers(appKey.value, userPage.value, userPageSize)
    users.value = res.data.data.content
    userTotal.value = res.data.data.totalElements
    if (users.value.length > 0) {
      const ids = users.value.map(u => u.userId)
      try {
        const stateRes = await imAdminApi.queryUserState(appKey.value, ids)
        userOnlineState.value = stateRes.data.data
      } catch {
        // ignore state query error
      }
    }
  } catch {
    ElMessage.error('加载用户失败')
  } finally {
    loadingUsers.value = false
  }
}

function formatLastSeen(ts: number): string {
  if (!ts) return '未知'
  const diff = Date.now() - ts
  const minutes = Math.floor(diff / 60000)
  if (minutes < 1) return '刚刚'
  if (minutes < 60) return `${minutes}分钟前`
  const hours = Math.floor(minutes / 60)
  if (hours < 24) return `${hours}小时前`
  const days = Math.floor(hours / 24)
  return `${days}天前`
}

async function loadGroups() {
  loadingGroups.value = true
  try {
    const res = await imAdminApi.listGroups(appKey.value)
    groups.value = res.data.data
  } catch {
    ElMessage.error('加载群组失败')
  } finally {
    loadingGroups.value = false
  }
}

async function loadMessages(page = historyPage.value) {
  loadingMessages.value = true
  try {
    const [startTime, endTime] = historyForm.value.timeRange ?? []
    const res = await imAdminApi.searchMessages(appKey.value, {
      keyword: historyForm.value.keyword.trim() || undefined,
      chatType: historyForm.value.chatType || undefined,
      msgType: historyForm.value.msgType || undefined,
      startTime: startTime ? toLocalDateTime(startTime) : undefined,
      endTime: endTime ? toLocalDateTime(endTime) : undefined,
      page,
      size: historyPageSize,
    })
    messages.value = res.data.data.content
    historyTotal.value = res.data.data.totalElements
    historyPage.value = page
  } catch {
    ElMessage.error('加载消息历史失败')
  } finally {
    loadingMessages.value = false
  }
}

async function loadOperationLogs(page = operationLogPage.value) {
  loadingLogs.value = true
  try {
    const res = await imAdminApi.getOperationLogs(appKey.value, page, operationLogPageSize)
    operationLogs.value = res.data.data.content
    operationLogTotal.value = res.data.data.totalElements
    operationLogPage.value = page
  } catch {
    ElMessage.error('加载操作日志失败')
  } finally {
    loadingLogs.value = false
  }
}

async function loadWebhooks() {
  loadingWebhooks.value = true
  try {
    const res = await imAdminApi.listWebhooks(appKey.value)
    webhooks.value = res.data.data
  } catch {
    ElMessage.error('加载回调配置失败')
  } finally {
    loadingWebhooks.value = false
  }
}

async function loadKeywordFilters() {
  loadingKeywordFilters.value = true
  try {
    const res = await imAdminApi.listKeywordFilters(appKey.value)
    keywordFilters.value = res.data.data
  } catch {
    ElMessage.error('加载过滤规则失败')
  } finally {
    loadingKeywordFilters.value = false
  }
}

async function loadGlobalMute() {
  loadingGlobalMute.value = true
  try {
    const res = await imAdminApi.getGlobalMute(appKey.value)
    globalMute.value = res.data.data
  } catch {
    ElMessage.error('加载全局禁言状态失败')
  } finally {
    loadingGlobalMute.value = false
  }
}

async function loadGroupJoinRequests() {
  const groupId = groupRequestGroupId.value.trim()
  if (!groupId) {
    ElMessage.warning('请先输入群组ID')
    return
  }
  loadingGroupRequests.value = true
  try {
    const res = await imAdminApi.listGroupJoinRequests(appKey.value, groupId)
    groupJoinRequests.value = res.data.data
  } catch {
    ElMessage.error('加载入群申请失败')
  } finally {
    loadingGroupRequests.value = false
  }
}

async function loadManagedGroupMembers() {
  const group = managedGroup.value
  if (!group) return
  const groupId = group.id
  loadingManagedGroupMembers.value = true
  try {
    const res = await imAdminApi.listGroupMembers(appKey.value, group.id)
    if (managedGroup.value?.id === groupId) {
      managedGroupMembers.value = res.data.data
    }
  } catch {
    ElMessage.error('加载群成员失败')
  } finally {
    loadingManagedGroupMembers.value = false
  }
}

async function loadManagedGroupJoinRequests() {
  const group = managedGroup.value
  if (!group) return
  const groupId = group.id
  loadingManagedGroupJoinRequests.value = true
  try {
    const res = await imAdminApi.listGroupJoinRequests(appKey.value, group.id)
    if (managedGroup.value?.id === groupId) {
      managedGroupJoinRequests.value = res.data.data
    }
  } catch {
    ElMessage.error('加载入群申请失败')
  } finally {
    loadingManagedGroupJoinRequests.value = false
  }
}

function onGroupMemberSearchInput() {
  if (groupMemberSearchTimer) clearTimeout(groupMemberSearchTimer)
  const keyword = groupMemberSearchKeyword.value.trim()
  if (!keyword) {
    groupMemberSearchResults.value = []
    return
  }
  groupMemberSearchTimer = setTimeout(async () => {
    searchingGroupMembers.value = true
    try {
      const res = await imAdminApi.searchUsers(appKey.value, keyword)
      const selectedIds = new Set(managedGroupMembers.value.map(item => item.userId))
      groupMemberSearchResults.value = res.data.data.filter(user => !selectedIds.has(user.userId))
    } catch {
      groupMemberSearchResults.value = []
    } finally {
      searchingGroupMembers.value = false
    }
  }, 300)
}

function groupMemberRoleLabel(userId: string) {
  const group = managedGroup.value
  if (!group) return '-'
  if (group.creatorId === userId) return '群主'
  return parseIdList(group.adminIds).includes(userId) ? '管理员' : '成员'
}

function groupMemberRoleTagType(userId: string) {
  const label = groupMemberRoleLabel(userId)
  return { 群主: 'warning', 管理员: 'success', 成员: 'info' }[label] ?? 'info'
}

async function addManagedGroupMember(user: ImUser) {
  const group = managedGroup.value
  if (!group) return
  try {
    await imAdminApi.addGroupMember(appKey.value, group.id, user.userId)
    ElMessage.success('已加入群组')
    groupMemberSearchResults.value = groupMemberSearchResults.value.filter(item => item.userId !== user.userId)
    await loadManagedGroupMembers()
    await loadGroups()
  } catch {
    ElMessage.error('添加群成员失败')
  }
}

async function removeManagedGroupMember(user: ImUser) {
  const group = managedGroup.value
  if (!group) return
  await ElMessageBox.confirm(`确认移除群成员 ${user.userId}？`, '移除群成员', {
    type: 'warning',
    confirmButtonText: '确认',
    cancelButtonText: '取消',
  })
  await imAdminApi.removeGroupMember(appKey.value, group.id, user.userId)
  ElMessage.success('成员已移除')
  await loadManagedGroupMembers()
  await loadGroups()
}

async function transferGroupOwner(user: ImUser) {
  const group = managedGroup.value
  if (!group) return
  await ElMessageBox.confirm(`确认将群主转让给 ${user.userId}？`, '转让群主', {
    type: 'warning',
    confirmButtonText: '确认',
    cancelButtonText: '取消',
  })
  await imAdminApi.transferGroupOwner(appKey.value, group.id, user.userId)
  ElMessage.success('群主已转让')
  await loadManagedGroupMembers()
  await loadGroups()
}

function openGroupAttributesDialog() {
  const group = managedGroup.value
  if (!group) return
  groupAttributesJson.value = '{}'
  showGroupAttributesDialog.value = true
}

async function submitGroupAttributes() {
  const group = managedGroup.value
  if (!group) return
  try {
    const attributes = JSON.parse(groupAttributesJson.value)
    await imAdminApi.updateGroupAttributes(appKey.value, group.id, attributes)
    ElMessage.success('扩展属性已保存')
    showGroupAttributesDialog.value = false
  } catch {
    ElMessage.error('JSON 格式错误')
  }
}

function openReadReceiptsDialog() {
  showReadReceiptsDialog.value = true
  loadGroupReadReceipts()
}

async function loadGroupReadReceipts() {
  const group = managedGroup.value
  if (!group) return
  loadingReadReceipts.value = true
  try {
    const res = await imAdminApi.getGroupReadReceipts(appKey.value, group.id)
    groupReadReceipts.value = res.data.data ?? []
  } catch {
    groupReadReceipts.value = []
  } finally {
    loadingReadReceipts.value = false
  }
}

async function toggleManagedGroupRole(user: ImUser) {
  const group = managedGroup.value
  if (!group) return
  const nextRole = groupMemberRoleLabel(user.userId) === '管理员' ? 'MEMBER' : 'ADMIN'
  await imAdminApi.setGroupRole(appKey.value, group.id, user.userId, nextRole)
  ElMessage.success('群成员角色已更新')
  await loadManagedGroupMembers()
  await loadGroups()
}

async function muteManagedGroupMember(user: ImUser) {
  const group = managedGroup.value
  if (!group) return
  const { value } = await ElMessageBox.prompt('请输入禁言时长（分钟）', '群成员禁言', {
    confirmButtonText: '确认',
    cancelButtonText: '取消',
    inputPattern: /^\d+$/,
    inputErrorMessage: '请输入非负整数',
    type: 'warning',
  })
  const minutes = Number(value)
  await imAdminApi.muteGroupMember(appKey.value, group.id, user.userId, minutes)
  ElMessage.success('成员已禁言')
}

async function toggleGlobalMute() {
  savingGlobalMute.value = true
  try {
    const res = await imAdminApi.setGlobalMute(appKey.value, globalMuteEnabled.value)
    globalMute.value = res.data.data
    ElMessage.success(globalMuteEnabled.value ? '全局禁言已开启' : '全局禁言已关闭')
  } catch {
    ElMessage.error('更新全局禁言失败')
    await loadGlobalMute()
  } finally {
    savingGlobalMute.value = false
  }
}

async function submitKeywordFilter() {
  if (!keywordFilterForm.value.pattern.trim()) {
    ElMessage.warning('请填写命中词')
    return
  }
  submittingKeywordFilter.value = true
  try {
    const payload = {
      pattern: keywordFilterForm.value.pattern.trim(),
      replacement: keywordFilterForm.value.replacement.trim() || undefined,
      action: keywordFilterForm.value.action,
      enabled: keywordFilterForm.value.enabled,
    }
    if (editingKeywordFilterId.value) {
      await imAdminApi.updateKeywordFilter(appKey.value, editingKeywordFilterId.value, payload)
      ElMessage.success('过滤规则已更新')
    } else {
      await imAdminApi.createKeywordFilter(appKey.value, payload)
      ElMessage.success('过滤规则已创建')
    }
    showKeywordFilterDialog.value = false
    resetKeywordFilterForm()
    loadKeywordFilters()
  } catch {
    ElMessage.error('保存过滤规则失败')
  } finally {
    submittingKeywordFilter.value = false
  }
}

async function deleteKeywordFilter(row: KeywordFilter) {
  await ElMessageBox.confirm(`确认删除过滤规则 ${row.pattern}？`, '删除过滤规则', {
    type: 'warning',
    confirmButtonText: '确认',
    cancelButtonText: '取消',
  })
  await imAdminApi.deleteKeywordFilter(appKey.value, row.id)
  ElMessage.success('过滤规则已删除')
  loadKeywordFilters()
}

async function searchMessages() {
  await loadMessages(0)
}

function openCreateWebhookDialog() {
  resetWebhookForm()
  showWebhookDialog.value = true
}

function openEditWebhookDialog(row: WebhookConfig) {
  editingWebhookId.value = row.id
  webhookForm.value = {
    url: row.url,
    secret: '',
    enabled: row.enabled,
  }
  showWebhookDialog.value = true
}

async function submitWebhookForm() {
  if (!webhookForm.value.url.trim()) {
    ElMessage.warning('请填写回调地址')
    return
  }
  submittingWebhook.value = true
  try {
    const payload = {
      url: webhookForm.value.url.trim(),
      secret: webhookForm.value.secret.trim() || undefined,
      enabled: webhookForm.value.enabled,
    }
    if (editingWebhookId.value) {
      await imAdminApi.updateWebhook(appKey.value, editingWebhookId.value, payload)
      ElMessage.success('回调配置已更新')
    } else {
      await imAdminApi.createWebhook(appKey.value, payload)
      ElMessage.success('回调配置已创建')
    }
    showWebhookDialog.value = false
    resetWebhookForm()
    loadWebhooks()
  } catch {
    ElMessage.error('保存回调配置失败')
  } finally {
    submittingWebhook.value = false
  }
}

async function deleteWebhook(row: WebhookConfig) {
  await ElMessageBox.confirm(`确认删除回调配置 ${row.url}？`, '删除回调配置', {
    type: 'warning',
    confirmButtonText: '确认',
    cancelButtonText: '取消',
  })
  await imAdminApi.deleteWebhook(appKey.value, row.id)
  ElMessage.success('回调配置已删除')
  loadWebhooks()
}

async function toggleUserStatus(user: ImUser) {
  const nextStatus = user.status === 'ACTIVE' ? 'BANNED' : 'ACTIVE'
  const action = nextStatus === 'BANNED' ? '封禁' : '解封'
  await ElMessageBox.confirm(`确认${action}用户 ${user.userId}？`, `${action}用户`, {
    type: 'warning',
    confirmButtonText: '确认',
    cancelButtonText: '取消',
  })
  await imAdminApi.updateUserStatus(appKey.value, user.userId, nextStatus)
  ElMessage.success(`已${action}`)
  loadUsers()
}

async function revokeMessage(message: ImMessage) {
  await ElMessageBox.confirm(`确认撤回消息 ${message.id}？`, '撤回消息', {
    type: 'warning',
    confirmButtonText: '确认',
    cancelButtonText: '取消',
  })
  await imAdminApi.revokeMessage(appKey.value, message.id)
  ElMessage.success('消息已撤回')
  loadMessages(historyPage.value)
}

async function approveGroupJoin(request: GroupJoinRequest) {
  const groupId = managedGroup.value?.id || groupRequestGroupId.value.trim()
  if (!groupId) {
    ElMessage.warning('请先输入群组ID')
    return
  }
  await ElMessageBox.confirm(`确认同意群 ${groupId} 的入群申请 ${request.requesterId}？`, '同意入群申请', {
    type: 'warning',
    confirmButtonText: '确认',
    cancelButtonText: '取消',
  })
  await imAdminApi.acceptGroupJoinRequest(appKey.value, groupId, request.id)
  ElMessage.success('已同意入群申请')
  loadGroupJoinRequests()
}

async function declineGroupJoin(request: GroupJoinRequest) {
  const groupId = managedGroup.value?.id || groupRequestGroupId.value.trim()
  if (!groupId) {
    ElMessage.warning('请先输入群组ID')
    return
  }
  await ElMessageBox.confirm(`确认拒绝群 ${groupId} 的入群申请 ${request.requesterId}？`, '拒绝入群申请', {
    type: 'warning',
    confirmButtonText: '确认',
    cancelButtonText: '取消',
  })
  await imAdminApi.rejectGroupJoinRequest(appKey.value, groupId, request.id)
  ElMessage.success('已拒绝入群申请')
  loadGroupJoinRequests()
}

async function submitSendGroupJoinRequest() {
  const groupId = sendGroupJoinRequestForm.value.groupId.trim()
  if (!groupId) {
    ElMessage.warning('请填写群组ID')
    return
  }
  submittingSendGroupJoinRequest.value = true
  try {
    await imAdminApi.sendGroupJoinRequest(
      appKey.value,
      groupId,
      sendGroupJoinRequestForm.value.remark.trim() || undefined,
    )
    ElMessage.success('入群申请已发送')
    showSendGroupJoinRequest.value = false
    resetSendGroupJoinRequestForm()
    groupRequestGroupId.value = groupId
    loadGroupJoinRequests()
  } catch {
    ElMessage.error('发送失败')
  } finally {
    submittingSendGroupJoinRequest.value = false
  }
}

async function dismissGroup(group: ImGroup) {
  await ElMessageBox.confirm(`确认解散群组 ${group.name}？`, '解散群组', {
    type: 'warning',
    confirmButtonText: '确认',
    cancelButtonText: '取消',
  })
  await imAdminApi.dismissGroup(appKey.value, group.id)
  ElMessage.success('群组已解散')
  loadGroups()
}

async function submitRegisterUser() {
  if (!registerForm.value.userId.trim() && !editingUserId.value) {
    ElMessage.warning('请填写用户ID')
    return
  }
  submittingRegister.value = true
  try {
      const payload = {
        nickname: registerForm.value.nickname.trim() || undefined,
        avatar: registerForm.value.avatar.trim() || undefined,
        gender: registerForm.value.gender,
        status: registerForm.value.status,
        admin: registerForm.value.admin,
      }
      if (editingUserId.value) {
        await imAdminApi.updateUser(appKey.value, editingUserId.value, payload)
        ElMessage.success('用户已更新')
      } else {
      await imAdminApi.registerUser(
        appKey.value,
        registerForm.value.userId.trim(),
        payload.nickname,
        payload.avatar,
        payload.gender,
        payload.status,
        payload.admin,
      )
      ElMessage.success('用户注册成功')
    }
    showRegisterUser.value = false
    loadUsers()
    loadStats()
  } catch {
    ElMessage.error(editingUserId.value ? '更新失败' : '注册失败，用户ID可能已存在')
  } finally {
    submittingRegister.value = false
  }
}

async function submitCreateGroup() {
  if (!createGroupForm.value.name.trim() || !createGroupForm.value.creatorId.trim()) {
    ElMessage.warning('请填写群名称和创建者ID')
    return
  }
  submittingCreateGroup.value = true
  try {
    const memberIds = selectedMembers.value.map(item => item.userId)
    await imAdminApi.createGroup(
      appKey.value,
      createGroupForm.value.name.trim(),
      createGroupForm.value.creatorId.trim(),
      memberIds,
      createGroupForm.value.groupType,
      createGroupForm.value.announcement.trim() || undefined,
    )
    ElMessage.success('群组创建成功')
    showCreateGroup.value = false
    loadGroups()
    loadStats()
  } catch {
    ElMessage.error('创建失败')
  } finally {
    submittingCreateGroup.value = false
  }
}

async function submitEditGroup() {
  if (!editingGroup.value) return
  if (!editGroupForm.value.name.trim()) {
    ElMessage.warning('请填写群名称')
    return
  }
  submittingEditGroup.value = true
  try {
    await imAdminApi.updateGroup(appKey.value, editingGroup.value.id, {
      name: editGroupForm.value.name.trim(),
      groupType: editGroupForm.value.groupType,
      announcement: editGroupForm.value.announcement.trim() || undefined,
    })
    ElMessage.success('群组已更新')
    showEditGroup.value = false
    loadGroups()
  } catch {
    ElMessage.error('更新失败')
  } finally {
    submittingEditGroup.value = false
  }
}

function handleTabChange(tab: string) {
  if (tab === 'groups' && groups.value.length === 0) {
    loadGroups()
  } else if (tab === 'governance') {
    if (keywordFilters.value.length === 0) {
      loadKeywordFilters()
    }
    if (!globalMute.value) {
      loadGlobalMute()
    }
  } else if (tab === 'logs' && operationLogs.value.length === 0) {
    loadOperationLogs()
  } else if (tab === 'webhooks' && webhooks.value.length === 0) {
    loadWebhooks()
  }
}

function handleUserPageChange(page: number) {
  userPage.value = page - 1
  loadUsers()
}

function handleHistoryPageChange(page: number) {
  loadMessages(page - 1)
}

function handleOperationLogPageChange(page: number) {
  loadOperationLogs(page - 1)
}

watch(appKey, (key) => {
  if (isServicesPortal.value) {
    clearPolling()
    serviceEnabled.value = null
    if (key) checkServiceEnabled(key)
  }
})

onMounted(() => {
  if (isServicesPortal.value) {
    appApi.list().then(res => {
      portalApps.value = res.data.data
      if (!appKey.value && portalApps.value.length) {
        const saved = getLastServiceApp('im')
        const target = saved && portalApps.value.some(a => a.appKey === saved) ? saved : portalApps.value[0].appKey
        switchApp(target)
      }
    })
    if (!appKey.value) return
    checkServiceEnabled(appKey.value)
    return
  }
  loadStats()
  loadUsers()
})

onBeforeUnmount(() => {
  clearPolling()
})

function logActionLabel(action: string) {
  return {
    REGISTER_USER: '注册用户',
    UPDATE_USER: '更新用户',
    UPDATE_USER_STATUS: '调整状态',
    CREATE_GROUP: '创建群组',
    UPDATE_GROUP: '更新群组',
    ADMIN_ADD_GROUP_MEMBER: '添加群成员',
    ADMIN_REMOVE_GROUP_MEMBER: '移除群成员',
    ADMIN_SET_GROUP_ROLE: '调整群角色',
    ADMIN_MUTE_GROUP_MEMBER: '群成员禁言',
    ADMIN_ACCEPT_GROUP_JOIN_REQUEST: '同意入群申请',
    ADMIN_REJECT_GROUP_JOIN_REQUEST: '拒绝入群申请',
    ADMIN_REVOKE_MESSAGE: '撤回消息',
    CREATE_FRIEND_REQUEST: '创建好友申请',
    ACCEPT_FRIEND_REQUEST: '同意好友申请',
    REJECT_FRIEND_REQUEST: '拒绝好友申请',
    ADD_BLACKLIST: '新增黑名单',
    REMOVE_BLACKLIST: '移除黑名单',
    CREATE_WEBHOOK: '创建回调',
    UPDATE_WEBHOOK: '更新回调',
    DELETE_WEBHOOK: '删除回调',
    CREATE_KEYWORD_FILTER: '创建过滤',
    UPDATE_KEYWORD_FILTER: '更新过滤',
    DELETE_KEYWORD_FILTER: '删除过滤',
    SET_GLOBAL_MUTE: '全局禁言',
    VIEW_STATS: '查看统计',
    VIEW_HISTORY: '查看消息',
    SEARCH_USERS: '搜索用户',
    SEARCH_GROUPS: '搜索群组',
    SEARCH_MESSAGES: '搜索消息',
  }[action] ?? action
}

function logResourceLabel(resourceType: string) {
  return {
    ACCOUNT: '用户',
    GROUP: '群组',
    MESSAGE: '消息',
    FRIEND_REQUEST: '好友申请',
    BLACKLIST: '黑名单',
    GROUP_JOIN_REQUEST: '入群申请',
    WEBHOOK: '回调',
    KEYWORD_FILTER: '过滤',
    GLOBAL_MUTE: '全局禁言',
    STATS: '统计',
  }[resourceType] ?? resourceType
}

function formatLogDetail(detail?: string | null) {
  if (!detail) return '-'
  const trimmed = detail.trim()
  if (!trimmed) return '-'
  try {
    const parsed = JSON.parse(trimmed)
    return typeof parsed === 'object' ? JSON.stringify(parsed, null, 2) : String(parsed)
  } catch {
    return trimmed
  }
}

function userAvatarFallback(row: ImUser) {
  return (row.nickname || row.userId || '?').slice(0, 1).toUpperCase()
}
</script>

<style scoped>
.stat-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 8px 0;
}

.stat-value {
  font-size: 28px;
  font-weight: 800;
  color: #1f2933;
}

.stat-label {
  font-size: 13px;
  color: #6b7280;
}

.toolbar {
  margin-bottom: 12px;
}

.table-wrap {
  overflow-x: auto;
}

.responsive-toolbar {
  flex-wrap: wrap;
}

.stat-grid {
  margin-bottom: 16px;
}

.toolbar-space-between {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.toolbar-group {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.section-block {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.section-title {
  font-size: 14px;
  font-weight: 600;
  color: #1f2933;
}

.search-results {
  width: 100%;
  margin-top: 4px;
  border: 1px solid #e4e7ed;
  border-radius: 4px;
  max-height: 180px;
  overflow-y: auto;
  background: #fff;
}

.search-result-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 12px;
  cursor: pointer;
  transition: background 0.15s;
}

.search-result-item:hover {
  background: #f0f4ff;
}

.result-nickname {
  font-weight: 500;
  color: #303133;
}

.result-userid {
  font-size: 12px;
  color: #909399;
}

.selected-members {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  min-height: 32px;
  width: 100%;
}

.no-members-hint {
  font-size: 13px;
  color: #c0c4cc;
}

.content-preview {
  display: inline-block;
  max-width: 100%;
  word-break: break-all;
}

.log-detail {
  white-space: pre-wrap;
  word-break: break-word;
  line-height: 1.5;
}

@media (max-width: 767px) {
  .stat-grid :deep(.el-col) {
    margin-bottom: 12px;
  }

  .toolbar-space-between {
    flex-direction: column;
    align-items: stretch;
  }

  .toolbar-group {
    width: 100%;
  }

  .toolbar-group :deep(.el-button),
  .toolbar-group :deep(.el-select),
  .toolbar-group :deep(.el-input),
  .toolbar-group :deep(.el-switch) {
    width: 100%;
  }

  .responsive-toolbar :deep(.el-button),
  .responsive-toolbar :deep(.el-select),
  .responsive-toolbar :deep(.el-input),
  .responsive-toolbar :deep(.el-radio-group) {
    width: 100%;
  }

  .responsive-toolbar :deep(.el-radio-group) {
    display: flex;
    flex-wrap: wrap;
  }

  .responsive-toolbar :deep(.el-radio-button) {
    flex: 1 1 32%;
  }

  .table-wrap :deep(.el-table) {
    min-width: 920px;
  }

  .section-head {
    flex-direction: column;
    align-items: stretch;
  }

  .section-head .toolbar-group {
    justify-content: flex-start;
  }

  .search-results {
    max-height: 240px;
  }
}

.portal-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 24px;
}
.portal-bar-title {
  font-size: 16px;
  font-weight: 600;
}
</style>
