<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>🛠️ Ops-Assistant</title>
<style>
:root{
  --bg:#f5f7fb;
  --text:#222;
  --card:#fff;
  --border:#e5e7eb;
  --muted:#6b7280;
  --accent:#ee5a24;
  --accent-hover:#d63031;
  --header-bg:linear-gradient(135deg,#ff6b6b,#ee5a24);
  --chip:#f3f4f6;
}
[data-theme="dark"]{
  --bg:#0f172a;
  --text:#e5e7eb;
  --card:#111827;
  --border:#1f2937;
  --muted:#9ca3af;
  --accent:#f97316;
  --accent-hover:#ea580c;
  --header-bg:linear-gradient(135deg,#7c2d12,#ea580c);
  --chip:#111827;
}
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:var(--bg);color:var(--text);min-height:100vh}
.header{background:var(--header-bg);color:#fff;padding:18px 20px;position:sticky;top:0;z-index:100;box-shadow:0 2px 10px rgba(0,0,0,.15);display:flex;align-items:center;justify-content:space-between;gap:12px}
.header h1{font-size:22px}
.header .subtitle{font-size:12px;opacity:.9}
.header .right{display:flex;align-items:center;gap:8px}
.header .right a,.header .right button{color:#fff;text-decoration:none;background:rgba(255,255,255,.2);padding:0 10px;border-radius:8px;font-size:13px;border:none;cursor:pointer;line-height:28px;height:28px;display:inline-flex;align-items:center;justify-content:center}
.header .right button{width:28px;padding:0;transition:transform .2s ease}
.header .right button.spin{transform:rotate(180deg)}
.header .right button svg{width:16px;height:16px;stroke:#fff;fill:none;stroke-width:2}

.wrap{max-width:980px;margin:0 auto;padding:14px}
.grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px}
.card{background:var(--card);border-radius:6px;padding:14px;border:1px solid var(--border);box-shadow:none}
.card h3{font-size:14px;margin-bottom:10px}
.stat{display:flex;align-items:center;justify-content:space-between;margin-bottom:6px;font-size:13px;color:var(--muted)}
.stat b{font-size:18px;color:var(--accent)}

.section{margin-top:12px}
.section-title{font-size:13px;color:var(--muted);margin:10px 2px}
.tags{display:flex;flex-wrap:wrap;gap:8px}
.tag{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--border);border-radius:6px;padding:4px 8px;font-size:12px;background:var(--card)}
.tag .dot{width:8px;height:8px;border-radius:50%}
.dot.ok{background:#22c55e}.dot.err{background:#ef4444}.dot.off{background:#9ca3af}

.actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}
.actions a{display:inline-block;text-decoration:none;background:var(--accent);color:#fff;border-radius:6px;padding:8px 12px;font-size:13px;border:1px solid rgba(0,0,0,.06)}
.actions a.secondary{background:#6b7280}
.actions a.hidden{display:none}

.list{display:grid;gap:8px}
.job{border:1px solid var(--border);border-radius:6px;padding:10px;font-size:12px;background:var(--card)}
.badge{display:inline-block;padding:2px 8px;border-radius:999px;font-size:12px}
.pending{background:#fef3c7;color:#92400e}.running{background:#dbeafe;color:#1e3a8a}.success{background:#dcfce7;color:#166534}.failed{background:#fee2e2;color:#991b1b}.cancelled{background:#e5e7eb;color:#374151}

.empty{padding:30px;text-align:center;color:var(--muted)}

@media(max-width:720px){
  .grid{grid-template-columns:1fr}
  .header{flex-direction:column;align-items:flex-start}
  .header .right{align-self:flex-end}
}
.theme-hidden{display:none !important;}
</style>
</head>
<body>
<div class="header">
  <div>
    <h1>🛠️ Ops-Assistant</h1>
    <div class="subtitle">{{.version}}</div>
  </div>
  <div class="right">
    <button id="themeToggle" class="theme-hidden" title="切换主题" aria-label="切换主题">
      <svg id="themeIcon" viewBox="0 0 24 24" aria-hidden="true">
        <circle cx="12" cy="12" r="5"></circle>
        <line x1="12" y1="2" x2="12" y2="4"></line>
        <line x1="12" y1="20" x2="12" y2="22"></line>
        <line x1="2" y1="12" x2="4" y2="12"></line>
        <line x1="20" y1="12" x2="22" y2="12"></line>
        <line x1="4.2" y1="4.2" x2="5.8" y2="5.8"></line>
        <line x1="18.2" y1="18.2" x2="19.8" y2="19.8"></line>
        <line x1="18.2" y1="5.8" x2="19.8" y2="4.2"></line>
        <line x1="4.2" y1="19.8" x2="5.8" y2="18.2"></line>
      </svg>
    </button>
    <a href="/logout">退出</a>
  </div>
</div>

<div class="wrap">
  <div class="grid">
    <div class="card">
      <h3>任务概览</h3>
      <div class="stat"><span>Pending</span><b id="sPending">0</b></div>
      <div class="stat"><span>Running</span><b id="sRunning">0</b></div>
      <div class="stat"><span>Success</span><b id="sSuccess">0</b></div>
      <div class="stat"><span>Failed</span><b id="sFailed">0</b></div>
      <div class="stat"><span>Cancelled</span><b id="sCancelled">0</b></div>
      <div class="actions">
        <a id="btnOps" href="/ops" class="hidden">🛠️ 任务中心</a>
      </div>
    </div>
    <div class="card">
      <h3>模块状态</h3>
      <div class="tags" id="modulesTag"></div>
      <div class="actions">
        <a id="btnCPA" href="/cpa" class="secondary hidden">🔧 CPA 配置</a>
        <a id="btnCF" href="/cf" class="secondary hidden">☁️ CF 配置</a>
        <a id="btnAI" href="/ai" class="secondary hidden" style="display:none">🤖 AI 配置</a>
        <a id="btnModules" href="#" class="secondary hidden" onclick="toggleModulesPanel();return false;">⚙️ 模块开关</a>
      </div>
    </div>
    <div class="card">
      <h3>通道状态</h3>
      <div class="tags" id="channelsTag"></div>
      <div class="actions">
        <a id="btnChannels" href="/channels" class="hidden">🔌 渠道配置</a>
        <a id="btnAudit" href="/audit" class="secondary hidden">🧾 审计日志</a>
      </div>
    </div>
  </div>

  <div class="section">
    <div class="section-title">最近任务</div>
    <div id="recentJobs" class="list"></div>
  </div>
</div>

<script>
let me=null;
const state={overview:null};

async function api(url,opt={}){
  const r=await fetch(url,opt);
  const out=await r.json().catch(()=>({}));
  if(!r.ok) throw new Error(out.message||('HTTP '+r.status));
  return out?.data||{};
}
function esc(s){return String(s||'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));}
function can(k){return !!(me&&me.effective_capabilities&&me.effective_capabilities[k]);}

async function loadMe(){
  const r=await fetch('/api/v1/me');
  const out=await r.json().catch(()=>({}));
  if(!r.ok) throw new Error(out.message||'读取用户失败');
  me=out?.data||{};
}

function initUI(){
  document.getElementById('btnOps').classList.toggle('hidden',!can('can_view_ops'));
  document.getElementById('btnChannels').classList.toggle('hidden',!can('can_view_channels'));
  document.getElementById('btnCPA').classList.toggle('hidden',!can('can_view_flags'));
  document.getElementById('btnCF').classList.toggle('hidden',!can('can_view_flags'));
  document.getElementById('btnAI').classList.toggle('hidden',!can('can_view_flags'));
  document.getElementById('btnAudit').classList.toggle('hidden',!can('can_view_audit'));
  document.getElementById('btnModules').classList.toggle('hidden',!can('can_view_flags'));
}

function renderOverview(){
  const ov=state.overview||{};
  const sc=ov.jobs?.status_count||{};
  document.getElementById('sPending').textContent=sc.pending??0;
  document.getElementById('sRunning').textContent=sc.running??0;
  document.getElementById('sSuccess').textContent=sc.success??0;
  document.getElementById('sFailed').textContent=sc.failed??0;
  document.getElementById('sCancelled').textContent=sc.cancelled??0;

  const mods=Array.isArray(ov.modules)?ov.modules:[];
  document.getElementById('modulesTag').innerHTML=mods.length?mods.map(m=>{
    const dot=m.enabled?'ok':'off';
    return `<span class="tag"><span class="dot ${dot}"></span>${esc(m.module)}</span>`;
  }).join(''):'<div class="empty">暂无模块</div>';

  const chs=Array.isArray(ov.channels)?ov.channels:[];
  document.getElementById('channelsTag').innerHTML=chs.length?chs.map(c=>{
    const dot=c.status==='ok'?'ok':(c.enabled?'err':'off');
    return `<span class="tag"><span class="dot ${dot}"></span>${esc(c.platform)}</span>`;
  }).join(''):'<div class="empty">暂无通道</div>';

  const jobs=Array.isArray(ov.jobs?.recent)?ov.jobs.recent:[];
  const box=document.getElementById('recentJobs');
  if(!jobs.length){ box.innerHTML='<div class="empty">暂无任务</div>'; return; }
  box.innerHTML=jobs.map(j=>`<div class="job">
    <div style="display:flex;justify-content:space-between;gap:8px;flex-wrap:wrap;">
      <strong>#${j.id} ${esc(j.command||'')}</strong>
      <span class="badge ${(j.status||'pending').toLowerCase()}">${esc(j.status||'pending')}</span>
    </div>
    <div style="color:#666;margin-top:4px;">runbook=${esc(j.runbook||'-')} · target=${esc(j.target||'-')}</div>
  </div>`).join('');
}

async function loadOverview(){
  state.overview=await api('/api/v1/dashboard/overview');
  renderOverview();
}

function toggleModulesPanel(){
  alert('模块开关请进入设置页（后续独立页面/弹窗）');
}

function setThemeIcon(theme){
  const icon=document.getElementById('themeIcon');
  if(!icon) return;
  if(theme==='dark'){
    icon.innerHTML = '<path d="M21 12.8A8.5 8.5 0 1 1 11.2 3.1 7 7 0 0 0 21 12.8z"></path>';
  }else{
    icon.innerHTML = '<circle cx="12" cy="12" r="5"></circle>'+
      '<line x1="12" y1="2" x2="12" y2="4"></line>'+
      '<line x1="12" y1="20" x2="12" y2="22"></line>'+
      '<line x1="2" y1="12" x2="4" y2="12"></line>'+
      '<line x1="20" y1="12" x2="22" y2="12"></line>'+
      '<line x1="4.2" y1="4.2" x2="5.8" y2="5.8"></line>'+
      '<line x1="18.2" y1="18.2" x2="19.8" y2="19.8"></line>'+
      '<line x1="18.2" y1="5.8" x2="19.8" y2="4.2"></line>'+
      '<line x1="4.2" y1="19.8" x2="5.8" y2="18.2"></line>';
  }
}
function applyTheme(theme){
  if(theme==='dark'){
    document.documentElement.setAttribute('data-theme','dark');
  }else{
    document.documentElement.removeAttribute('data-theme');
  }
  localStorage.setItem('theme',theme);
  setThemeIcon(theme);
}
function toggleTheme(){
  const cur=localStorage.getItem('theme')||'dark';
  const next=cur==='light'?'dark':'light';
  const btn=document.getElementById('themeToggle');
  if(btn){btn.classList.add('spin'); setTimeout(()=>btn.classList.remove('spin'),200);} 
  applyTheme(next);
}
function initTheme(){
  const saved=localStorage.getItem('theme')||'dark';
  applyTheme(saved);
  const btn=document.getElementById('themeToggle');
  if(btn){ btn.addEventListener('click',toggleTheme); }
}

(async function(){
  try{ initTheme(); await loadMe(); initUI(); await loadOverview(); setInterval(loadOverview,15000);}catch(e){
    document.getElementById('recentJobs').innerHTML='<div class="empty">初始化失败：'+esc(e.message||e)+'</div>';
  }
})();
</script>
</body>
</html>
