{% extends "ewoooc_base.html" %}

{% block title %}部署守門與程式碼審查 - EwoooC{% endblock %}

{% block extra_css %}
  <style>
    .code-review-page {
      --bg:      var(--momo-bg-paper);
      --panel:   var(--momo-bg-surface);
      --border:  var(--momo-border-light);
      --text:    var(--momo-text-primary);
      --muted:   var(--momo-text-secondary);
      --red:     var(--momo-danger);
      --orange:  var(--momo-warm-clay);
      --yellow:  var(--momo-warning);
      --green:   var(--momo-success);
      --blue:    var(--momo-info);
      --purple:  var(--momo-warm-saffron);
      --accent:  var(--momo-page-accent);
      color: var(--text);
      font-family: var(--momo-font-family);
      font-size: 14px;
    }
    .code-review-page * { box-sizing: border-box; }

    /* ── Layout ─────────────────────────────────────────── */
    .topbar { background: var(--momo-dot-grid), var(--panel); border: 1px solid var(--momo-border-strong); border-radius: var(--momo-radius-md); margin-bottom: 12px; padding: 14px 18px; display: flex; align-items: center; gap: 12px; }
    .topbar h1 { font-size: 18px; font-weight: 800; color: var(--text); margin: 0; letter-spacing: 0; }
    .topbar .badge { font-size: 11px; padding: 3px 8px; border-radius: var(--momo-radius-sm); background: var(--momo-tag-terra-bg); border: 1px solid var(--momo-tag-terra-border); color: var(--momo-tag-terra-text); }
    .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: minmax(280px, 340px) minmax(0, 1fr); gap: 12px; min-height: 680px; overflow: visible; }
    .sidebar { background: transparent; overflow: visible; }
    .main { overflow: visible; min-width: 0; }
    .layout,
    .sidebar,
    .main,
    .card,
    .card-header,
    .card-body {
      min-width: 0;
    }

    /* ── Card ───────────────────────────────────────────── */
    .card { background: var(--panel); border: 1px solid var(--border); border-radius: var(--momo-radius-md); margin-bottom: 12px; overflow: hidden; box-shadow: none; }
    .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: var(--momo-radius-sm); border: 1px solid var(--border); background: var(--momo-bg-paper); transition: border-color .3s; }
    .step.running { border-color: var(--blue); background: var(--momo-info-bg); }
    .step.ok      { border-color: var(--green); background: var(--momo-success-bg); }
    .step.error   { border-color: var(--red); background: var(--momo-danger-bg); }
    .step-num { width: 22px; height: 22px; border-radius: var(--momo-radius-sm); background: var(--momo-bg-subtle); font-size: 11px; font-weight: 700; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
    .step.ok .step-num,
    .step.running .step-num,
    .step.error .step-num   { color: var(--momo-text-inverse); }
    .step.ok .step-num      { background: var(--green); }
    .step.running .step-num { background: var(--blue); }
    .step.error .step-num   { background: var(--red); }
    .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 var(--momo-border-light); vertical-align: top; }
    tr:last-child td { border-bottom: none; }
    tr:hover td { background: var(--momo-bg-paper); }
    .badge-sev { display: inline-block; padding: 2px 7px; border-radius: 10px; font-size: 11px; font-weight: 700; }
    .badge-CRITICAL { background: var(--momo-danger-bg);  color: var(--momo-danger-text); }
    .badge-HIGH     { background: var(--momo-warm-clay-soft); color: var(--orange); }
    .badge-MEDIUM   { background: var(--momo-warning-bg); color: var(--momo-warning-text); }
    .badge-LOW      { background: var(--momo-success-bg);  color: var(--momo-success-text); }
    .badge-type { display: inline-block; padding: 1px 6px; border-radius: var(--momo-radius-sm); font-size: 10px; background: var(--momo-bg-paper); color: var(--muted); }
    code { background: var(--momo-bg-paper); padding: 1px 5px; border-radius: var(--momo-radius-xs); font-size: 12px; color: var(--momo-info-text); }

    /* ── Architecture Report ─────────────────────────────── */
    .report-box { background: var(--momo-bg-paper); border: 1px solid var(--border); border-radius: var(--momo-radius-sm); 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: var(--momo-info-bg); }
    .ea-box.critical { border-left-color: var(--red); background: var(--momo-danger-bg); }
    .ea-box.high     { border-left-color: var(--orange); background: var(--momo-warm-clay-soft); }
    .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: var(--momo-info-bg);  border: 1px solid var(--blue); }
    #statusBar.completed { background: var(--momo-success-bg);   border: 1px solid var(--green); }
    #statusBar.error     { background: var(--momo-danger-bg);   border: 1px solid var(--red); }
    #statusBar.skipped   { background: var(--momo-bg-paper); 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: var(--momo-page-accent-soft); color: var(--text); border-color: var(--momo-page-accent-line); }
    .tab-pane { display: none; }
    .tab-pane.active { display: block; }

    @media (max-width: 760px) {
      .topbar {
        flex-wrap: wrap;
        padding: 10px 14px;
      }

      .topbar h1 {
        font-size: 16px;
      }

      .layout {
        grid-template-columns: 1fr;
        height: auto;
        min-height: calc(100vh - 49px);
        overflow: visible;
      }

      .sidebar,
      .main {
        width: 100%;
        max-width: 100%;
        overflow: visible;
      }

      .sidebar {
        border-right: 0;
        border-bottom: 1px solid var(--border);
      }

      .card,
      .card-header,
      .card-body,
      .pipeline,
      .step,
      .step-info {
        max-width: 100%;
        min-width: 0;
      }

      .card-header {
        flex-wrap: wrap;
        overflow-wrap: anywhere;
      }

      #pipelineId {
        flex: 1 1 100%;
        margin-left: 0 !important;
        overflow-wrap: anywhere;
        white-space: normal;
      }

      .step-name,
      .step-agent,
      .step-summary,
      .hist-sha,
      .hist-meta,
      .report-box,
      .ea-box,
      code {
        overflow-wrap: anywhere;
        word-break: break-word;
      }

      .step-summary {
        white-space: normal;
      }

      .hist-item > div:first-child {
        align-items: flex-start;
        flex-direction: column;
        gap: 4px;
      }

      .tabs {
        flex-wrap: wrap;
      }

      .tab {
        flex: 1 1 150px;
        min-width: 0;
        text-align: center;
        overflow-wrap: anywhere;
      }

      .empty {
        padding: 28px 16px;
        overflow-wrap: anywhere;
      }

      #findingsTable table,
      #findingsTable thead,
      #findingsTable tbody,
      #findingsTable tr,
      #findingsTable td {
        display: block;
        width: 100%;
      }

      #findingsTable thead {
        display: none;
      }

      #findingsTable tr {
        border-bottom: 1px solid var(--border);
        padding: 10px 12px;
      }

      #findingsTable tr:last-child {
        border-bottom: 0;
      }

      #findingsTable td {
        border-bottom: 0;
        padding: 6px 0;
        overflow-wrap: anywhere;
      }

      #findingsTable td::before {
        color: var(--muted);
        content: "";
        display: block;
        font-size: 11px;
        font-weight: 700;
        letter-spacing: .04em;
        margin-bottom: 3px;
        text-transform: uppercase;
      }

      #findingsTable td:nth-child(1)::before { content: "嚴重度"; }
      #findingsTable td:nth-child(2)::before { content: "類型"; }
      #findingsTable td:nth-child(3)::before { content: "檔案 / 位置"; }
      #findingsTable td:nth-child(4)::before { content: "問題說明"; }
      #findingsTable td:nth-child(5)::before { content: "修復建議"; }
    }
  </style>
{% endblock %}

