<script setup lang="ts">
import { ref, onMounted } from 'vue'
import {
  getOperationLogs,
  exportOperationLogs,
  type OperationLog,
} from '@/api/operationLog'
import AppLayout from '@/components/AppLayout.vue'

const logs = ref<OperationLog[]>([])
const loading = ref(false)
const exporting = ref(false)
const totalElements = ref(0)
const currentPage = ref(0)
const pageSize = ref(15)
const totalPages = ref(0)

const keyword = ref('')
const filterUsername = ref('')
const filterModule = ref('')
const filterStatus = ref('')
const startDate = ref('')
const endDate = ref('')

const showDetail = ref(false)
const detailLog = ref<OperationLog | null>(null)
const showRawData = ref(false)

const statusOptions = [
  { label: '全部状态', value: '' },
  { label: '操作成功', value: '0' },
  { label: '操作失败', value: '1' },
]

const moduleOptions = [
  { label: '全部模块', value: '' },
  { label: '用户管理', value: '用户管理' },
  { label: '角色管理', value: '角色管理' },
  { label: '菜单管理', value: '菜单管理' },
  { label: '字典管理', value: '字典管理' },
  { label: '配置管理', value: '配置管理' },
  { label: '会员管理', value: '会员管理' },
  { label: '会员卡管理', value: '会员卡管理' },
  { label: '团课管理', value: '团课管理' },
  { label: '签到管理', value: '签到管理' },
  { label: '认证', value: '认证' },
  { label: '公告管理', value: '公告管理' },
  { label: '消息管理', value: '消息管理' },
  { label: '数据字典', value: '数据字典' },
  { label: '权限管理', value: '权限管理' },
]

// ---- 业务语言翻译 ----

/** 将操作模块拆分为 module 和 action */
function parseOperation(log: OperationLog): { module: string; action: string } {
  const parts = log.operation.split(' - ')
  return {
    module: parts[0] || '',
    action: parts.slice(1).join(' - ') || parts[0] || '',
  }
}

/** 将 HTTP 方法 + 路径翻译为业务描述 */
function translateHttpMethod(log: OperationLog): string {
  const { action } = parseOperation(log)
  const method = log.method || ''

  if (method.includes('POST') || method.includes('PUT') || method.includes('DELETE') || method.includes('PATCH')) {
    const httpMethod = method.split(' ')[0] || ''
    const methodMap: Record<string, string> = {
      POST: '新增操作',
      PUT: '修改操作',
      DELETE: '删除操作',
      PATCH: '修改操作',
    }
    return methodMap[httpMethod] || action || '未知操作'
  }
  return '查询操作'
}

/** 将参数 JSON 转为可读的键值列表 */
function parseParamsToReadable(params: string | null): Array<{ key: string; value: string }> {
  if (!params) return []
  try {
    // Try parsing as JSON first
    const obj = JSON.parse(params)
    if (Array.isArray(obj)) {
      return obj.length > 0
        ? [{ key: '数据列表', value: `共 ${obj.length} 条记录` }]
        : []
    }
    if (typeof obj === 'object' && obj !== null) {
      return Object.entries(obj)
        .filter(([, v]) => v !== null && v !== undefined && v !== '')
        .map(([k, v]) => ({
          key: translateFieldName(k),
          value: formatFieldValue(k, v),
        }))
    }
    return [{ key: '参数', value: String(obj) }]
  } catch {
    // URL query string style: key=value&key2=value2
    if (params.includes('=') || params.includes('&')) {
      const pairs = params.split('&').filter(Boolean)
      return pairs.map((pair) => {
        const [k, v] = pair.split('=')
        return { key: translateFieldName(k || ''), value: decodeURIComponent(v || '') }
      })
    }
    // Plain text
    return [{ key: '内容', value: params }]
  }
}

