<!DOCTYPE html>
<html lang="zh-TW">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>EwoooC — AI Code Review Dashboard</title>
  <style>
    :root {
      --bg:      #0d1117;
      --panel:   #161b22;
      --border:  #30363d;
      --text:    #e6edf3;
      --muted:   #8b949e;
      --red:     #f85149;
      --orange:  #d29922;
      --yellow:  #e3b341;
      --green:   #3fb950;
      --blue:    #58a6ff;
      --purple:  #bc8cff;
      --accent:  #e94560;
    }
    * { box-sizing: border-box; margin: 0; padding: 0; }
    body { background: var(--bg); color: var(--text); font-family: -apple-system, 'Segoe UI', sans-serif; font-size: 14px; }

    /* ── Layout ─────────────────────────────────────────── */
    .topbar { background: var(--panel); border-bottom: 1px solid var(--border); padding: 12px 24px; display: flex; align-items: center; gap: 12px; }
    .topbar h1 { font-size: 18px; font-weight: 700; color: var(--accent); }
    .topbar .badge { font-size: 11px; padding: 2px 8px; border-radius: 12px; background: #21262d; color: var(--muted); }
    .live-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--green); animation: pulse 1.5s infinite; margin-left: auto; }
    .live-dot.idle { background: var(--muted); animation: none; }
    @keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.3; } }

    .layout { display: grid; grid-template-columns: 340px 1fr; gap: 0; height: calc(100vh - 49px); overflow: hidden; }
    .sidebar { background: var(--panel); border-right: 1px solid var(--border); overflow-y: auto; padding: 16px; }
    .main { overflow-y: auto; padding: 16px; }

    /* ── Card ───────────────────────────────────────────── */
    .card { background: var(--panel); border: 1px solid var(--border); border-radius: 8px; margin-bottom: 12px; overflow: hidden; }
    .card-header { padding: 10px 14px; border-bottom: 1px solid var(--border); font-weight: 600; font-size: 13px; display: flex; align-items: center; gap: 8px; }
    .card-body { padding: 14px; }

    /* ── Pipeline Steps ──────────────────────────────────── */
    .pipeline { display: flex; flex-direction: column; gap: 6px; }
    .step { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 6px; border: 1px solid var(--border); background: #0d1117; transition: border-color .3s; }
    .step.running { border-color: var(--blue); background: rgba(88,166,255,.07); }
    .step.ok      { border-color: var(--green); background: rgba(63,185,80,.06); }
    .step.error   { border-color: var(--red); background: rgba(248,81,73,.07); }
    .step-num { width: 22px; height: 22px; border-radius: 50%; background: #21262d; font-size: 11px; font-weight: 700; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
    .step.ok .step-num      { background: var(--green); color: #000; }
    .step.running .step-num { background: var(--blue); color: #000; }
    .step.error .step-num   { background: var(--red); color: #fff; }
    .step-info { flex: 1; min-width: 0; }
    .step-name { font-weight: 600; font-size: 13px; }
    .step-agent { font-size: 11px; color: var(--purple); }
    .step-summary { font-size: 11px; color: var(--muted); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .step-icon { font-size: 16px; }
    .spinner { display: inline-block; animation: spin .8s linear infinite; }
    @keyframes spin { to { transform: rotate(360deg); } }

    /* ── Severity Badges ─────────────────────────────────── */
    .sev-grid { display: grid; grid-template-columns: repeat(2,1fr); gap: 8px; }
    .sev-cell { text-align: center; padding: 10px 6px; border-radius: 6px; border: 1px solid var(--border); }
    .sev-cell .num { font-size: 28px; font-weight: 700; line-height: 1; }
    .sev-cell .lbl { font-size: 11px; color: var(--muted); margin-top: 4px; }
    .sev-critical { border-color: var(--red)!important; }    .sev-critical .num { color: var(--red); }
    .sev-high     { border-color: var(--orange)!important; } .sev-high .num     { color: var(--orange); }
    .sev-medium   { border-color: var(--yellow)!important; } .sev-medium .num   { color: var(--yellow); }
    .sev-low      { border-color: var(--green)!important; }  .sev-low .num      { color: var(--green); }

    /* ── Findings Table ──────────────────────────────────── */
    table { width: 100%; border-collapse: collapse; font-size: 13px; }
    th { padding: 8px 10px; text-align: left; color: var(--muted); font-weight: 600; border-bottom: 1px solid var(--border); font-size: 11px; text-transform: uppercase; letter-spacing: .5px; }
    td { padding: 8px 10px; border-bottom: 1px solid #21262d; vertical-align: top; }
    tr:last-child td { border-bottom: none; }
    tr:hover td { background: rgba(255,255,255,.02); }
    .badge-sev { display: inline-block; padding: 2px 7px; border-radius: 10px; font-size: 11px; font-weight: 700; }
    .badge-CRITICAL { background: rgba(248,81,73,.2);  color: var(--red); }
    .badge-HIGH     { background: rgba(210,153,34,.2); color: var(--orange); }
    .badge-MEDIUM   { background: rgba(227,179,65,.2); color: var(--yellow); }
    .badge-LOW      { background: rgba(63,185,80,.2);  color: var(--green); }
    .badge-type { display: inline-block; padding: 1px 6px; border-radius: 8px; font-size: 10px; background: #21262d; color: var(--muted); }
    code { background: #21262d; padding: 1px 5px; border-radius: 4px; font-size: 12px; color: var(--blue); }

    /* ── OpenClaw Report ─────────────────────────────────── */
    .report-box { background: #0d1117; border: 1px solid var(--border); border-radius: 6px; padding: 12px; font-size: 13px; line-height: 1.6; }
    .report-box b { color: var(--text); }

    /* ── EA Decision ─────────────────────────────────────── */
    .ea-box { padding: 12px 14px; border-radius: 6px; border-left: 4px solid var(--blue); background: rgba(88,166,255,.06); }
    .ea-box.critical { border-left-color: var(--red); background: rgba(248,81,73,.07); }
    .ea-box.high     { border-left-color: var(--orange); background: rgba(210,153,34,.07); }
    .ea-box.medium   { border-left-color: var(--yellow); }
    .ea-box.low      { border-left-color: var(--green); }
    .ea-priority { font-size: 20px; font-weight: 700; }
    .ea-reasoning { color: var(--muted); font-size: 12px; margin-top: 4px; }
    .ea-fix { margin-top: 8px; font-size: 12px; }

    /* ── History ─────────────────────────────────────────── */
    .hist-item { padding: 10px; border: 1px solid var(--border); border-radius: 6px; margin-bottom: 8px; cursor: pointer; transition: border-color .2s; }
    .hist-item:hover { border-color: var(--blue); }
    .hist-sha { font-family: monospace; font-size: 12px; color: var(--blue); }
    .hist-meta { font-size: 11px; color: var(--muted); margin-top: 3px; }
    .hist-sev { display: flex; gap: 6px; margin-top: 5px; }
    .hist-sev span { font-size: 11px; padding: 1px 6px; border-radius: 8px; }

    /* ── Status Bar ──────────────────────────────────────── */
    #statusBar { padding: 10px 14px; border-radius: 6px; margin-bottom: 12px; font-size: 13px; display: none; }
    #statusBar.running   { background: rgba(88,166,255,.1);  border: 1px solid var(--blue); }
    #statusBar.completed { background: rgba(63,185,80,.1);   border: 1px solid var(--green); }
    #statusBar.error     { background: rgba(248,81,73,.1);   border: 1px solid var(--red); }
    #statusBar.skipped   { background: rgba(139,148,158,.1); border: 1px solid var(--border); }

    /* ── Empty state ─────────────────────────────────────── */
    .empty { text-align: center; padding: 40px; color: var(--muted); }
    .empty .icon { font-size: 40px; margin-bottom: 10px; }

    /* ── Tabs ────────────────────────────────────────────── */
    .tabs { display: flex; gap: 4px; margin-bottom: 12px; }
    .tab { padding: 6px 14px; border-radius: 6px; cursor: pointer; font-size: 13px; color: var(--muted); border: 1px solid transparent; }
    .tab.active { background: #21262d; color: var(--text); border-color: var(--border); }
    .tab-pane { display: none; }
    .tab-pane.active { display: block; }
  </style>
</head>
<body>

<!-- Top Bar -->
<div class="topbar">
  <span>🔍</span>
  <h1>AI Code Review</h1>
  <span class="badge">EwoooC · Post-Deploy Pipeline</span>
  <div id="liveDot" class="live-dot idle" title="Pipeline 狀態"></div>
</div>

<div class="layout">

  <!-- ── Sidebar ─────────────────────────────────────────────────── -->
  <div class="sidebar">

    <!-- Pipeline Steps -->
    <div class="card">
      <div class="card-header">🤖 Pipeline 進度
        <span id="pipelineId" style="font-size:11px;color:var(--muted);margin-left:auto;font-family:monospace"></span>
      </div>
      <div class="card-body">
        <div class="pipeline" id="stepsContainer">
          <div class="step" id="step-1"><div class="step-num">1</div><div class="step-info"><div class="step-name">讀取變更檔案</div><div class="step-agent">system</div></div></div>
          <div class="step" id="step-2"><div class="step-num">2</div><div class="step-info"><div class="step-name">Hermes 程式碼掃描</div><div class="step-agent">Hermes · hermes3:latest</div></div></div>
          <div class="step" id="step-3"><div class="step-num">3</div><div class="step-info"><div class="step-name">OpenClaw 架構評估</div><div class="step-agent">OpenClaw · Gemini 2.5</div></div></div>
          <div class="step" id="step-4"><div class="step-num">4</div><div class="step-info"><div class="step-name">Elephant Alpha 決策</div><div class="step-agent">Elephant Alpha · 100B</div></div></div>
          <div class="step" id="step-5"><div class="step-num">5</div><div class="step-info"><div class="step-name">NemoTron 行動派遣</div><div class="step-agent">NemoTron · Dispatcher</div></div></div>
        </div>
      </div>
    </div>

    <!-- Severity Summary -->
    <div class="card">
      <div class="card-header">📊 問題嚴重度分佈</div>
      <div class="card-body">
        <div class="sev-grid">
          <div class="sev-cell sev-critical"><div class="num" id="cnt-critical">—</div><div class="lbl">🔴 CRITICAL</div></div>
          <div class="sev-cell sev-high"><div class="num" id="cnt-high">—</div><div class="lbl">🟠 HIGH</div></div>
          <div class="sev-cell sev-medium"><div class="num" id="cnt-medium">—</div><div class="lbl">🟡 MEDIUM</div></div>
          <div class="sev-cell sev-low"><div class="num" id="cnt-low">—</div><div class="lbl">🟢 LOW</div></div>
        </div>
      </div>
    </div>

    <!-- Commit Info -->
    <div class="card">
      <div class="card-header">📦 本次部署資訊</div>
      <div class="card-body" id="commitInfo" style="font-size:13px;color:var(--muted);line-height:1.8;">
        <span style="color:var(--muted)">等待下次部署觸發...</span>
      </div>
    </div>

    <!-- History -->
    <div class="card">
      <div class="card-header">🕐 歷史記錄</div>
      <div class="card-body" style="padding:8px;" id="historyList">
        <div class="empty"><div class="icon">📋</div><div>載入中...</div></div>
      </div>
    </div>

  </div>

  <!-- ── Main Content ─────────────────────────────────────────────── -->
  <div class="main">

    <!-- Status Bar -->
    <div id="statusBar"></div>

    <!-- Tabs -->
    <div class="tabs">
      <div class="tab active" onclick="switchTab('findings')">⚠️ 問題清單</div>
      <div class="tab" onclick="switchTab('openclaw')">💡 OpenClaw 評估</div>
      <div class="tab" onclick="switchTab('ea')">🤖 Elephant Alpha 決策</div>
    </div>

    <!-- Tab: Findings -->
    <div class="tab-pane active" id="tab-findings">
      <div class="card">
        <div class="card-header">⚠️ 問題清單
          <span id="findingsCount" style="font-size:11px;color:var(--muted);margin-left:auto"></span>
        </div>
        <div class="card-body" style="padding:0">
          <div id="findingsTable">
            <div class="empty"><div class="icon">🔍</div><div>等待 Code Review 完成...</div></div>
          </div>
        </div>
      </div>
    </div>

    <!-- Tab: OpenClaw -->
    <div class="tab-pane" id="tab-openclaw">
      <div class="card">
        <div class="card-header">💡 OpenClaw 架構品質評估</div>
        <div class="card-body">
          <div class="report-box" id="openclawReport">
            <span style="color:var(--muted)">等待 OpenClaw 分析完成...</span>
          </div>
        </div>
      </div>
    </div>

    <!-- Tab: EA Decision -->
    <div class="tab-pane" id="tab-ea">
      <div class="card">
        <div class="card-header">🤖 Elephant Alpha 決策協調結果</div>
        <div class="card-body">
          <div id="eaDecision">
            <span style="color:var(--muted)">等待 Elephant Alpha 決策...</span>
          </div>
        </div>
      </div>
    </div>

  </div>
</div>

<script>
const SEV_ORDER = { CRITICAL: 0, HIGH: 1, MEDIUM: 2, LOW: 3 };
let _polling = null;
let _lastPipelineId = null;

// ── Tab switching ──────────────────────────────────────────────────
function switchTab(name) {
  document.querySelectorAll('.tab').forEach((t,i) => t.classList.toggle('active', ['findings','openclaw','ea'][i] === name));
  document.querySelectorAll('.tab-pane').forEach(p => p.classList.remove('active'));
  document.getElementById('tab-' + name).classList.add('active');
}

// ── Pipeline step rendering ───────────────────────────────────────
function renderSteps(steps, currentStep) {
  for (let i = 1; i <= 5; i++) {
    const el = document.getElementById('step-' + i);
    if (!el) continue;
    const info = steps.find(s => s.step === i);
    el.className = 'step';
    const numEl = el.querySelector('.step-num');
    const summEl = el.querySelector('.step-summary') || (() => {
      const d = document.createElement('div');
      d.className = 'step-summary';
      el.querySelector('.step-info').appendChild(d);
      return d;
    })();

    if (info) {
      if (info.status === 'ok')      { el.classList.add('ok');      numEl.textContent = '✓'; }
      else if (info.status === 'error') { el.classList.add('error'); numEl.textContent = '✗'; }
      else if (info.status === 'running') { el.classList.add('running'); numEl.innerHTML = '<span class="spinner">⟳</span>'; }
      summEl.textContent = info.summary || '';
    } else if (i === currentStep) {
      el.classList.add('running');
      numEl.innerHTML = '<span class="spinner">⟳</span>';
    } else {
      numEl.textContent = i;
    }
  }
}

// ── Severity counters ─────────────────────────────────────────────
function renderSeverity(sev) {
  ['critical','high','medium','low'].forEach(k => {
    const el = document.getElementById('cnt-' + k);
    if (el) el.textContent = (sev && sev[k] !== undefined) ? sev[k] : '0';
  });
}

// ── Findings table ────────────────────────────────────────────────
function renderFindings(findings) {
  const el = document.getElementById('findingsTable');
  document.getElementById('findingsCount').textContent = findings.length ? `共 ${findings.length} 項` : '';
  if (!findings.length) {
    el.innerHTML = '<div class="empty"><div class="icon">✅</div><div>無發現問題</div></div>';
    return;
  }
  const sorted = [...findings].sort((a,b) => (SEV_ORDER[a.severity]||3) - (SEV_ORDER[b.severity]||3));
  el.innerHTML = `<table>
    <thead><tr><th>嚴重度</th><th>類型</th><th>檔案 / 位置</th><th>問題說明</th><th>修復建議</th></tr></thead>
    <tbody>${sorted.map(f => `
      <tr>
        <td><span class="badge-sev badge-${f.severity}">${f.severity}</span></td>
        <td><span class="badge-type">${f.type||'?'}</span></td>
        <td><code>${(f.file||'').split('/').pop()}</code><br><span style="font-size:11px;color:var(--muted)">${f.line_hint||''}</span></td>
        <td>${escHtml(f.description||'')}</td>
        <td style="color:var(--muted)">${escHtml(f.suggestion||'')}</td>
      </tr>`).join('')}
    </tbody></table>`;
}

// ── OpenClaw report ───────────────────────────────────────────────
function renderOpenClaw(html) {
  document.getElementById('openclawReport').innerHTML = html || '<span style="color:var(--muted)">（未取得）</span>';
}

// ── EA Decision ───────────────────────────────────────────────────
function renderEA(ea, autoFix) {
  if (!ea || !ea.priority) {
    document.getElementById('eaDecision').innerHTML = '<span style="color:var(--muted)">等待決策...</span>';
    return;
  }
  const priColors = { critical: 'var(--red)', high: 'var(--orange)', medium: 'var(--yellow)', low: 'var(--green)' };
  const col = priColors[ea.priority] || 'var(--blue)';
  const fixFiles = (ea.fix_files||[]).map(f=>`<code>${f}</code>`).join(' ');
  document.getElementById('eaDecision').innerHTML = `
    <div class="ea-box ${ea.priority}">
      <div style="display:flex;align-items:center;gap:10px">
        <div class="ea-priority" style="color:${col}">${ea.priority.toUpperCase()}</div>
        <div>${autoFix ? '🔧 <b>自動修復已觸發（AiderHeal）</b>' : (ea.human_review_needed ? '👁 <b>需人工審查</b>' : '✅ <b>無需修復</b>')}</div>
      </div>
      <div class="ea-reasoning">${escHtml(ea.reasoning||'')}</div>
      ${fixFiles ? `<div class="ea-fix">修復範圍：${fixFiles}</div>` : ''}
    </div>`;
}

// ── Commit info ───────────────────────────────────────────────────
function renderCommitInfo(state) {
  const el = document.getElementById('commitInfo');
  if (!state.commit_sha) return;
  const files = (state.changed_files||[]).slice(0,5).map(f=>`<code>${f.split('/').pop()}</code>`).join(' ');
  const more = (state.changed_files||[]).length > 5 ? `<span style="color:var(--muted)">+${state.changed_files.length-5}</span>` : '';
  el.innerHTML = `
    <div><b>Commit</b> <code>${state.commit_sha.slice(0,8)}</code></div>
    <div><b>Branch</b> <code>${state.branch||'?'}</code></div>
    <div><b>模式</b> ${state.deploy_type||'sync'}</div>
    <div><b>變更</b> ${files} ${more}</div>`;
}

// ── Status bar ────────────────────────────────────────────────────
function renderStatusBar(state) {
  const el = document.getElementById('statusBar');
  const dot = document.getElementById('liveDot');
  if (!state.status) { el.style.display='none'; return; }

  el.style.display = 'block';
  el.className = state.status;
  const msgs = {
    running:   `⟳ <b>Pipeline 執行中</b> — Step ${state.current_step}/5`,
    completed: `✅ <b>Code Review 完成</b> — ${state.message||''}`,
    error:     `❌ <b>Pipeline 失敗</b> — ${escHtml(state.message||'')}`,
    skipped:   `⏭ <b>已略過</b> — ${escHtml(state.message||'')}`,
  };
  el.innerHTML = msgs[state.status] || '';

  dot.className = 'live-dot' + (state.status === 'running' ? '' : ' idle');
}

// ── History ───────────────────────────────────────────────────────
let _historyData = [];

function renderHistory(items) {
  _historyData = items;
  const el = document.getElementById('historyList');
  if (!items.length) { el.innerHTML = '<div class="empty"><div>尚無歷史記錄</div></div>'; return; }
  el.innerHTML = items.map((h, idx) => {
    const sev = h.severity_summary || {};
    return `<div class="hist-item" onclick="loadHistoryItem(${idx})" data-idx="${idx}">
      <div style="display:flex;justify-content:space-between">
        <span class="hist-sha">${h.commit_sha}</span>
        <span style="font-size:11px;color:var(--muted)">${h.created_at.slice(0,16).replace('T',' ')}</span>
      </div>
      <div class="hist-meta">🌿 ${h.branch}  •  ${(h.changed_files||[]).length} 檔案${h.auto_fix?' • 🔧 已自動修復':''}</div>
      <div class="hist-sev">
        ${sev.critical?`<span style="background:rgba(248,81,73,.2);color:var(--red)">🔴 ${sev.critical}</span>`:''}
        ${sev.high?`<span style="background:rgba(210,153,34,.2);color:var(--orange)">🟠 ${sev.high}</span>`:''}
        ${sev.medium?`<span style="background:rgba(227,179,65,.2);color:var(--yellow)">🟡 ${sev.medium}</span>`:''}
        ${sev.low?`<span style="background:rgba(63,185,80,.2);color:var(--green)">🟢 ${sev.low}</span>`:''}
        ${!h.total_issues?`<span style="color:var(--green)">✅ 無問題</span>`:''}
      </div>
    </div>`;
  }).join('');
}

function loadHistoryItem(idx) {
  const h = _historyData[idx];
  if (!h) return;
  document.querySelectorAll('.hist-item').forEach((el, i) => {
    el.style.borderColor = i === idx ? 'var(--blue)' : '';
  });
  renderSeverity(h.severity_summary);
  const files = (h.changed_files||[]).slice(0,5).map(f=>`<code>${f.split('/').pop()}</code>`).join(' ');
  const more = (h.changed_files||[]).length > 5 ? `<span style="color:var(--muted)">+${h.changed_files.length-5}</span>` : '';
  document.getElementById('commitInfo').innerHTML = `
    <div><b>Commit</b> <code>${h.commit_sha}</code></div>
    <div><b>Branch</b> <code>${h.branch||'?'}</code></div>
    <div><b>時間</b> ${h.created_at.slice(0,16).replace('T',' ')}</div>
    <div><b>變更</b> ${files} ${more}</div>`;
  document.getElementById('pipelineId').textContent = (h.pipeline_id||'').slice(-14);
  const sBar = document.getElementById('statusBar');
  sBar.style.display = 'block';
  sBar.className = 'completed';
  sBar.innerHTML = `✅ <b>歷史記錄</b> — Commit ${h.commit_sha}${h.auto_fix ? '  🔧 已自動修復' : ''}`;
  renderFindings(h.findings || []);
  renderOpenClaw(h.openclaw_report || '');
  renderEA(h.ea_decision || {}, h.auto_fix || false);
  for (let i = 1; i <= 5; i++) {
    const el = document.getElementById('step-' + i);
    if (el) { el.className = 'step ok'; el.querySelector('.step-num').textContent = '✓'; }
  }
}

// ── Main polling loop ─────────────────────────────────────────────
async function poll() {
  try {
    const r = await fetch('/code-review/api/status');
    const state = await r.json();

    if (state.pipeline_id !== _lastPipelineId && state.pipeline_id) {
      _lastPipelineId = state.pipeline_id;
    }

    renderStatusBar(state);
    renderSteps(state.steps||[], state.current_step||0);
    renderSeverity(state.severity_summary);
    renderFindings(state.findings||[]);
    renderOpenClaw(state.openclaw_report);
    renderEA(state.ea_decision, state.auto_fix_triggered);
    renderCommitInfo(state);
    document.getElementById('pipelineId').textContent = (state.pipeline_id||'').slice(-14);

    // 每 3s 輪詢（running）/ 30s（idle）
    const interval = state.status === 'running' ? 3000 : 30000;
    _polling = setTimeout(poll, interval);
  } catch(e) {
    _polling = setTimeout(poll, 10000);
  }
}

async function loadHistory() {
  try {
    const r = await fetch('/code-review/api/history?limit=15');
    const items = await r.json();
    renderHistory(items);
    // 無 active pipeline 且尚未顯示任何 pipeline 內容時，自動展示最新一筆
    if (items.length && !_lastPipelineId) {
      loadHistoryItem(0);
    }
  } catch(e) {
    document.getElementById('historyList').innerHTML = '<div class="empty"><div>載入失敗</div></div>';
  }
}

function escHtml(s) {
  return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
}

// ── Init ──────────────────────────────────────────────────────────
poll();
loadHistory();
setInterval(loadHistory, 60000);
</script>
</body>
</html>
