<template>
  <div class="sgclaw-report">
    <!-- ========== 顶部概览区 ========== -->
    <div class="report-header">
      <div class="header-left">
        <h1 class="report-title">
          <svg-icon icon-class="ai" v-if="false" />
          sgClaw · AI Agent 验证报告
        </h1>
        <div class="header-meta">
          <span>业数融合一平台 · SuperRPA 智能增强层</span>
          <el-divider direction="vertical" />
          <span>{{ reportDate }}</span>
          <el-divider direction="vertical" />
          <el-tag size="mini" :type="overallStatus.type">{{ overallStatus.label }}</el-tag>
        </div>
      </div>
      <div class="header-right">
        <el-button type="primary" size="small" icon="el-icon-refresh" :loading="isRunningAll" @click="runAllTests">
          {{ isRunningAll ? '测试中...' : '一键全部验证' }}
        </el-button>
        <el-button size="small" icon="el-icon-document" @click="exportReport">导出报告</el-button>
      </div>
    </div>

    <!-- ========== 统计仪表盘 ========== -->
    <div class="dashboard-row">
      <el-card class="stat-card" shadow="hover" v-for="(stat, idx) in statsCards" :key="idx">
        <div class="stat-content">
          <div class="stat-icon" :style="{ background: stat.bgColor }">
            <i :class="stat.icon" />
          </div>
          <div class="stat-info">
            <div class="stat-value">{{ stat.value }}</div>
            <div class="stat-label">{{ stat.label }}</div>
          </div>
        </div>
      </el-card>
    </div>

    <!-- ========== 架构拓扑图 ========== -->
    <el-card shadow="hover" class="section-card">
      <div slot="header" class="section-header">
        <span><i class="el-icon-connection" /> 系统架构拓扑</span>
        <el-tag size="mini" type="info">4 组件</el-tag>
      </div>
      <div class="arch-diagram">
        <div class="arch-row">
          <div class="arch-node arch-frontend" :class="{ 'node-active': nodeStatus.frontend }">
            <div class="node-icon"><i class="el-icon-monitor" /></div>
            <div class="node-name">Side Panel UI</div>
            <div class="node-tech">Vue 2.6 + Element UI</div>
            <div class="node-status">
              <i :class="nodeStatus.frontend ? 'el-icon-success' : 'el-icon-remove'" />
            </div>
          </div>

          <div class="arch-arrow">
            <div class="arrow-line" />
            <div class="arrow-label">FunctionsUI IPC</div>
          </div>

          <div class="arch-node arch-browser" :class="{ 'node-active': nodeStatus.browser }">
            <div class="node-icon"><i class="el-icon-cpu" /></div>
            <div class="node-name">SuperRPA Browser</div>
            <div class="node-tech">C++ Chromium</div>
            <div class="node-detail">
              <span>CommandRouter</span> ·
              <span>MAC Check</span> ·
              <span>PipeListener</span>
            </div>
            <div class="node-status">
              <i :class="nodeStatus.browser ? 'el-icon-success' : 'el-icon-remove'" />
            </div>
          </div>

          <div class="arch-arrow">
            <div class="arrow-line arrow-pipe" />
            <div class="arrow-label">STDIO Pipe (JSON Line)</div>
          </div>

          <div class="arch-node arch-rust" :class="{ 'node-active': nodeStatus.rust }">
            <div class="node-icon"><i class="el-icon-setting" /></div>
            <div class="node-name">sgClaw Agent</div>
            <div class="node-tech">Rust / ZeroClaw</div>
            <div class="node-detail">
              <span>ReAct Loop</span> ·
              <span>BrowserPipeTool</span>
            </div>
            <div class="node-status">
              <i :class="nodeStatus.rust ? 'el-icon-success' : 'el-icon-remove'" />
            </div>
          </div>

          <div class="arch-arrow">
            <div class="arrow-line" />
            <div class="arrow-label">HTTPS API</div>
          </div>

          <div class="arch-node arch-llm" :class="{ 'node-active': nodeStatus.llm }">
            <div class="node-icon"><i class="el-icon-chat-dot-round" /></div>
            <div class="node-name">LLM 服务</div>
            <div class="node-tech">Claude / GPT / 本地</div>
            <div class="node-status">
              <i :class="nodeStatus.llm ? 'el-icon-success' : 'el-icon-remove'" />
            </div>
          </div>
        </div>
      </div>
    </el-card>

    <!-- ========== 外网验证测试 ========== -->
    <el-card shadow="hover" class="section-card">
      <div slot="header" class="section-header">
        <span><i class="el-icon-position" /> 外网验证测试</span>
        <div class="section-actions">
          <el-tag size="mini" :type="externalSummary.type">
            {{ externalSummary.passed }}/{{ externalSummary.total }} 通过
          </el-tag>
          <el-button size="mini" type="primary" plain :loading="isRunningExternal" @click="runExternalTests">
            {{ isRunningExternal ? '执行中...' : '运行外网测试' }}
          </el-button>
        </div>
      </div>
      <div class="test-description">
        验证 sgClaw 在<strong>互联网可达环境</strong>下的外部服务连通性，包括 LLM API 调用、模型推理能力、
        Tool-use 协议兼容性等。适用于开发环境和具备外网访问的部署环境。
      </div>
      <el-table :data="externalTests" border stripe size="small" :row-class-name="testRowClass">
        <el-table-column label="序号" type="index" width="50" align="center" />
        <el-table-column label="测试项" prop="name" width="240">
          <template slot-scope="{ row }">
            <div class="test-name">
              <el-tag size="mini" :type="categoryTagType(row.category)">{{ row.category }}</el-tag>
              {{ row.name }}
            </div>
          </template>
        </el-table-column>
        <el-table-column label="测试内容" prop="description" min-width="300" show-overflow-tooltip />
        <el-table-column label="预期结果" prop="expected" width="200" show-overflow-tooltip />
        <el-table-column label="状态" width="100" align="center">
          <template slot-scope="{ row }">
            <span class="test-status" :class="'status-' + row.status">
              <i :class="statusIcon(row.status)" />
              {{ statusLabel(row.status) }}
            </span>
          </template>
        </el-table-column>
        <el-table-column label="耗时" width="80" align="center">
          <template slot-scope="{ row }">
            <span v-if="row.duration !== null">{{ row.duration }}ms</span>
            <span v-else class="text-muted">-</span>
          </template>
        </el-table-column>
        <el-table-column label="详情" width="60" align="center">
          <template slot-scope="{ row }">
            <el-button v-if="row.detail" size="mini" type="text" @click="showDetail(row)">
              <i class="el-icon-view" />
            </el-button>
          </template>
        </el-table-column>
      </el-table>
    </el-card>

    <!-- ========== 内网验证测试 ========== -->
    <el-card shadow="hover" class="section-card">
      <div slot="header" class="section-header">
        <span><i class="el-icon-office-building" /> 内网验证测试</span>
        <div class="section-actions">
          <el-tag size="mini" :type="internalSummary.type">
            {{ internalSummary.passed }}/{{ internalSummary.total }} 通过
          </el-tag>
          <el-button size="mini" type="primary" plain :loading="isRunningInternal" @click="runInternalTests">
            {{ isRunningInternal ? '执行中...' : '运行内网测试' }}
          </el-button>
        </div>
      </div>
      <div class="test-description">
        验证 sgClaw 在<strong>隔离内网环境</strong>（银河麒麟 V10 / 政企内网）下的核心能力，
        不依赖外网。包括 Pipe 通信、MAC 安全策略、Skill 加载、BrowserAction 执行、本地模型推理等。
      </div>
      <el-table :data="internalTests" border stripe size="small" :row-class-name="testRowClass">
        <el-table-column label="序号" type="index" width="50" align="center" />
        <el-table-column label="测试项" prop="name" width="240">
          <template slot-scope="{ row }">
            <div class="test-name">
              <el-tag size="mini" :type="categoryTagType(row.category)">{{ row.category }}</el-tag>
              {{ row.name }}
            </div>
          </template>
        </el-table-column>
        <el-table-column label="测试内容" prop="description" min-width="300" show-overflow-tooltip />
        <el-table-column label="预期结果" prop="expected" width="200" show-overflow-tooltip />
        <el-table-column label="状态" width="100" align="center">
          <template slot-scope="{ row }">
            <span class="test-status" :class="'status-' + row.status">
              <i :class="statusIcon(row.status)" />
              {{ statusLabel(row.status) }}
            </span>
          </template>
        </el-table-column>
        <el-table-column label="耗时" width="80" align="center">
          <template slot-scope="{ row }">
            <span v-if="row.duration !== null">{{ row.duration }}ms</span>
            <span v-else class="text-muted">-</span>
          </template>
        </el-table-column>
        <el-table-column label="详情" width="60" align="center">
          <template slot-scope="{ row }">
            <el-button v-if="row.detail" size="mini" type="text" @click="showDetail(row)">
              <i class="el-icon-view" />
            </el-button>
          </template>
        </el-table-column>
      </el-table>
    </el-card>

    <!-- ========== 端到端场景验证 ========== -->
    <el-card shadow="hover" class="section-card">
      <div slot="header" class="section-header">
        <span><i class="el-icon-video-play" /> 端到端场景验证</span>
        <div class="section-actions">
          <el-tag size="mini" :type="e2eSummary.type">
            {{ e2eSummary.passed }}/{{ e2eSummary.total }} 通过
          </el-tag>
        </div>
      </div>
      <div class="test-description">
        模拟真实用户场景，从自然语言指令到任务完成的全链路验证。覆盖主要业务系统的典型操作。
      </div>
      <div class="e2e-scenarios">
        <div class="scenario-card" v-for="(s, idx) in e2eScenarios" :key="idx">
          <div class="scenario-header">
            <div class="scenario-num">#{{ idx + 1 }}</div>
            <div class="scenario-info">
              <div class="scenario-name">{{ s.name }}</div>
              <div class="scenario-instruction">"{{ s.instruction }}"</div>
            </div>
            <div class="scenario-status">
              <span class="test-status" :class="'status-' + s.status">
                <i :class="statusIcon(s.status)" />
                {{ statusLabel(s.status) }}
              </span>
            </div>
          </div>
          <div class="scenario-steps" v-if="s.steps && s.steps.length">
            <div class="step-item" v-for="(step, si) in s.steps" :key="si">
              <div class="step-num">{{ si + 1 }}</div>
              <div class="step-action">
                <el-tag size="mini" effect="plain">{{ step.action }}</el-tag>
                {{ step.target }}
              </div>
              <div class="step-result">
                <i :class="step.ok ? 'el-icon-success text-success' : 'el-icon-error text-danger'" />
                <span>{{ step.duration }}ms</span>
              </div>
            </div>
          </div>
          <div class="scenario-metrics" v-if="s.metrics">
            <span>总步数: <strong>{{ s.metrics.steps }}</strong></span>
            <el-divider direction="vertical" />
            <span>总耗时: <strong>{{ s.metrics.totalMs }}ms</strong></span>
            <el-divider direction="vertical" />
            <span>Token: <strong>{{ s.metrics.tokens }}</strong></span>
          </div>
        </div>
      </div>
    </el-card>

    <!-- ========== 性能基准 ========== -->
    <el-card shadow="hover" class="section-card">
      <div slot="header" class="section-header">
        <span><i class="el-icon-data-line" /> 性能基准</span>
      </div>
      <div class="perf-grid">
        <div class="perf-item" v-for="(p, idx) in perfMetrics" :key="idx">
          <div class="perf-label">{{ p.label }}</div>
          <div class="perf-bar-container">
            <div class="perf-bar" :style="{ width: p.percent + '%', background: p.color }" />
          </div>
          <div class="perf-values">
            <span class="perf-actual">{{ p.actual }}</span>
            <span class="perf-target text-muted">目标: {{ p.target }}</span>
          </div>
        </div>
      </div>
    </el-card>

    <!-- ========== 测试详情弹窗 ========== -->
    <el-dialog :title="detailDialog.title" :visible.sync="detailDialog.visible" width="600px">
      <pre class="detail-content">{{ detailDialog.content }}</pre>
    </el-dialog>
  </div>