/** 字段名映射为中文 */
function translateFieldName(key: string): string {
  const map: Record<string, string> = {
    id: '编号',
    username: '用户名',
    nickname: '昵称',
    password: '密码',
    phone: '手机号',
    email: '邮箱',
    gender: '性别',
    birthday: '生日',
    address: '地址',
    avatar: '头像',
    roleName: '角色名称',
    roleKey: '角色标识',
    roleSort: '排序',
    status: '状态',
    menuName: '菜单名称',
    menuType: '菜单类型',
    orderNum: '排序号',
    component: '组件路径',
    perms: '权限标识',
    parentId: '上级编号',
    configKey: '配置键',
    configValue: '配置值',
    dictName: '字典名称',
    dictType: '字典类型',
    dictLabel: '字典标签',
    dictValue: '字典值',
    memberNo: '会员编号',
    cardName: '卡片名称',
    cardType: '卡片类型',
    price: '价格',
    totalTimes: '总次数',
    validityDays: '有效天数',
    expireTime: '到期时间',
    content: '内容',
    title: '标题',
    reason: '原因',
    page: '页码',
    size: '每页条数',
    sort: '排序字段',
    order: '排序方式',
    keyword: '关键字',
    startTime: '开始时间',
    endTime: '结束时间',
  }
  return map[key] || key
}

/** 将字段值格式化为可读文本 */
function formatFieldValue(key: string, value: unknown): string {
  if (value === null || value === undefined) return '--'
  if (key === 'gender') return value === 1 ? '男' : value === 2 ? '女' : '未知'
  if (key === 'status') return value === 0 || value === '0' ? '启用' : '停用'
  if (key === 'order') return value === 'desc' ? '降序' : '升序'
  return String(value)
}

/** 将执行结果翻译为业务语言 */
function translateResult(result: string | null): string {
  if (!result) return '无记录'
  // HTTP 200 (45ms) -> "操作成功"
  const match = result.match(/HTTP\s*(\d+)\s*\((\d+)ms\)/)
  if (match) {
    const code = parseInt(match[1]!)
    const ms = parseInt(match[2]!)
    if (code >= 200 && code < 300) {
      return `操作成功${ms > 0 ? `（系统处理耗时 ${formatDuration(ms)}）` : ''}`
    }
    return `操作异常（状态码 ${code}）`
  }
  return result
}

// ---- 原有函数 ----

async function fetchLogs() {
  loading.value = true
  try {
    const params: Record<string, string | number> = {
      page: currentPage.value,
      size: pageSize.value,
      sort: 'createdAt',
      order: 'desc',
    }
    if (keyword.value) params.keyword = keyword.value
    if (filterUsername.value) params.username = filterUsername.value
    if (filterModule.value) params.operation = filterModule.value
    if (filterStatus.value) params.status = filterStatus.value
    if (startDate.value) params.startTime = startDate.value + 'T00:00:00'
    if (endDate.value) params.endTime = endDate.value + 'T23:59:59'

    const result = await getOperationLogs(params)
    logs.value = result.content || []
    totalElements.value = result.totalElements
    totalPages.value = result.totalPages
  } catch {
    logs.value = []
  } finally {
    loading.value = false
  }
}

function handleSearch() {
  currentPage.value = 0
  fetchLogs()
}

function handleReset() {
  keyword.value = ''
  filterUsername.value = ''
  filterModule.value = ''
  filterStatus.value = ''
  startDate.value = ''
  endDate.value = ''
  currentPage.value = 0
  fetchLogs()
}

function goPage(page: number) {
  if (page < 0 || page >= totalPages.value) return
  currentPage.value = page
  fetchLogs()
}

function showDetailModal(log: OperationLog) {
  detailLog.value = log
  showRawData.value = false
  showDetail.value = true
}

function closeDetailModal() {
  showDetail.value = false
  detailLog.value = null
  showRawData.value = false
}

function formatTime(t: string | null): string {
  if (!t) return '--'
  return new Date(t).toLocaleString('zh-CN')
}

function formatDuration(ms: number): string {
  if (ms < 1000) return ms + ' 毫秒'
  if (ms < 60000) return (ms / 1000).toFixed(1) + ' 秒'
  return (ms / 60000).toFixed(1) + ' 分钟'
}

function statusText(status: string) {
  return status === '0' ? '成功' : '失败'
}

function statusBadge(status: string) {
  return status === '0' ? 'success' : 'error'
}