{% block content %}
<div class="code-review-page">

<!-- Top Bar -->
<div class="topbar">
  <span>🔍</span>
  <h1>部署守門與程式碼審查</h1>
  <span class="badge">EwoooC · 業績流程上線前檢查</span>
  <div id="liveDot" class="live-dot idle" title="流程狀態"></div>
</div>

<div class="layout">

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

    <!-- Review Steps -->
    <div class="card">
      <div class="card-header">審查流程
        <span id="pipelineId" style="font-size:11px;color:var(--muted);margin-left:auto;"></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">系統</div></div></div>
          <div class="step" id="step-2"><div class="step-num">2</div><div class="step-info"><div class="step-name">程式碼掃描</div><div class="step-agent">靜態規則與風險掃描</div></div></div>
          <div class="step" id="step-3"><div class="step-num">3</div><div class="step-info"><div class="step-name">架構品質評估</div><div class="step-agent">AI 延伸分析</div></div></div>
          <div class="step" id="step-4"><div class="step-num">4</div><div class="step-info"><div class="step-name">上線決策</div><div class="step-agent">風險優先級判斷</div></div></div>
          <div class="step" id="step-5"><div class="step-num">5</div><div class="step-info"><div class="step-name">修復派工</div><div class="step-agent">自動修復流程</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">🔴 最高</div></div>
          <div class="sev-cell sev-high"><div class="num" id="cnt-high">—</div><div class="lbl">🟠 高</div></div>
          <div class="sev-cell sev-medium"><div class="num" id="cnt-medium">—</div><div class="lbl">🟡 中</div></div>
          <div class="sev-cell sev-low"><div class="num" id="cnt-low">—</div><div class="lbl">🟢 低</div></div>
        </div>
      </div>
    </div>

    <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')">💡 架構評估</div>
      <div class="tab" onclick="switchTab('ea')">🤖 上線決策</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>等待程式碼審查完成...</div></div>
          </div>
        </div>
      </div>
    </div>

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

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

  </div>
