<?php
declare(strict_types=1);
$CFG = require __DIR__ . '/../config.php';
$title = htmlspecialchars((string) ($CFG['title'] ?? '云 SIP 管理'), ENT_QUOTES, 'UTF-8');
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?= $title ?></title>
<style>
:root{
  --bg:#0d1117; --panel:#151b23; --panel2:#1c232d; --border:#2a3340;
  --text:#e6edf3; --dim:#8b949e; --accent:#3b82f6; --ok:#22c55e; --warn:#f59e0b; --err:#ef4444;
}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--text);
  font:14px/1.55 -apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif}
a{color:var(--accent)}
.wrap{max-width:1180px;margin:0 auto;padding:22px}
header{display:flex;align-items:center;gap:14px;padding:16px 20px;background:var(--panel);
  border:1px solid var(--border);border-radius:14px;margin-bottom:18px}
.logo{width:38px;height:38px;border-radius:10px;background:linear-gradient(135deg,#3b82f6,#8b5cf6);
  display:grid;place-items:center;font-weight:700;font-size:16px;color:#fff;flex:none}
h1{font-size:17px;margin:0}
.sub{color:var(--dim);font-size:12px}
.spacer{flex:1}
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:14px;margin-bottom:18px}
.card{background:var(--panel);border:1px solid var(--border);border-radius:14px;padding:16px}
.card h2{font-size:13px;color:var(--dim);font-weight:600;margin:0 0 12px;letter-spacing:.3px;text-transform:uppercase}
.kpi{font-size:26px;font-weight:700}
.kpi small{font-size:13px;color:var(--dim);font-weight:400}
table{width:100%;border-collapse:collapse;font-size:13px}
th,td{text-align:left;padding:9px 10px;border-bottom:1px solid var(--border)}
th{color:var(--dim);font-weight:600;font-size:12px}
tr:last-child td{border-bottom:none}
.dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:7px;vertical-align:middle}
.on{background:var(--ok);box-shadow:0 0 8px var(--ok)}
.off{background:#4b5563}
.tag{display:inline-block;padding:1px 8px;border-radius:999px;font-size:12px;border:1px solid var(--border);color:var(--dim)}
.tag.ok{color:var(--ok);border-color:rgba(34,197,94,.4)}
.tag.err{color:var(--err);border-color:rgba(239,68,68,.4)}
.btn{background:var(--accent);color:#fff;border:0;border-radius:9px;padding:9px 16px;font-size:14px;cursor:pointer}
.btn:hover{filter:brightness(1.1)}
.btn.ghost{background:transparent;border:1px solid var(--border);color:var(--dim)}
input[type=password]{width:100%;padding:11px 13px;border-radius:9px;border:1px solid var(--border);
  background:var(--panel2);color:var(--text);font-size:14px;outline:none}
input[type=password]:focus{border-color:var(--accent)}
#login{max-width:360px;margin:9vh auto;background:var(--panel);border:1px solid var(--border);
  border-radius:16px;padding:26px}
#login h1{margin:0 0 4px;font-size:19px}
#login p{color:var(--dim);margin:0 0 18px;font-size:13px}
.err{color:var(--err);font-size:13px;min-height:18px;margin-top:9px}
.mono{font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:12px}
.foot{color:var(--dim);font-size:12px;text-align:center;margin:22px 0 8px}
.muted{color:var(--dim)}
</style>
</head>
<body>

<div id="login" style="display:none">
  <h1><?= $title ?></h1>
  <p>请登录后管理分机与查看通话</p>
  <input type="password" id="pwd" placeholder="管理口令" autocomplete="current-password">
  <div class="err" id="err"></div>
  <div style="margin-top:12px"><button class="btn" style="width:100%" id="doLogin">登 录</button></div>
</div>

<div class="wrap" id="app" style="display:none">
  <header>
    <div class="logo">SIP</div>
    <div>
      <h1><?= $title ?></h1>
      <div class="sub" id="serverInfo">—</div>
    </div>
    <div class="spacer"></div>
    <span class="tag" id="ts">—</span>
    <button class="btn ghost" id="refresh">刷新</button>
    <button class="btn ghost" id="logout">退出</button>
  </header>

  <div class="grid">
    <div class="card"><h2>在线分机</h2><div class="kpi" id="kOnline">—</div></div>
    <div class="card"><h2>进行中通话</h2><div class="kpi" id="kCalls">—</div></div>
    <div class="card"><h2>服务运行</h2><div class="kpi" id="kUptime" style="font-size:15px;line-height:1.5">—</div></div>
  </div>

  <div class="card" style="margin-bottom:18px">
    <h2>分机</h2>
    <table><thead><tr><th>分机</th><th>名称</th><th>注册</th><th>来源</th><th>端点状态</th></tr></thead>
    <tbody id="extBody"><tr><td colspan="5" class="muted">加载中…</td></tr></tbody></table>
  </div>

  <div class="card" style="margin-bottom:18px">
    <h2>实时通话</h2>
    <table><thead><tr><th>通道</th><th>分机</th><th>状态</th><th>应用</th><th>数据</th></tr></thead>
    <tbody id="callBody"><tr><td colspan="5" class="muted">暂无通话</td></tr></tbody></table>
  </div>

  <div class="card">
    <h2>通话记录（最近 40 条）</h2>
    <table><thead><tr><th>开始时间</th><th>主叫</th><th>被叫</th><th>通话时长</th><th>结果</th></tr></thead>
    <tbody id="cdrBody"><tr><td colspan="5" class="muted">暂无记录</td></tr></tbody></table>
  </div>

  <div class="foot">智装客 · 云 SIP 服务 · Asterisk</div>
</div>

<script>
const $ = id => document.getElementById(id);
let timer = null;

async function api(action, opts = {}) {
  const r = await fetch('api.php?action=' + action, opts);
  return r.json();
}

function esc(s){ return String(s ?? '').replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c])); }