async function handleExport() {
  exporting.value = true
  try {
    const params: Record<string, string> = {}
    if (filterUsername.value) params.username = filterUsername.value
    if (filterModule.value) params.operation = filterModule.value
    if (filterStatus.value) params.status = filterStatus.value
    if (keyword.value) params.keyword = keyword.value
    if (startDate.value) params.startTime = startDate.value + 'T00:00:00'
    if (endDate.value) params.endTime = endDate.value + 'T23:59:59'

    const blob = await exportOperationLogs(params)
    const url = window.URL.createObjectURL(blob)
    const link = document.createElement('a')
    link.href = url
    const now = new Date()
    const filename = `操作日志_${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}${String(now.getDate()).padStart(2, '0')}_${String(now.getHours()).padStart(2, '0')}${String(now.getMinutes()).padStart(2, '0')}${String(now.getSeconds()).padStart(2, '0')}.xlsx`
    link.download = filename
    document.body.appendChild(link)
    link.click()
    document.body.removeChild(link)
    window.URL.revokeObjectURL(url)
  } catch {
    // 导出失败
  } finally {
    exporting.value = false
  }
}

onMounted(fetchLogs)
</script>

<template>
  <AppLayout>
    <!-- 筛选栏 -->
    <div class="filter-bar">
      <div class="filter-row">
        <input v-model="keyword" class="filter-input" placeholder="输入关键词搜索..." @keyup.enter="handleSearch" />
        <input v-model="filterUsername" class="filter-input short" placeholder="操作人" />
        <select v-model="filterModule" class="filter-select">
          <option v-for="o in moduleOptions" :key="o.value" :value="o.value">{{ o.label }}</option>
        </select>
        <select v-model="filterStatus" class="filter-select">
          <option v-for="o in statusOptions" :key="o.value" :value="o.value">{{ o.label }}</option>
        </select>
        <input v-model="startDate" type="date" class="filter-input date" />
        <span class="date-sep">至</span>
        <input v-model="endDate" type="date" class="filter-input date" />
        <button class="btn btn-primary" @click="handleSearch">
          <i class="fas fa-search"></i> 查询
        </button>
        <button class="btn btn-outline" @click="handleReset">
          <i class="fas fa-redo"></i> 重置
        </button>
        <div class="filter-spacer"></div>
        <button class="btn btn-export" :disabled="exporting" @click="handleExport">
          <i class="fas fa-download"></i>
          {{ exporting ? '导出中...' : '导出报表' }}
        </button>
      </div>
    </div>

    <!-- 加载骨架 -->
    <div v-if="loading" class="table-container">
      <table class="data-table">
        <thead>
          <tr>
            <th style="width: 90px">操作人</th>
            <th style="width: 100px">操作类型</th>
            <th style="width: 160px">操作描述</th>
            <th style="width: 110px">操作模块</th>
            <th style="width: 90px">状态</th>
            <th style="width: 80px">耗时</th>
            <th style="width: 150px">操作时间</th>
            <th style="width: 70px">详情</th>
          </tr>
        </thead>
      </table>
      <div v-for="i in 8" :key="'sk-' + i" class="sk-row">
        <div class="sk-cell skeleton"></div>
      </div>
    </div>

    <!-- 空状态 -->
    <div v-else-if="!logs.length" class="empty-section">
      <div class="empty-state">
        <i class="fas fa-clipboard-list empty-icon"></i>
        <p>暂无操作记录</p>
        <span class="empty-hint">系统中尚未产生任何操作日志</span>
      </div>
    </div>

    <!-- 表格 -->
    <div v-else class="table-container">
      <table class="data-table">
        <thead>
          <tr>
            <th style="width: 90px">操作人</th>
            <th style="width: 100px">操作类型</th>
            <th style="width: 160px">操作描述</th>
            <th style="width: 110px">操作模块</th>
            <th style="width: 90px">状态</th>
            <th style="width: 80px">耗时</th>
            <th style="width: 150px">操作时间</th>
            <th style="width: 70px">详情</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="log in logs" :key="log.id">
            <td class="cell-username">
              <span class="avatar-dot">{{ log.username?.charAt(0) || '?' }}</span>
              {{ log.username }}
            </td>
            <td>
              <span class="badge-action" :class="translateHttpMethod(log).includes('新增') ? 'action-create'
                : translateHttpMethod(log).includes('修改') ? 'action-update'
                : translateHttpMethod(log).includes('删除') ? 'action-delete'
                : 'action-read'">
                {{ translateHttpMethod(log) }}
              </span>
            </td>
            <td class="cell-desc" :title="parseOperation(log).action">{{ parseOperation(log).action }}</td>
            <td>
              <span class="module-tag">{{ parseOperation(log).module }}</span>
            </td>
            <td>
              <span class="badge" :class="statusBadge(log.status)">
                {{ statusText(log.status) }}
              </span>
            </td>
            <td class="cell-duration">{{ formatDuration(log.duration) }}</td>
            <td class="cell-time">{{ formatTime(log.createdAt) }}</td>
            <td>
              <button class="btn-detail" @click="showDetailModal(log)">
                <i class="fas fa-eye"></i>
              </button>
            </td>
          </tr>
        </tbody>
      </table>

      <!-- 分页 -->
      <div class="pagination">
        <span class="page-info">共 {{ totalElements }} 条记录</span>
        <div class="page-controls">
          <button :disabled="currentPage <= 0" @click="goPage(0)">
            <i class="fas fa-angle-double-left"></i>
          </button>
          <button :disabled="currentPage <= 0" @click="goPage(currentPage - 1)">
            <i class="fas fa-angle-left"></i>
          </button>
          <span class="page-num">{{ currentPage + 1 }} / {{ totalPages || 1 }}</span>
          <button :disabled="currentPage >= totalPages - 1" @click="goPage(currentPage + 1)">
            <i class="fas fa-angle-right"></i>
          </button>
          <button :disabled="currentPage >= totalPages - 1" @click="goPage(totalPages - 1)">
            <i class="fas fa-angle-double-right"></i>
          </button>
        </div>
      </div>
    </div>

    <!-- 详情弹窗 -->
    <Teleport to="body">
      <transition name="modal">
        <div v-if="showDetail" class="modal-overlay" @click.self="closeDetailModal">
          <div class="modal-content">
            <div class="modal-header">
              <h3><i class="fas fa-info-circle modal-header-icon"></i>操作详情</h3>
              <button class="modal-close" @click="closeDetailModal">
                <i class="fas fa-times"></i>
              </button>
            </div>
            <div v-if="detailLog" class="modal-body">
              <!-- ===== 业务概要 ===== -->
              <div class="summary-card">
                <div class="summary-icon" :class="statusBadge(detailLog.status)">
                  <i :class="statusBadge(detailLog.status) === 'success' ? 'fas fa-check-circle' : 'fas fa-exclamation-circle'"></i>
                </div>
                <div class="summary-text">
                  <div class="summary-title">{{ translateResult(detailLog.result) }}</div>
                  <div class="summary-subtitle">
                    {{ parseOperation(detailLog).module }} — {{ parseOperation(detailLog).action }}
                  </div>
                  <div class="summary-meta">
                    操作人：<strong>{{ detailLog.username }}</strong>
                    &nbsp;|&nbsp;
                    时间：{{ formatTime(detailLog.createdAt) }}
                    &nbsp;|&nbsp;
                    耗时：{{ formatDuration(detailLog.duration) }}
                  </div>
                </div>
              </div>

              <!-- ===== 操作详情 ===== -->
              <div class="detail-section">
                <h4><i class="fas fa-list-ul"></i> 操作详情</h4>
                <div class="param-list" v-if="parseParamsToReadable(detailLog.params).length">
                  <div class="param-item" v-for="p in parseParamsToReadable(detailLog.params)" :key="p.key">
                    <span class="param-key">{{ p.key }}</span>
                    <span class="param-eq">=</span>
                    <span class="param-value">{{ p.value }}</span>
                  </div>
                </div>
                <div v-else class="no-data">本次操作无额外参数</div>
              </div>

              <!-- ===== 错误信息 ===== -->
              <div v-if="detailLog.errorMsg" class="detail-section error-section">
                <h4><i class="fas fa-exclamation-triangle"></i> 错误信息</h4>
                <div class="error-msg">{{ detailLog.errorMsg }}</div>
              </div>

              <!-- ===== 技术详情（折叠） ===== -->
              <div class="raw-toggle" :class="{ expanded: showRawData }">
                <button class="raw-toggle-btn" @click="showRawData = !showRawData">
                  <i class="fas" :class="showRawData ? 'fa-chevron-up' : 'fa-chevron-down'"></i>
                  技术详情（供技术人员排查用）
                </button>
                <div v-if="showRawData" class="raw-content">
                  <div class="raw-field">
                    <span class="raw-label">记录编号</span>
                    <span class="raw-value">{{ detailLog.id }}</span>
                  </div>
                  <div class="raw-field">
                    <span class="raw-label">接口地址</span>
                    <code class="raw-code">{{ detailLog.method }}</code>
                  </div>
                  <div class="raw-field">
                    <span class="raw-label">客户端 IP</span>
                    <span class="raw-value">{{ detailLog.ip }}</span>
                  </div>
                  <div class="raw-field">
                    <span class="raw-label">原始参数</span>
                    <pre class="raw-pre">{{ detailLog.params || '（无）' }}</pre>
                  </div>
                  <div class="raw-field">
                    <span class="raw-label">原始结果</span>
                    <pre class="raw-pre">{{ detailLog.result || '（无）' }}</pre>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </transition>
    </Teleport>
  </AppLayout>