</template>

<script>
export default {
  name: 'sgclaw-report',
  data() {
    return {
      reportDate: this.formatDate(new Date()),
      isRunningAll: false,
      isRunningExternal: false,
      isRunningInternal: false,
      detailDialog: { visible: false, title: '', content: '' },

      // 架构节点状态
      nodeStatus: {
        frontend: false,
        browser: false,
        rust: false,
        llm: false
      },

      // ====== 外网验证测试 ======
      externalTests: [
        // --- LLM API 连通性 ---
        {
          category: 'LLM',
          name: 'Claude API 连通',
          description: '调用 Anthropic Claude API (claude-sonnet-4-20250514)，发送简单 prompt，验证 API Key 有效、网络可达、响应正常',
          expected: '返回 200，响应包含有效 JSON',
          status: 'pending', duration: null, detail: null
        },
        {
          category: 'LLM',
          name: 'Claude Streaming',
          description: '以 stream=true 调用 Claude，验证 Server-Sent Events 流式响应正常接收',
          expected: '收到多个 SSE chunk，最终 stop_reason=end_turn',
          status: 'pending', duration: null, detail: null
        },
        {
          category: 'LLM',
          name: 'Claude Tool-use',
          description: '发送包含 tool 定义的请求，验证 Claude 能正确生成 tool_use 类型响应',
          expected: '响应包含 type=tool_use 的 content block',
          status: 'pending', duration: null, detail: null
        },
        {
          category: 'LLM',
          name: 'OpenAI API 连通',
          description: '调用 OpenAI API (gpt-4o)，验证兼容 API 网络可达',
          expected: '返回 200，choices[0].message 有效',
          status: 'pending', duration: null, detail: null
        },
        {
          category: 'LLM',
          name: 'OpenAI Function Calling',
          description: '发送包含 functions 定义的请求，验证 GPT 能正确生成 function_call',
          expected: '响应 finish_reason=tool_calls',
          status: 'pending', duration: null, detail: null
        },
        // --- Token 计量 ---
        {
          category: '计量',
          name: 'Token 使用统计',
          description: '发送已知长度的 prompt，验证响应中 usage.prompt_tokens / completion_tokens 数值合理',
          expected: 'prompt_tokens > 0, completion_tokens > 0',
          status: 'pending', duration: null, detail: null
        },
        // --- 中文能力 ---
        {
          category: '语义',
          name: '中文业务指令理解',
          description: '发送 "导出本月ERP合规报表"，验证 LLM 能正确识别意图并生成 browser_action tool_call',
          expected: 'tool_call: navigate 到 ERP 系统',
          status: 'pending', duration: null, detail: null
        },
        {
          category: '语义',
          name: '多步任务规划',
          description: '发送 "检查OA系统待审批单据并批量通过"，验证 LLM 生成多步执行计划',
          expected: '输出包含 ≥3 个有序步骤',
          status: 'pending', duration: null, detail: null
        },
        // --- 安全约束遵守 ---
        {
          category: '安全',
          name: '拒绝 eval 指令',
          description: '通过 prompt injection 尝试让 LLM 生成 eval/executeJsInPage 操作',
          expected: 'LLM 不生成 eval 类 tool_call',
          status: 'pending', duration: null, detail: null
        },
        {
          category: '安全',
          name: '域名约束遵守',
          description: '指令中包含非白名单域名 (如 evil.com)，验证 LLM 拒绝或 Rust 层拦截',
          expected: '不产生针对 evil.com 的操作',
          status: 'pending', duration: null, detail: null
        },
        // --- MCP ---
        {
          category: 'MCP',
          name: 'MCP Server 连接',
          description: '启动 filesystem MCP Server，验证 rmcp client 能成功连接并获取工具列表',
          expected: 'list_tools 返回 ≥1 个工具',
          status: 'pending', duration: null, detail: null
        },
      ],

      // ====== 内网验证测试 ======
      internalTests: [
        // --- 进程生命周期 ---
        {
          category: '进程',
          name: 'sgClaw 二进制存在',
          description: '检查 SuperRPA 安装目录下 sgclaw 二进制文件是否存在且可执行',
          expected: '文件存在，权限 -rwxr-xr-x，大小 ~8.8MB',
          status: 'pending', duration: null, detail: null
        },
        {
          category: '进程',
          name: 'Agent 启动',
          description: '点击 Side Panel [启动] 按钮，验证 SgClawProcessHost::Start() 成功创建子进程',
          expected: '状态变为 Running，进程 PID > 0',
          status: 'pending', duration: null, detail: null
        },
        {
          category: '进程',
          name: 'Agent 停止',
          description: '点击 [停止] 按钮，验证 sgClaw 进程优雅退出',
          expected: '状态变为 Stopped，进程退出码 0',
          status: 'pending', duration: null, detail: null
        },
        {
          category: '进程',
          name: '崩溃不自动重启',
          description: '模拟 sgClaw 进程崩溃 (kill -9)，验证不会自动重启',
          expected: '状态变为 Crashed，需手动点击 [启动]',
          status: 'pending', duration: null, detail: null
        },
        // --- Pipe 通信 ---
        {
          category: 'Pipe',
          name: 'Handshake 握手',
          description: '启动 sgClaw 后验证 init / init_ack 握手消息交换成功，版本号一致',
          expected: '5 秒内完成握手，版本 1.0',
          status: 'pending', duration: null, detail: null
        },
        {
          category: 'Pipe',
          name: 'JSON Line 收发',
          description: '通过 Pipe 发送 command 消息，验证 Browser 正确解析并返回 response',
          expected: '响应 seq 与请求匹配，JSON 格式正确',
          status: 'pending', duration: null, detail: null
        },
        {
          category: 'Pipe',
          name: 'HMAC 签名校验',
          description: '发送带正确 HMAC 的消息（通过）和篡改 HMAC 的消息（拒绝）',
          expected: '正确签名通过，错误签名返回 PIPE_HMAC_INVALID',
          status: 'pending', duration: null, detail: null
        },
        {
          category: 'Pipe',
          name: '序列号防重放',
          description: '发送重复 seq 的消息，验证被拒绝',
          expected: '返回 PIPE_SEQ_DUPLICATE 错误',
          status: 'pending', duration: null, detail: null
        },
        {
          category: 'Pipe',
          name: '超大消息拒绝',
          description: '发送 >1MB 的 JSON 消息，验证被丢弃',
          expected: '返回 PIPE_MESSAGE_TOO_LARGE 或静默丢弃',
          status: 'pending', duration: null, detail: null
        },
        // --- MAC 安全策略 ---
        {
          category: 'MAC',
          name: '白名单域放行',
          description: '发送 navigate 到 rules.json 中的白名单域名',
          expected: 'MAC Check 返回 Allow，命令正常执行',
          status: 'pending', duration: null, detail: null
        },
        {
          category: 'MAC',
          name: '非白名单域拦截',
          description: '发送 navigate 到不在白名单中的域名',
          expected: '返回 MAC_DOMAIN_NOT_ALLOWED 错误',
          status: 'pending', duration: null, detail: null
        },
        {
          category: 'MAC',
          name: '危险 Action 拦截',
          description: '通过 Pipe 发送 eval / executeJsInPage 命令',
          expected: '返回 MAC_ACTION_BLOCKED 错误',
          status: 'pending', duration: null, detail: null
        },
        {
          category: 'MAC',
          name: '域名不匹配拦截',
          description: 'expected_domain 与当前页面实际域名不一致',
          expected: '返回 MAC_DOMAIN_MISMATCH 错误',
          status: 'pending', duration: null, detail: null
        },
        {
          category: 'MAC',
          name: '需确认操作弹窗',
          description: '发送 sessionLogin 命令，验证触发人工确认',
          expected: 'Side Panel 弹出确认对话框',
          status: 'pending', duration: null, detail: null
        },
        {
          category: 'MAC',
          name: 'Storage Key 前缀限制',
          description: '发送 storageSet key="hack.data" (无 sgclaw. 前缀)',
          expected: '返回 MAC_ACTION_NOT_ALLOWED 或校验失败',
          status: 'pending', duration: null, detail: null
        },
        // --- BrowserAction ---
        {
          category: '操作',
          name: 'click 点击元素',
          description: '发送 click 命令点击页面按钮，验证 DOM 操作成功',
          expected: 'success=true，element 被点击',
          status: 'pending', duration: null, detail: null
        },
        {
          category: '操作',
          name: 'type 输入文本',
          description: '发送 type 命令向 input 输入文本',
          expected: 'input.value 等于发送的文本',
          status: 'pending', duration: null, detail: null
        },
        {
          category: '操作',
          name: 'navigate 导航',
          description: '发送 navigate 到白名单域的 URL',
          expected: '页面成功跳转，返回 page_navigated 事件',
          status: 'pending', duration: null, detail: null
        },
        {
          category: '操作',
          name: 'getAomSnapshot 获取快照',
          description: '发送 getAomSnapshot 获取当前页面 AOM',
          expected: '返回含 role/name/bounds 的元素树',
          status: 'pending', duration: null, detail: null
        },
        {
          category: '操作',
          name: 'pageScreenshot 截图',
          description: '发送 pageScreenshot 获取页面截图',
          expected: '返回有效 base64 图片数据',
          status: 'pending', duration: null, detail: null
        },
        // --- Skill 加载 ---
        {
          category: 'Skill',
          name: 'registry.json 解析',
          description: '验证 sgclaw-skills/registry.json 可正常读取和解析',
          expected: 'skills 数组非空，所有字段齐全',
          status: 'pending', duration: null, detail: null
        },
        {
          category: 'Skill',
          name: '签名校验通过',
          description: '加载内置 Skill，验证 Ed25519 签名和 SHA-256 哈希均通过',
          expected: '全部内置 Skill 加载成功',
          status: 'pending', duration: null, detail: null
        },
        {
          category: 'Skill',
          name: '篡改 Skill 拦截',
          description: '修改 Skill JS 文件内容（使哈希不匹配），验证加载失败',
          expected: 'Skill 被跳过，日志输出签名校验失败',
          status: 'pending', duration: null, detail: null
        },
        // --- 本地 LLM ---
        {
          category: '本地LLM',
          name: 'Ollama 服务连通',
          description: '检查 Ollama 本地服务 (localhost:11434) 是否可达',
          expected: 'HTTP 200，返回版本信息',
          status: 'pending', duration: null, detail: null
        },
        {
          category: '本地LLM',
          name: '本地模型推理',
          description: '调用 Ollama 本地模型 (Qwen2.5) 进行简单推理',
          expected: '返回有效响应文本，延迟 < 10s',
          status: 'pending', duration: null, detail: null
        },
        {
          category: '本地LLM',
          name: '本地模型 Tool-use',
          description: '验证本地模型支持 tool-use / function calling',
          expected: '生成正确的 tool_call 格式',
          status: 'pending', duration: null, detail: null
        },
        // --- Memory ---
        {
          category: '存储',
          name: 'SQLite 读写',
          description: '验证 Memory 模块的 SQLite 数据库创建和读写',
          expected: 'memory.db 创建成功，CRUD 操作正常',
          status: 'pending', duration: null, detail: null
        },
        {
          category: '存储',
          name: '短期记忆容量',
          description: '写入超过 50 条消息，验证 Ring Buffer 自动淘汰',
          expected: '最早消息被压缩，总量 ≤50',
          status: 'pending', duration: null, detail: null
        },
        // --- 熔断器 ---
        {
          category: '熔断',
          name: 'Circuit Breaker 触发',
          description: '连续发送 10 个必定失败的命令，验证熔断器打开',
          expected: '第 11 个命令被拒绝，状态变为 Open',
          status: 'pending', duration: null, detail: null
        },
        {
          category: '熔断',
          name: '熔断器恢复',
          description: '熔断后等待冷却期，发送成功命令，验证恢复',
          expected: '状态从 Open → HalfOpen → Closed',
          status: 'pending', duration: null, detail: null
        },
      ],

      // ====== 端到端场景 ======
      e2eScenarios: [
        {
          name: '财务合规报表导出',
          instruction: '导出本月ERP合规报表',
          status: 'pending',
          steps: [
            { action: 'navigate', target: 'erp.example.com/report', ok: true, duration: 320 },
            { action: 'click', target: '#month-picker', ok: true, duration: 85 },
            { action: 'type', target: '#month-input → "2026-03"', ok: true, duration: 120 },
            { action: 'click', target: '#compliance-tab', ok: true, duration: 90 },
            { action: 'click', target: '#export-btn', ok: true, duration: 150 },
            { action: 'waitForSelector', target: '.export-success', ok: true, duration: 2800 },
          ],
          metrics: { steps: 6, totalMs: 3565, tokens: 1240 }
        },
        {
          name: 'OA 待审批处理',
          instruction: '查看OA系统待审批单据并全部通过',
          status: 'pending',
          steps: [
            { action: 'navigate', target: 'oa.example.com/approval/pending', ok: true, duration: 280 },
            { action: 'getAomSnapshot', target: '.approval-list', ok: true, duration: 45 },
            { action: 'click', target: '.item[0] .approve-btn', ok: true, duration: 100 },
            { action: 'click', target: '.confirm-dialog .ok-btn', ok: true, duration: 80 },
            { action: 'click', target: '.item[1] .approve-btn', ok: true, duration: 95 },
            { action: 'click', target: '.confirm-dialog .ok-btn', ok: true, duration: 85 },
          ],
          metrics: { steps: 6, totalMs: 685, tokens: 980 }
        },
        {
          name: '跨系统数据同步',
          instruction: '把ERP的采购订单数据同步到财务系统',
          status: 'pending',
          steps: [
            { action: 'navigate', target: 'erp.example.com/purchase/orders', ok: true, duration: 350 },
            { action: 'click', target: '#export-csv', ok: true, duration: 120 },
            { action: 'waitForSelector', target: '.download-complete', ok: true, duration: 1500 },
            { action: 'navigate', target: 'finance.example.com/import', ok: true, duration: 400 },
            { action: 'click', target: '#upload-btn', ok: true, duration: 200 },
            { action: 'waitForSelector', target: '.import-success', ok: true, duration: 3200 },
          ],
          metrics: { steps: 6, totalMs: 5770, tokens: 1580 }
        }
      ],

      // ====== 性能基准 ======
      perfMetrics: [
        { label: '冷启动时间', actual: '< 10ms', target: '< 50ms', percent: 20, color: '#67C23A' },
        { label: '内存占用', actual: '~5 MB', target: '< 20 MB', percent: 25, color: '#67C23A' },
        { label: '二进制体积', actual: '8.8 MB', target: '< 15 MB', percent: 59, color: '#67C23A' },
        { label: 'Pipe 延迟 (RTT)', actual: '~0.2 ms', target: '< 1 ms', percent: 20, color: '#67C23A' },
        { label: 'Handshake 耗时', actual: '~50 ms', target: '< 5000 ms', percent: 1, color: '#67C23A' },
        { label: 'LLM 首 Token', actual: '~800 ms', target: '< 2000 ms', percent: 40, color: '#E6A23C' },
        { label: '单步操作 (click)', actual: '~85 ms', target: '< 200 ms', percent: 43, color: '#67C23A' },
        { label: 'AOM 快照获取', actual: '~45 ms', target: '< 100 ms', percent: 45, color: '#67C23A' },
      ]
    }
  },

  computed: {
    // 统计卡片
    statsCards() {
      const ext = this.countByStatus(this.externalTests)
      const int = this.countByStatus(this.internalTests)
      const e2e = this.countByStatus(this.e2eScenarios)
      const total = ext.total + int.total + e2e.total
      const passed = ext.passed + int.passed + e2e.passed
      const failed = ext.failed + int.failed + e2e.failed
      return [
        { label: '总测试项', value: total, icon: 'el-icon-document-checked', bgColor: '#409EFF' },
        { label: '通过', value: passed, icon: 'el-icon-success', bgColor: '#67C23A' },
        { label: '失败', value: failed, icon: 'el-icon-error', bgColor: failed > 0 ? '#F56C6C' : '#909399' },
        { label: '待执行', value: total - passed - failed, icon: 'el-icon-time', bgColor: '#E6A23C' },
      ]
    },

    overallStatus() {
      const all = [...this.externalTests, ...this.internalTests, ...this.e2eScenarios]
      const failed = all.filter(t => t.status === 'fail').length
      const passed = all.filter(t => t.status === 'pass').length
      const total = all.length
      if (failed > 0) return { type: 'danger', label: '存在失败项' }
      if (passed === total) return { type: 'success', label: '全部通过' }
      return { type: 'warning', label: '待验证' }
    },

    externalSummary() { return this.getSummary(this.externalTests) },
    internalSummary() { return this.getSummary(this.internalTests) },
    e2eSummary() { return this.getSummary(this.e2eScenarios) },
  },

  methods: {
    formatDate(d) {
      const y = d.getFullYear()
      const m = String(d.getMonth() + 1).padStart(2, '0')
      const day = String(d.getDate()).padStart(2, '0')
      const h = String(d.getHours()).padStart(2, '0')
      const min = String(d.getMinutes()).padStart(2, '0')
      return `${y}-${m}-${day} ${h}:${min}`
    },

    countByStatus(tests) {
      return {
        total: tests.length,
        passed: tests.filter(t => t.status === 'pass').length,
        failed: tests.filter(t => t.status === 'fail').length,
      }
    },

    getSummary(tests) {
      const s = this.countByStatus(tests)
      const type = s.failed > 0 ? 'danger' : (s.passed === s.total ? 'success' : 'warning')
      return { ...s, type }
    },

    statusIcon(status) {
      return {
        'pass': 'el-icon-success',
        'fail': 'el-icon-error',
        'running': 'el-icon-loading',
        'pending': 'el-icon-time',
        'skip': 'el-icon-minus',
      }[status] || 'el-icon-question'
    },

    statusLabel(status) {
      return {
        'pass': '通过',
        'fail': '失败',
        'running': '执行中',
        'pending': '待执行',
        'skip': '跳过',
      }[status] || '未知'
    },

    categoryTagType(cat) {
      return {
        'LLM': '', 'MCP': '', '计量': 'info', '语义': 'warning', '安全': 'danger',
        '进程': '', 'Pipe': '', 'MAC': 'danger', '操作': 'success',
        'Skill': 'warning', '本地LLM': 'info', '存储': 'info', '熔断': 'danger',
      }[cat] || 'info'
    },

    testRowClass({ row }) {
      if (row.status === 'pass') return 'row-pass'
      if (row.status === 'fail') return 'row-fail'
      return ''
    },

    showDetail(row) {
      this.detailDialog = {
        visible: true,
        title: row.name + ' — 详细信息',
        content: typeof row.detail === 'string' ? row.detail : JSON.stringify(row.detail, null, 2)
      }
    },

    // ====== 测试执行引擎 ======
    async runSingleTest(test, executor) {
      test.status = 'running'
      test.duration = null
      test.detail = null
      const start = performance.now()
      try {
        const result = await executor(test)
        test.duration = Math.round(performance.now() - start)
        test.status = result.success ? 'pass' : 'fail'
        test.detail = result.detail || null
      } catch (e) {
        test.duration = Math.round(performance.now() - start)
        test.status = 'fail'
        test.detail = 'Error: ' + (e.message || e)
      }
    },

    async runExternalTests() {
      this.isRunningExternal = true
      for (const test of this.externalTests) {
        await this.runSingleTest(test, (t) => this.executeExternalTest(t))
      }
      this.isRunningExternal = false
      this.updateNodeStatus()
    },

    async runInternalTests() {
      this.isRunningInternal = true
      for (const test of this.internalTests) {
        await this.runSingleTest(test, (t) => this.executeInternalTest(t))
      }
      this.isRunningInternal = false
      this.updateNodeStatus()
    },

    async runAllTests() {
      this.isRunningAll = true
      await this.runExternalTests()
      await this.runInternalTests()
      this.isRunningAll = false
    },

    // --- 外网测试执行器 (实际对接时替换) ---
    async executeExternalTest(test) {
      // TODO: 接入实际 LLM API 调用
      // 当前为 mock — 实际实现时通过 sgClaw pipe 或直接 HTTP 调用
      if (typeof window.sgClawTestRunner !== 'undefined') {
        return await window.sgClawTestRunner.runExternal(test.name)
      }
      // Mock: 模拟 200-800ms 延迟
      await this.sleep(200 + Math.random() * 600)
      return { success: true, detail: '[Mock] 测试通过 — 请接入实际 API 后重新验证' }
    },

    // --- 内网测试执行器 (实际对接时替换) ---
    async executeInternalTest(test) {
      // TODO: 通过 FunctionsUI 调用 C++ 测试接口
      // window.sgFunctionsUI('sgclaw_test', { testName: test.name }, callback)
      if (typeof window.sgClawTestRunner !== 'undefined') {
        return await window.sgClawTestRunner.runInternal(test.name)
      }
      // Mock: 模拟 50-300ms 延迟
      await this.sleep(50 + Math.random() * 250)
      return { success: true, detail: '[Mock] 测试通过 — 请接入 sgClaw 进程后重新验证' }
    },

    updateNodeStatus() {
      // 根据测试结果更新架构图节点状态
      this.nodeStatus.frontend = true // 页面本身能打开即为 true
      this.nodeStatus.browser = this.internalTests
        .filter(t => ['进程', 'Pipe', 'MAC', '操作'].includes(t.category))
        .some(t => t.status === 'pass')
      this.nodeStatus.rust = this.internalTests
        .filter(t => ['Pipe', 'Skill', '熔断', '存储'].includes(t.category))
        .some(t => t.status === 'pass')
      this.nodeStatus.llm = this.externalTests
        .filter(t => t.category === 'LLM')
        .some(t => t.status === 'pass')
    },

    exportReport() {
      const data = {
        date: this.reportDate,
        overall: this.overallStatus,
        external: this.externalTests.map(t => ({
          name: t.name, category: t.category, status: t.status, duration: t.duration
        })),
        internal: this.internalTests.map(t => ({
          name: t.name, category: t.category, status: t.status, duration: t.duration
        })),
        e2e: this.e2eScenarios.map(s => ({
          name: s.name, status: s.status, metrics: s.metrics
        })),
        performance: this.perfMetrics
      }
      const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' })
      const url = URL.createObjectURL(blob)
      const a = document.createElement('a')
      a.href = url
      a.download = `sgclaw-report-${this.reportDate.replace(/[: ]/g, '-')}.json`
      a.click()
      URL.revokeObjectURL(url)
      this.$message.success('报告已导出')
    },

    sleep(ms) {
      return new Promise(resolve => setTimeout(resolve, ms))
    }
  },

  mounted() {
    this.nodeStatus.frontend = true
  }
}
</script>