function fmtDur(sec){
  sec = parseInt(sec || 0, 10);
  if (!sec) return '<span class="muted">—</span>';
  const m = Math.floor(sec/60), s = sec%60;
  return `${m}分${String(s).padStart(2,'0')}秒`;
}

async function load() {
  try {
    const d = await api('status');
    if (!d.ok) throw new Error(d.msg || '加载失败');
    $('serverInfo').textContent = `SIP 服务器 ${d.sip_uri}   ·   ${d.version}`;
    $('ts').textContent = d.ts;
    $('kOnline').innerHTML = `${d.online} <small>/ ${d.extensions.length}</small>`;
    $('kCalls').innerHTML = `${d.calls.length} <small>路</small>`;
    $('kUptime').textContent = d.uptime || '—';

    $('extBody').innerHTML = d.extensions.map(e => `
      <tr>
        <td class="mono"><b>${esc(e.number)}</b></td>
        <td>${esc(e.name)}</td>
        <td>${e.registered
              ? (e.reachable
                  ? '<span class="tag ok"><span class="dot on"></span>在线</span>'
                  : '<span class="tag ok"><span class="dot on"></span>已注册</span>')
              : '<span class="tag"><span class="dot off"></span>离线</span>'}</td>
        <td class="mono muted">${esc(e.contact || '—')}</td>
        <td class="muted">${esc(e.state)}</td>
      </tr>`).join('');

    $('callBody').innerHTML = d.calls.length ? d.calls.map(c => `
      <tr>
        <td class="mono muted" style="max-width:280px;overflow:hidden;text-overflow:ellipsis">${esc(c.channel)}</td>
        <td class="mono"><b>${esc(c.exten)}</b></td>
        <td>${esc(c.state)}</td>
        <td class="muted">${esc(c.app)}</td>
        <td class="muted">${esc(c.data)}</td>
      </tr>`).join('') : '<tr><td colspan="5" class="muted">暂无通话</td></tr>';

    $('cdrBody').innerHTML = d.cdr.length ? d.cdr.map(r => `
      <tr>
        <td class="mono muted">${esc(r.start)}</td>
        <td class="mono">${esc(r.src)}</td>
        <td class="mono">${esc(r.dst)}</td>
        <td>${fmtDur(r.bill)}</td>
        <td>${r.disp === 'ANSWERED'
              ? '<span class="tag ok">已接听</span>'
              : '<span class="tag err">' + esc(r.disp) + '</span>'}</td>
      </tr>`).join('') : '<tr><td colspan="5" class="muted">暂无记录</td></tr>';
  } catch (e) {
    $('serverInfo').textContent = '⚠ ' + e.message;
  }
}

async function boot() {
  const w = await api('whoami');
  if (w.authed) { showApp(); } else { showLogin(); }
}

function showLogin(){
  $('login').style.display = 'block';
  $('app').style.display = 'none';
  $('pwd').focus();
}
function showApp(){
  $('login').style.display = 'none';
  $('app').style.display = 'block';
  load();
  clearInterval(timer);
  timer = setInterval(load, 5000);
}

$('doLogin').onclick = async () => {
  const body = new URLSearchParams({ password: $('pwd').value });
  const d = await api('login', { method:'POST', body });
  if (d.ok) { showApp(); } else { $('err').textContent = d.msg || '登录失败'; }
};
$('pwd').addEventListener('keydown', e => { if (e.key === 'Enter') $('doLogin').click(); });
$('refresh').onclick = load;
$('logout').onclick = async () => { await api('logout'); clearInterval(timer); showLogin(); };

boot();
</script>
</body>
</html>