</template>

<style scoped>
/* ---- filter bar ---- */
.filter-bar {
  margin-bottom: 20px;
}

.filter-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.filter-input {
  padding: 8px 14px;
  border-radius: 12px;
  border: 1px solid #e2e8f0;
  font-size: 13px;
  color: #1e293b;
  outline: none;
  background: white;
  transition: border-color 0.15s;
}

.filter-input:focus {
  border-color: #4f7cff;
}

.filter-input.short {
  width: 130px;
}

.filter-input.date {
  width: 145px;
}

.filter-select {
  padding: 8px 14px;
  border-radius: 12px;
  border: 1px solid #e2e8f0;
  font-size: 13px;
  color: #1e293b;
  outline: none;
  background: white;
  cursor: pointer;
}

.filter-spacer {
  flex: 1;
}

.date-sep {
  font-size: 13px;
  color: #8b9ab0;
}

.btn {
  padding: 8px 18px;
  border-radius: 20px;
  border: 1px solid #e2e8f0;
  background: white;
  font-weight: 500;
  font-size: 13px;
  color: #1e293b;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  transition: 0.15s;
  white-space: nowrap;
}

.btn-primary {
  background: #4f7cff;
  border-color: #4f7cff;
  color: white;
}

.btn-primary:hover { background: #3b6bf0; }

.btn-outline:hover {
  background: #f0f5ff;
  border-color: #4f7cff;
  color: #4f7cff;
}

.btn-export {
  background: #10b981;
  border-color: #10b981;
  color: white;
}

.btn-export:hover { background: #059669; }
.btn-export:disabled { opacity: 0.6; cursor: not-allowed; }

/* ---- table ---- */
.table-container {
  background: white;
  border-radius: 20px;
  border: 1px solid #edf2f9;
  overflow: hidden;
  animation: fadeIn 0.35s ease;
}

.data-table {
  width: 100%;
  border-collapse: collapse;
}

.data-table thead th {
  text-align: left;
  padding: 12px 14px;
  font-size: 12px;
  font-weight: 600;
  color: #6b7d94;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  border-bottom: 1px solid #edf2f9;
  background: #fafbfd;
}

.data-table tbody td {
  padding: 12px 14px;
  font-size: 13px;
  color: #1e293b;
  border-bottom: 1px solid #f1f5fa;
  vertical-align: middle;
}

.data-table tbody tr:last-child td {
  border-bottom: none;
}

.data-table tbody tr:hover {
  background: #f8fafd;
}

.cell-username {
  font-weight: 500;
  display: flex;
  align-items: center;
  gap: 8px;
}

.avatar-dot {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: #f0f5ff;
  color: #4f7cff;
  font-weight: 700;
  font-size: 12px;
  flex-shrink: 0;
}

.cell-desc {
  font-weight: 500;
  max-width: 160px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cell-duration {
  font-weight: 500;
  color: #6b7d94;
}

.cell-time {
  font-size: 12px;
  color: #8b9ab0;
}

/* ---- module tag ---- */
.module-tag {
  display: inline-block;
  padding: 2px 10px;
  border-radius: 20px;
  font-size: 12px;
  font-weight: 500;
  background: #eff6ff;
  color: #3b6bf0;
  border: 1px solid #bfdbfe;
}

/* ---- action badge ---- */
.badge-action {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 20px;
  font-size: 12px;
  font-weight: 600;
}

.badge-action.action-create {
  background: #d1fae5;
  color: #065f46;
}

.badge-action.action-update {
  background: #fef3c7;
  color: #92400e;
}

.badge-action.action-delete {
  background: #fee2e2;
  color: #991b1b;
}

.badge-action.action-read {
  background: #e0e7ff;
  color: #3730a3;
}

/* ---- badge ---- */
.badge {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 20px;
  font-size: 12px;
  font-weight: 600;
}

.badge.success {
  background: #d1fae5;
  color: #065f46;
}

.badge.error {
  background: #fee2e2;
  color: #991b1b;
}

/* ---- detail button ---- */
.btn-detail {
  width: 32px;
  height: 32px;
  border-radius: 10px;
  border: 1px solid #e2e8f0;
  background: white;
  color: #6b7d94;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: 0.15s;
  font-size: 13px;
}

.btn-detail:hover {
  background: #f0f5ff;
  border-color: #4f7cff;
  color: #4f7cff;
}

/* ---- pagination ---- */
.pagination {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 20px;
  border-top: 1px solid #edf2f9;
}

.page-info {
  font-size: 13px;
  color: #8b9ab0;
}

.page-controls {
  display: flex;
  align-items: center;
  gap: 8px;
}

.page-controls button {
  width: 32px;
  height: 32px;
  border-radius: 10px;
  border: 1px solid #e2e8f0;
  background: white;
  color: #3e4e62;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: 0.15s;
  font-size: 12px;
}

.page-controls button:hover:not(:disabled) {
  background: #f0f5ff;
  border-color: #4f7cff;
  color: #4f7cff;
}

.page-controls button:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.page-num {
  font-size: 13px;
  font-weight: 600;
  color: #1e293b;
  padding: 0 8px;
}

/* ---- skeleton ---- */
.skeleton {
  background: linear-gradient(90deg, #eef2f8 25%, #e0e7f0 50%, #eef2f8 75%);
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite;
  border-radius: 8px;
}

.sk-row {
  padding: 14px 20px;
  border-bottom: 1px solid #f1f5fa;
}

.sk-cell {
  height: 16px;
  width: 70%;
}

/* ---- empty ---- */
.empty-section {
  display: flex;
  justify-content: center;
  padding: 80px 0;
}

.empty-state {
  text-align: center;
  color: #8b9ab0;
}

.empty-icon {
  font-size: 48px;
  margin-bottom: 16px;
  display: block;
}

.empty-state p {
  font-size: 15px;
  font-weight: 500;
  color: #3e4e62;
  margin-bottom: 6px;
}

.empty-hint {
  font-size: 13px;
  color: #94a3b8;
}

/* ---- modal ---- */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(11, 26, 51, 0.4);
  backdrop-filter: blur(4px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
}

.modal-content {
  background: white;
  border-radius: 24px;
  width: 640px;
  max-height: 85vh;
  overflow-y: auto;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);
}

.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 20px 24px;
  border-bottom: 1px solid #edf2f9;
}

.modal-header h3 {
  font-weight: 600;
  font-size: 16px;
  color: #0b1a33;
  display: flex;
  align-items: center;
}

.modal-header-icon {
  margin-right: 10px;
  color: #4f7cff;
}

.modal-close {
  width: 36px;
  height: 36px;
  border-radius: 12px;
  border: 1px solid #e2e8f0;
  background: white;
  color: #6b7d94;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

.modal-close:hover {
  background: #fee2e2;
  border-color: #fecaca;
  color: #ef4444;
}

.modal-body {
  padding: 20px 24px;
}

/* ---- summary card ---- */
.summary-card {
  display: flex;
  gap: 16px;
  padding: 18px;
  background: #f8fafd;
  border-radius: 16px;
  border: 1px solid #edf2f9;
  margin-bottom: 20px;
  align-items: flex-start;
}

.summary-icon {
  font-size: 28px;
  flex-shrink: 0;
}

.summary-icon.success {
  color: #10b981;
}

.summary-icon.error {
  color: #ef4444;
}

.summary-text {
  flex: 1;
}

.summary-title {
  font-weight: 600;
  font-size: 15px;
  color: #1e293b;
  margin-bottom: 4px;
}

.summary-subtitle {
  font-size: 13px;
  color: #6b7d94;
  margin-bottom: 8px;
}

.summary-meta {
  font-size: 12px;
  color: #8b9ab0;
}

.summary-meta strong {
  color: #3e4e62;
}

/* ---- detail section ---- */
.detail-section {
  margin-bottom: 18px;
}

.detail-section h4 {
  font-size: 13px;
  font-weight: 600;
  color: #3e4e62;
  margin-bottom: 10px;
  display: flex;
  align-items: center;
  gap: 6px;
}

.error-section h4 {
  color: #dc2626;
}

.param-list {
  background: #f8fafd;
  border: 1px solid #edf2f9;
  border-radius: 12px;
  overflow: hidden;
}

.param-item {
  display: flex;
  align-items: center;
  padding: 10px 14px;
  border-bottom: 1px solid #f1f5fa;
  font-size: 13px;
}

.param-item:last-child {
  border-bottom: none;
}

.param-key {
  font-weight: 600;
  color: #3e4e62;
  min-width: 90px;
  flex-shrink: 0;
}

.param-eq {
  color: #cbd5e1;
  margin: 0 8px;
}

.param-value {
  color: #4f7cff;
  word-break: break-all;
}

.no-data {
  font-size: 13px;
  color: #8b9ab0;
  padding: 14px;
  background: #f8fafd;
  border-radius: 12px;
  border: 1px solid #edf2f9;
  text-align: center;
}

.error-msg {
  background: #fef2f2;
  border: 1px solid #fecaca;
  border-radius: 12px;
  padding: 14px;
  font-size: 13px;
  color: #991b1b;
  line-height: 1.5;
}

/* ---- raw toggle ---- */
.raw-toggle {
  margin-top: 20px;
  border: 1px solid #e2e8f0;
  border-radius: 14px;
  overflow: hidden;
}

.raw-toggle-btn {
  width: 100%;
  padding: 12px 16px;
  background: #fafbfd;
  border: none;
  font-size: 13px;
  font-weight: 500;
  color: #6b7d94;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 8px;
  transition: background 0.15s;
}

.raw-toggle-btn:hover {
  background: #f0f5ff;
  color: #4f7cff;
}

.raw-toggle.expanded .raw-toggle-btn {
  border-bottom: 1px solid #edf2f9;
  color: #4f7cff;
}

.raw-content {
  padding: 16px;
}

.raw-field {
  margin-bottom: 14px;
}

.raw-field:last-child {
  margin-bottom: 0;
}

.raw-label {
  display: block;
  font-size: 11px;
  font-weight: 600;
  color: #8b9ab0;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-bottom: 6px;
}

.raw-value {
  font-size: 13px;
  color: #1e293b;
}

.raw-code {
  display: block;
  font-family: 'Consolas', 'Monaco', monospace;
  font-size: 12px;
  padding: 10px 14px;
  background: #f1f5f9;
  border-radius: 8px;
  color: #475569;
  word-break: break-all;
}

.raw-pre {
  font-family: 'Consolas', 'Monaco', monospace;
  font-size: 12px;
  padding: 10px 14px;
  background: #f1f5f9;
  border-radius: 8px;
  color: #475569;
  white-space: pre-wrap;
  word-break: break-all;
  max-height: 180px;
  overflow-y: auto;
  line-height: 1.5;
  margin: 0;
}

/* ---- animations ---- */
@keyframes shimmer {
  0% { background-position: -200% 0; }
  100% { background-position: 200% 0; }
}

@keyframes fadeIn {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}

.modal-enter-active { animation: fadeIn 0.2s ease; }
.modal-leave-active { animation: fadeIn 0.15s ease reverse; }
</style>