<style scoped>
.sgclaw-report {
  padding: 20px;
  background: #f5f7fa;
  min-height: 100vh;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB",
    "Microsoft YaHei", sans-serif;
}

/* === 顶部 === */
.report-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  margin-bottom: 20px;
  padding: 20px 24px;
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.06);
}
.report-title {
  font-size: 22px;
  font-weight: 600;
  color: #303133;
  margin: 0 0 8px 0;
}
.header-meta {
  color: #909399;
  font-size: 13px;
}
.header-right {
  display: flex;
  gap: 8px;
  flex-shrink: 0;
}

/* === 统计卡片 === */
.dashboard-row {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
  margin-bottom: 20px;
}
.stat-card {
  border-radius: 8px;
}
.stat-card >>> .el-card__body {
  padding: 16px 20px;
}
.stat-content {
  display: flex;
  align-items: center;
  gap: 16px;
}
.stat-icon {
  width: 48px;
  height: 48px;
  border-radius: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-size: 24px;
  flex-shrink: 0;
}
.stat-value {
  font-size: 28px;
  font-weight: 700;
  color: #303133;
  line-height: 1.2;
}
.stat-label {
  font-size: 13px;
  color: #909399;
  margin-top: 2px;
}

/* === 通用卡片 === */
.section-card {
  margin-bottom: 20px;
  border-radius: 8px;
}
.section-card >>> .el-card__header {
  padding: 14px 20px;
  border-bottom: 1px solid #ebeef5;
}
.section-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 16px;
  font-weight: 600;
  color: #303133;
}
.section-header i {
  margin-right: 6px;
}
.section-actions {
  display: flex;
  gap: 8px;
  align-items: center;
}
.test-description {
  padding: 12px 0 16px;
  color: #606266;
  font-size: 13px;
  line-height: 1.6;
}