</div>
</div>
{% endblock %}

{% block extra_js %}
<script>
const SEV_ORDER = { CRITICAL: 0, HIGH: 1, MEDIUM: 2, LOW: 3 };
const SEV_LABEL = { CRITICAL: '最高', HIGH: '高', MEDIUM: '中', LOW: '低' };
const PRIORITY_LABEL = { critical: '最高', high: '高', medium: '中', low: '低' };
const DEPLOY_TYPE_LABEL = { sync: '同步部署', rebuild: '重建部署', manual: '手動觸發' };
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');
}

// ── Review 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}">${SEV_LABEL[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>`;
}

// ── Architecture report ───────────────────────────────────────────
function renderArchitectureReport(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 priorityLabel = PRIORITY_LABEL[ea.priority] || ea.priority || '待確認';
  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}">${priorityLabel}</div>
        <div>${autoFix ? '🔧 <b>自動修復已觸發</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>`;
}

// ── 上線證據 ─────────────────────────────────────────────────────
function renderCommitInfo(state) {
  const el = document.getElementById('commitInfo');
  if (!state.commit_sha) return;
  const changedCount = (state.changed_files||[]).length;
  el.innerHTML = `
    <div><b>狀態</b> 已收到上線檢查資料</div>
    <div><b>模式</b> ${DEPLOY_TYPE_LABEL[state.deploy_type] || state.deploy_type || '同步部署'}</div>
    <div><b>變更</b> ${changedCount} 項待檢查</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>流程執行中</b> — 第 ${state.current_step}/5 步`,
    completed: `✅ <b>程式碼審查完成</b> — ${state.message||''}`,
    error:     `❌ <b>流程需確認</b> — 請查看風險清單`,
    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">上線檢查</span>
        <span style="font-size:11px;color:var(--muted)">${h.created_at.slice(0,16).replace('T',' ')}</span>
      </div>
      <div class="hist-meta">${(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 changedCount = (h.changed_files||[]).length;
  document.getElementById('commitInfo').innerHTML = `
    <div><b>狀態</b> 已完成上線檢查</div>
    <div><b>時間</b> ${h.created_at.slice(0,16).replace('T',' ')}</div>
    <div><b>變更</b> ${changedCount} 項已檢查</div>`;
  document.getElementById('pipelineId').textContent = '最近一次';
  const sBar = document.getElementById('statusBar');
  sBar.style.display = 'block';
  sBar.className = 'completed';
  sBar.innerHTML = `✅ <b>歷史記錄</b> — 已完成上線檢查${h.auto_fix ? '，並完成修復流程' : ''}`;
  renderFindings(h.findings || []);
  renderArchitectureReport(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||[]);
    renderArchitectureReport(state.openclaw_report);
    renderEA(state.ea_decision, state.auto_fix_triggered);
    renderCommitInfo(state);
    document.getElementById('pipelineId').textContent = state.pipeline_id ? '執行中' : '';

    // 每 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>
{% endblock %}
