<!--
  组件名称：ProjectReport.vue
  组件描述：性能测试合并报告页面 - 聚合指标卡片 / 分位数表 / 状态码分布 / 各任务明细

  @author czj
  @date 2026-08-20
-->

<template>
  <div class="project-report-page">
    <!-- 加载中 -->
    <el-skeleton v-if="loading" :rows="8" animated />

    <template v-else-if="report">
      <!-- 顶部操作栏 -->
      <div class="report-header">
        <div class="header-left">
          <el-button @click="goBack" text>
            <el-icon><ArrowLeft /></el-icon>返回项目详情
          </el-button>
          <h2 class="page-title">合并报告</h2>
          <el-select
            v-if="hasHistory && historyReports.length > 0"
            :model-value="historyReportId"
            placeholder="选择历史报告"
            size="default"
            style="width: 260px"
            @change="onHistoryChange"
          >
            <el-option
              v-for="r in historyReports"
              :key="r.id"
              :label="historyLabel(r)"
              :value="r.id"
            />
          </el-select>
          <el-tag v-if="report.batchId" type="info" size="small">
            {{ report.batchId }}
          </el-tag>
        </div>
        <div class="header-right">
          <el-button type="primary" :loading="loading" @click="loadReport">
            <el-icon><Refresh /></el-icon>刷新
          </el-button>
          <el-button @click="exportReport">
            <el-icon><Download /></el-icon>导出 JSON
          </el-button>
        </div>
      </div>

      <!-- 执行摘要 -->
      <el-descriptions title="执行摘要 Summary" :column="3" border class="summary-block">
        <el-descriptions-item label="报告 ID">{{ report.id }}</el-descriptions-item>
        <el-descriptions-item label="批次 ID Batch ID">{{ report.batchId || '-' }}</el-descriptions-item>
        <el-descriptions-item label="生成时间 Created At">{{ formatTime(report.createdAt) }}</el-descriptions-item>
        <el-descriptions-item label="总任务数 Total Tasks">{{ report.taskSummaries.length }}</el-descriptions-item>
        <el-descriptions-item label="总请求数 Total Requests">{{ report.summary.totalRequests }}</el-descriptions-item>
        <el-descriptions-item label="实际总耗时 Actual Duration">{{ report.summary.durationActual != null ? report.summary.durationActual.toFixed(1) + 's' : '-' }}</el-descriptions-item>
      </el-descriptions>

      <!-- 第一行：请求统计 -->
      <el-row :gutter="12" class="metrics-row">
        <el-col :span="6">
          <el-card shadow="never" class="metric-card">
            <div class="metric-value">{{ report.summary.totalRequests }}</div>
            <div class="metric-label">Total Requests（总请求数）</div>
          </el-card>
        </el-col>
        <el-col :span="6">
          <el-card shadow="never" class="metric-card">
            <div class="metric-value">{{ report.summary.successCount }}</div>
            <div class="metric-label">Total Success（总成功数）</div>
          </el-card>
        </el-col>
        <el-col :span="6">
          <el-card shadow="never" class="metric-card">
            <div class="metric-value">{{ report.summary.failCount }}</div>
            <div class="metric-label">Total Failed（总失败数）</div>
          </el-card>
        </el-col>
        <el-col :span="6">
          <el-card shadow="never" class="metric-card">
            <div class="metric-value" :style="{ color: (report.summary.errorRate * 100) > 5 ? '#f56c6c' : '#67c23a' }">
              {{ (report.summary.errorRate * 100).toFixed(2) }}%
            </div>
            <div class="metric-label">Error Rate（整体错误率）</div>
          </el-card>
        </el-col>
      </el-row>

      <!-- 第二行：吞吐 + 响应时间 -->
      <el-row :gutter="12" class="metrics-row">
        <el-col :span="6">
          <el-card shadow="never" class="metric-card">
            <div class="metric-value">{{ report.summary.actualTps.toFixed(1) }}</div>
            <div class="metric-label">Overall TPS（整体吞吐量）</div>
          </el-card>
        </el-col>
        <el-col :span="6">
          <el-card shadow="never" class="metric-card">
            <div class="metric-value">{{ report.summary.peakTps.toFixed(1) }}</div>
            <div class="metric-label">Peak TPS（峰值吞吐量）</div>
          </el-card>
        </el-col>
        <el-col :span="6">
          <el-card shadow="never" class="metric-card">
            <div class="metric-value">{{ report.summary.avgResponseTime.toFixed(1) }}ms</div>
            <div class="metric-label">Avg Response Time（平均响应时间）</div>
          </el-card>
        </el-col>
        <el-col :span="6">
          <el-card shadow="never" class="metric-card">
            <div class="metric-value" :style="{ color: rtPerfColor(report.summary.p95ResponseTime) }">
              {{ report.summary.p95ResponseTime.toFixed(1) }}ms
            </div>
            <div class="metric-label">P95 Response Time（P95 响应时间）</div>
          </el-card>
        </el-col>
      </el-row>

      <!-- 第三行：稳定性 + 极值 -->
      <el-row :gutter="12" class="metrics-row">
        <el-col :span="6">
          <el-card shadow="never" class="metric-card">
            <div class="metric-value">{{ report.summary.stdDev.toFixed(1) }}ms</div>
            <div class="metric-label">Std Dev（响应时间标准差）</div>
          </el-card>
        </el-col>
        <el-col :span="6">
          <el-card shadow="never" class="metric-card">
            <div class="metric-value" :style="{ color: apdexColor(report.summary.apdex) }">
              {{ report.summary.apdex.toFixed(2) }}
            </div>
            <div class="metric-label">Apdex（用户满意度）</div>
          </el-card>
        </el-col>
        <el-col :span="6">
          <el-card shadow="never" class="metric-card">
            <div class="metric-value">{{ report.summary.minResponseTime.toFixed(1) }}ms</div>
            <div class="metric-label">Min Response Time（最小响应时间）</div>
          </el-card>
        </el-col>
        <el-col :span="6">
          <el-card shadow="never" class="metric-card">
            <div class="metric-value">{{ report.summary.maxResponseTime.toFixed(1) }}ms</div>
            <div class="metric-label">Max Response Time（最大响应时间）</div>
          </el-card>
        </el-col>
      </el-row>

      <!-- 第四行：带宽 -->
      <el-row :gutter="12" class="metrics-row">
        <el-col :span="6">
          <el-card shadow="never" class="metric-card">
            <div class="metric-value">{{ formatBytes(report.summary.totalSentBytes) }}</div>
            <div class="metric-label">Total Sent（总发送数据）</div>
          </el-card>
        </el-col>
        <el-col :span="6">
          <el-card shadow="never" class="metric-card">
            <div class="metric-value">{{ formatBytes(report.summary.totalReceivedBytes) }}</div>
            <div class="metric-label">Total Received（总接收数据）</div>
          </el-card>
        </el-col>
        <el-col :span="6">
          <el-card shadow="never" class="metric-card">
            <div class="metric-value">{{ report.summary.durationActual != null ? report.summary.durationActual.toFixed(1) + 's' : '-' }}</div>
            <div class="metric-label">Actual Duration（实际总耗时）</div>
          </el-card>
        </el-col>
        <el-col :span="6">
          <el-card shadow="never" class="metric-card">
            <div class="metric-value">{{ report.summary.actualTps.toFixed(1) }}</div>
            <div class="metric-label">Overall TPS（整体吞吐量）</div>
          </el-card>
        </el-col>
      </el-row>

      <!-- 响应时间分位数 -->
      <el-row :gutter="12">
        <el-col :span="12">
          <el-card shadow="never" class="chart-card">
            <template #header><span>响应时间分位数 Percentile Table</span></template>
            <el-table :data="quantileRows" size="small">
              <el-table-column prop="label" label="Percentile（分位数）" width="120" />
              <el-table-column prop="value" label="Value (ms)" />
            </el-table>
          </el-card>
        </el-col>
        <el-col :span="12">
          <el-card shadow="never" class="chart-card">
            <template #header><span>状态码分布 Status Code</span></template>
            <el-table :data="statusRows" size="small">
              <el-table-column prop="code" label="Status Code（状态码）" width="120" />
              <el-table-column prop="count" label="Count（数量）" />
              <el-table-column prop="percent" label="Percentage（占比）" />
            </el-table>
          </el-card>
        </el-col>
      </el-row>

      <!-- 各任务明细表 -->
      <el-card shadow="never" class="chart-card">
        <template #header><span>各任务明细 Task Details</span></template>
        <el-table :data="report.taskSummaries" size="small" stripe>
          <el-table-column prop="taskName" label="Task Name（任务名称）" min-width="160" show-overflow-tooltip />
          <el-table-column label="状态" width="100">
            <template #default="{ row }">
              <el-tag :type="statusType(row.status)" size="small">
                {{ statusLabel(row.status) }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column prop="totalRequests" label="Requests（请求数）" width="90" />
          <el-table-column prop="successCount" label="Success（成功）" width="80" />
          <el-table-column prop="failCount" label="Failed（失败）" width="80" />
          <el-table-column label="Error Rate（错误率）" width="90">
            <template #default="{ row }">
              <span :style="{ color: (row.errorRate * 100) > 5 ? '#f56c6c' : '#67c23a' }">
                {{ (row.errorRate * 100).toFixed(1) }}%
              </span>
            </template>
          </el-table-column>
          <el-table-column label="Avg Response Time（平均响应时间）" width="120">
            <template #default="{ row }">{{ row.avgResponseTime.toFixed(1) }}ms</template>
          </el-table-column>
          <el-table-column label="TPS（吞吐量）" width="90">
            <template #default="{ row }">{{ row.actualTps.toFixed(1) }}</template>
          </el-table-column>
          <el-table-column label="操作" width="100" fixed="right">
            <template #default="{ row }">
              <el-button link type="primary" size="small" @click="viewTaskReport(row.taskId)">
                查看报告
              </el-button>
            </template>
          </el-table-column>
        </el-table>
      </el-card>
    </template>

    <el-empty v-else description="暂无合并报告数据" />
  </div>
</template>

<script setup lang="ts">
/**
 * 性能测试合并报告页面
 *
 * 功能：展示项目维度的聚合指标卡片 + 各任务明细列表
 * 路由：/performance/project-report?projectId=xxx&reportId=xxx
 */

import { ref, onMounted, computed, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { ArrowLeft, Refresh, Download } from '@element-plus/icons-vue'
import { getProjectReport, getBatchReport, getProject, getProjectReports } from '@/api/performance'
import type { ProjectReportResponse, PerformanceProject, ProjectReportListResponse } from '@/types/performance'

const route = useRoute()
const router = useRouter()

const projectId = computed(() => route.query.projectId as string)
const reportId = computed(() => route.query.reportId as string)

const loading = ref(false)
const project = ref<PerformanceProject | null>(null)
const report = ref<ProjectReportResponse | null>(null)

// 历史报告列表（同一项目下的历史合并报告切换）
const historyReports = ref<ProjectReportListResponse['items']>([])
const hasHistory = computed(() => historyReports.value.length > 0)
const historyReportId = computed(() => report.value?.id || '')

/** 返回项目详情页 */
function goBack() {
  if (projectId.value) {
    router.push(`/performance/projects/${projectId.value}`)
  } else {
    router.push('/performance/projects')
  }
}

/** 加载合并报告 */
async function loadReport() {
  loading.value = true
  try {
    let res: ProjectReportResponse
    if (reportId.value) {
      // 按 reportId 获取
      res = await getBatchReport(reportId.value)
    } else if (projectId.value) {
      // 获取项目最新合并报告
      res = await getProjectReport(projectId.value)
    } else {
      ElMessage.error('缺少 projectId 或 reportId 参数')
      return
    }
    report.value = res
    // 加载后自动拉取历史报告列表
    loadHistoryReports()
  } catch (e: any) {
    ElMessage.error('加载合并报告失败: ' + (e.response?.data?.detail || e.message || ''))
  } finally {
    loading.value = false
  }
}

/** 加载历史报告列表（供切换用） */
async function loadHistoryReports() {
  if (!projectId.value) return
  try {
    const res = await getProjectReports(projectId.value, { page: 1, pageSize: 50 })
    historyReports.value = res.items
  } catch {
    // 历史列表非必需
  }
}

/** 历史报告切换 */
function onHistoryChange(val: string) {
  router.push(`/performance/project-report?reportId=${val}&projectId=${projectId.value}`)
}

/** 历史报告下拉标签 */
function historyLabel(r: ProjectReportResponse) {
  const time = r.createdAt?.replace('T', ' ').substring(0, 16) || ''
  return `${time} · ${r.summary.totalRequests} 请求 · ${r.taskSummaries.length} 任务`
}

/** 加载项目信息 */
async function loadProject() {
  if (!projectId.value) return
  try {
    project.value = await getProject(projectId.value)
  } catch {
    // 项目信息非必需
  }
}

/** 跳转到单任务报告 */
function viewTaskReport(taskId: string) {
  router.push(`/performance/report?taskId=${taskId}`)
}

/** 导出 JSON */
function exportReport() {
  if (!report.value) return
  const blob = new Blob([JSON.stringify(report.value, null, 2)], { type: 'application/json' })
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  a.download = `report_${report.value.id || 'merged'}.json`
  a.click()
  URL.revokeObjectURL(url)
  ElMessage.success('报告已导出')
}

/** 分位数表行 */
const quantileRows = computed(() => {
  if (!report.value) return []
  const s = report.value.summary
  return [
    { label: 'P50', value: s.p50ResponseTime.toFixed(1) },
    { label: 'P90', value: s.p90ResponseTime.toFixed(1) },
    { label: 'P95', value: s.p95ResponseTime.toFixed(1) },
    { label: 'P99', value: s.p99ResponseTime.toFixed(1) },
  ]
})

/** 状态码分布表行 */
const statusRows = computed(() => {
  if (!report.value) return []
  const s = report.value.summary
  const total = s.status2xx + s.status3xx + s.status4xx + s.status5xx || 1
  return [
    { code: '2xx', count: s.status2xx, percent: (s.status2xx / total * 100).toFixed(1) + '%' },
    { code: '3xx', count: s.status3xx, percent: (s.status3xx / total * 100).toFixed(1) + '%' },
    { code: '4xx', count: s.status4xx, percent: (s.status4xx / total * 100).toFixed(1) + '%' },
    { code: '5xx', count: s.status5xx, percent: (s.status5xx / total * 100).toFixed(1) + '%' },
  ]
})

/** 格式化时间 */
function formatTime(t: string | null) {
  return t?.replace('T', ' ').substring(0, 19) || ''
}

/** 格式化字节数 */
function formatBytes(bytes: number) {
  if (!bytes) return '0 B'
  const units = ['B', 'KB', 'MB', 'GB']
  let i = 0
  let size = bytes
  while (size >= 1024 && i < units.length - 1) {
    size /= 1024
    i++
  }
  return size.toFixed(1) + ' ' + units[i]
}

/** 响应时间性能颜色 */
function rtPerfColor(ms: number) {
  if (ms < 200) return '#67c23a'
  if (ms < 500) return '#e6a23c'
  return '#f56c6c'
}

/** Apdex 颜色 */
function apdexColor(val: number) {
  if (val >= 0.94) return '#67c23a'
  if (val >= 0.85) return '#e6a23c'
  return '#f56c6c'
}

/** 状态标签类型 */
function statusType(status: string) {
  const map: Record<string, string> = { completed: 'success', running: 'primary', pending: 'info', failed: 'danger', cancelled: 'warning' }
  return map[status] || 'info'
}

/** 状态标签文字 */
function statusLabel(status: string) {
  const map: Record<string, string> = { completed: '已完成', running: '运行中', pending: '等待中', failed: '失败', cancelled: '已取消' }
  return map[status] || status
}

onMounted(() => {
  loadProject()
  loadReport()
})

// 路由参数变化时重新加载（历史报告切换）
watch(reportId, (newVal, oldVal) => {
  if (newVal && newVal !== oldVal) {
    loadReport()
  }
})
</script>

<style scoped>
.project-report-page {
  padding: 0;
}

.report-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;

  .header-left {
    display: flex;
    align-items: center;
    gap: 12px;

    .page-title {
      font-size: 20px;
      font-weight: 600;
      color: #303133;
      margin: 0;
    }
  }

  .header-right {
    display: flex;
    gap: 12px;
  }
}

.summary-block {
  margin-bottom: 20px;
}

.metrics-row {
  margin-bottom: 12px;
}

.metric-card {
  text-align: center;
  padding: 8px 0;
  background: #fafafa;

  .metric-value {
    font-size: 24px;
    font-weight: 700;
    color: #303133;
    line-height: 1.4;
  }

  .metric-label {
    font-size: 13px;
    color: #909399;
    margin-top: 4px;
  }
}

.chart-card {
  margin-bottom: 16px;
}
</style>