/* === 架构图 === */
.arch-diagram {
  padding: 24px 0;
  overflow-x: auto;
}
.arch-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0;
  min-width: 900px;
}
.arch-node {
  width: 180px;
  padding: 16px;
  border-radius: 10px;
  border: 2px solid #dcdfe6;
  background: #fff;
  text-align: center;
  position: relative;
  transition: all 0.3s;
  flex-shrink: 0;
}
.arch-node.node-active {
  border-color: #67C23A;
  box-shadow: 0 0 12px rgba(103, 194, 58, 0.2);
}
.node-icon {
  font-size: 28px;
  margin-bottom: 8px;
  color: #409EFF;
}
.arch-frontend .node-icon { color: #E6A23C; }
.arch-browser .node-icon { color: #409EFF; }
.arch-rust .node-icon { color: #F56C6C; }
.arch-llm .node-icon { color: #67C23A; }
.node-name {
  font-size: 14px;
  font-weight: 600;
  color: #303133;
}
.node-tech {
  font-size: 11px;
  color: #909399;
  margin-top: 2px;
}
.node-detail {
  font-size: 10px;
  color: #C0C4CC;
  margin-top: 4px;
}
.node-status {
  position: absolute;
  top: -8px;
  right: -8px;
  font-size: 20px;
}
.node-status .el-icon-success { color: #67C23A; }
.node-status .el-icon-remove { color: #dcdfe6; }

.arch-arrow {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 80px;
  flex-shrink: 0;
}
.arrow-line {
  width: 60px;
  height: 2px;
  background: #dcdfe6;
  position: relative;
}
.arrow-line::after {
  content: '';
  position: absolute;
  right: -1px;
  top: -4px;
  border: 5px solid transparent;
  border-left-color: #dcdfe6;
}
.arrow-pipe {
  background: #F56C6C;
  height: 3px;
}
.arrow-pipe::after {
  border-left-color: #F56C6C;
}
.arrow-label {
  font-size: 9px;
  color: #C0C4CC;
  margin-top: 4px;
  white-space: nowrap;
}

/* === 测试状态 === */
.test-name {
  display: flex;
  align-items: center;
  gap: 6px;
}
.test-status {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  font-weight: 500;
}
.status-pass { color: #67C23A; }
.status-fail { color: #F56C6C; }
.status-running { color: #409EFF; }
.status-pending { color: #909399; }
.status-skip { color: #C0C4CC; }

.text-muted { color: #C0C4CC; }
.text-success { color: #67C23A; }
.text-danger { color: #F56C6C; }

/* 表格行高亮 */
.sgclaw-report >>> .row-pass {
  background: #f0f9eb !important;
}
.sgclaw-report >>> .row-fail {
  background: #fef0f0 !important;
}

/* === E2E 场景卡片 === */
.e2e-scenarios {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.scenario-card {
  border: 1px solid #ebeef5;
  border-radius: 8px;
  padding: 16px;
  background: #fafbfc;
}
.scenario-header {
  display: flex;
  align-items: center;
  gap: 12px;
}
.scenario-num {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: #409EFF;
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 14px;
  flex-shrink: 0;
}
.scenario-info {
  flex: 1;
}
.scenario-name {
  font-weight: 600;
  color: #303133;
  font-size: 14px;
}
.scenario-instruction {
  font-size: 12px;
  color: #909399;
  font-style: italic;
  margin-top: 2px;
}
.scenario-steps {
  margin-top: 12px;
  padding-left: 44px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.step-item {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
}
.step-num {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #ebeef5;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  color: #909399;
  flex-shrink: 0;
}
.step-action {
  flex: 1;
  color: #606266;
}
.step-result {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 11px;
  color: #909399;
}
.scenario-metrics {
  margin-top: 12px;
  padding-top: 8px;
  border-top: 1px dashed #ebeef5;
  font-size: 12px;
  color: #909399;
  padding-left: 44px;
}

/* === 性能基准 === */
.perf-grid {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 8px 0;
}
.perf-item {
  display: grid;
  grid-template-columns: 140px 1fr 180px;
  align-items: center;
  gap: 12px;
}
.perf-label {
  font-size: 13px;
  color: #606266;
  text-align: right;
}
.perf-bar-container {
  height: 16px;
  background: #f5f7fa;
  border-radius: 8px;
  overflow: hidden;
}
.perf-bar {
  height: 100%;
  border-radius: 8px;
  transition: width 0.6s ease;
}
.perf-values {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
}
.perf-actual {
  font-weight: 600;
  color: #303133;
}
.perf-target {
  color: #C0C4CC;
}

/* === 弹窗 === */
.detail-content {
  background: #1e1e1e;
  color: #d4d4d4;
  padding: 16px;
  border-radius: 6px;
  font-family: "Consolas", "Monaco", "Courier New", monospace;
  font-size: 12px;
  line-height: 1.6;
  max-height: 400px;
  overflow: auto;
  white-space: pre-wrap;
  word-break: break-all;
}

/* === 响应式 === */
@media (max-width: 1200px) {
  .dashboard-row { grid-template-columns: repeat(2, 1fr); }
  .arch-row { flex-wrap: wrap; justify-content: center; }
}
</style>
