<!-- tools/netdiag/templates/wizard.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>网络体检</title>
  <script defer src="/static/alpine.min.js"></script>
  <script src="/static/tailwind.js"></script>
  <style>[x-cloak]{display:none!important}</style>
</head>
<body class="min-h-screen bg-slate-50 text-slate-900">
<main class="max-w-5xl mx-auto px-4 py-8" x-data="netdiag()" x-cloak>
  <h1 class="text-2xl font-bold mb-1">网络体检</h1>
  <p class="text-slate-600 mb-6">网络卡？点一下，给你这台电脑做个体检。</p>

  <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
    <!-- 左：流程图 -->
    <aside class="md:col-span-1 bg-white rounded-xl ring-1 ring-slate-200 p-4">
      <h2 class="text-xs uppercase text-slate-500 font-semibold mb-3">排查流程</h2>
      <ol class="space-y-2 text-sm">
        <template x-for="(s, i) in steps" :key="i">
          <li class="flex items-center gap-2 px-2 py-1.5 rounded"
              :class="i===active ? 'bg-blue-50 ring-1 ring-blue-200 font-medium' : ''">
            <span class="h-2 w-2 rounded-full"
                  :class="{'bg-emerald-500': s.state==='ok','bg-amber-500': s.state==='warn','bg-red-500': s.state==='crit','bg-slate-300': s.state==='idle'}"></span>
            <span x-text="s.label"></span>
          </li>
        </template>
      </ol>
    </aside>

    <!-- 右：当前步骤/结果 -->
    <section class="md:col-span-2 bg-white rounded-xl ring-1 ring-slate-200 p-6">
      <template x-if="!started">
        <button @click="runLocal()" class="px-5 py-2.5 bg-blue-600 hover:bg-blue-700 text-white rounded-md font-medium">开始排查</button>
      </template>

      <template x-if="loading">
        <p class="text-slate-500">正在体检本机网络…</p>
      </template>

      <template x-if="result">
        <div>
          <div class="rounded-lg p-4 mb-4"
               :class="{'bg-red-50': result.worst==='crit','bg-amber-50': result.worst==='warn','bg-emerald-50': result.worst==='ok'}">
            <div class="font-semibold mb-1">根因</div>
            <div class="text-sm" x-text="result.root_cause || '未发现明显问题'"></div>
          </div>
          <template x-for="f in result.findings" :key="f.title">
            <div class="border-t border-slate-100 py-3">
              <div class="flex items-center gap-2">
                <span class="text-xs px-2 py-0.5 rounded-full"
                      :class="{'bg-red-100 text-red-700': f.severity==='crit','bg-amber-100 text-amber-700': f.severity==='warn','bg-emerald-100 text-emerald-700': f.severity==='ok'}"
                      x-text="f.title"></span>
              </div>
              <p class="text-sm text-slate-700 mt-1" x-text="f.detail"></p>
              <p class="text-sm text-emerald-700 mt-1" x-show="f.self_fix" x-text="'你可以：' + (f.self_fix||'')"></p>
              <p class="text-sm text-blue-700 mt-1" x-show="f.it_fix" x-text="'转 IT：' + (f.it_fix||'')"></p>
            </div>
          </template>

          <!-- 可选：AP 侧关联 -->
          <details class="mt-6">
            <summary class="cursor-pointer text-sm text-slate-600">怀疑是 AP 问题？进一步确认（需控制器密码，用完即弃）</summary>
            <div class="mt-3 space-y-2">
              <input x-model="ap.host" placeholder="控制器地址 192.168.1.2" class="w-full rounded border-slate-300 text-sm">
              <input x-model="ap.user" placeholder="账号 admin" class="w-full rounded border-slate-300 text-sm">
              <input x-model="ap.password" type="password" placeholder="密码（不保存）" class="w-full rounded border-slate-300 text-sm">
              <button @click="runAp()" :disabled="apLoading" class="px-4 py-2 bg-slate-800 text-white rounded text-sm disabled:opacity-50">连控制器确认</button>
              <p class="text-sm text-red-600 mt-1" x-show="apError" x-text="apError"></p>
            </div>
          </details>

          <button @click="copyReport()" class="mt-6 px-4 py-2 bg-slate-100 rounded text-sm">复制诊断摘要给 IT</button>
        </div>
      </template>
    </section>
  </div>

<script>
function netdiag() {
  return {
    started:false, loading:false, apLoading:false, apError:'', result:null, active:0,
    ap:{host:'192.168.1.2', user:'admin', password:'', my_ap_name:null},
    steps:[
      {label:'界定故障域', state:'idle'},
      {label:'本机 / 无线', state:'idle'},
      {label:'出口 / 运营商', state:'idle'},
      {label:'AP 侧关联', state:'idle'},
    ],
    async runLocal(){
      this.started=true; this.loading=true; this.active=1;
      const r = await fetch('/api/diagnose/local', {method:'POST'});
      this.result = await r.json(); this.loading=false; this.paint();
    },
    async runAp(){
      if (this.apLoading) return;
      this.active=3; this.apLoading=true;
      try {
        const r = await fetch('/api/diagnose/ap', {method:'POST',
          headers:{'Content-Type':'application/json'}, body:JSON.stringify(this.ap)});
        const apRes = await r.json();
        if (!r.ok || !apRes || !apRes.findings) { this.apError='连接失败，请重试'; return; }
        this.apError='';
        this.result.findings = this.result.findings.concat(apRes.findings);
        if (apRes.worst==='crit' || (apRes.worst==='warn' && this.result.worst==='ok'))
          this.result.root_cause = apRes.root_cause;
        this.result.worst = worstOf(this.result.worst, apRes.worst);
        if (apRes.evidence) this.result.evidence = (this.result.evidence||'') + '\n\n' + apRes.evidence;
        this.paint();
      } catch(e) {
        this.apError='连接出错，请重试';
      } finally {
        this.ap.password=''; this.apLoading=false;
      }
    },
    paint(){
      const sev = l => { const f=this.result.findings.filter(x=>x.layer===l);
        return f.some(x=>x.severity==='crit')?'crit':f.some(x=>x.severity==='warn')?'warn':f.length?'ok':'idle'; };
      this.steps[1].state=sev('local'); this.steps[2].state=sev('uplink'); this.steps[3].state=sev('ap');
      this.steps[0].state='ok';
    },
    copyReport(){
      const lines=this.result.findings.map(f=>`[${f.severity}] ${f.title} — ${f.detail}`);
      let text='网络体检摘要\n根因: '+(this.result.root_cause||'')+'\n'+lines.join('\n');
      if (this.result.evidence) text += '\n\n—— 原始数据（供 IT）——\n'+this.result.evidence;
      navigator.clipboard.writeText(text);
    },
  };
}
function worstOf(a,b){const o={ok:0,warn:1,crit:2};return o[b]>o[a]?b:a;}
</script>
</main>
</body>
</html>
