// dms-ui front-end controller. Vanilla JS, no build step.

const $ = (sel, root = document) => root.querySelector(sel);
const $$ = (sel, root = document) => Array.from(root.querySelectorAll(sel));

const state = {
  host: null,
  hosts: [],
  logSource: null,
  logPaused: false,
};

// ---------- API helper ----------------------------------------------------
async function api(path, opts = {}) {
  const res = await fetch(path, {
    credentials: "include",
    headers: { "Content-Type": "application/json", ...(opts.headers || {}) },
    ...opts,
  });
  if (res.status === 401) {
    showLogin();
    throw new Error("not authenticated");
  }
  const ct = res.headers.get("content-type") || "";
  const body = ct.includes("application/json") ? await res.json() : await res.text();
  if (!res.ok) {
    const msg = (body && body.detail) || res.statusText;
    toast(`[${res.status}] ${msg}`, true);
    throw new Error(msg);
  }
  return body;
}

const get  = (p)        => api(p);
const post = (p, body)  => api(p, { method: "POST", body: JSON.stringify(body || {}) });

// ---------- toast ---------------------------------------------------------
let toastTimer;
function toast(msg, isError = false) {
  const el = $("#toast");
  el.textContent = msg;
  el.className = `fixed bottom-4 right-4 text-sm rounded px-3 py-2 shadow-lg border ${
    isError ? "bg-rose-950 border-rose-700 text-rose-200"
            : "bg-slate-800 border-slate-700 text-slate-100"
  }`;
  el.classList.remove("hidden");
  clearTimeout(toastTimer);
  toastTimer = setTimeout(() => el.classList.add("hidden"), 3500);
}

// ---------- login / logout ------------------------------------------------
function showLogin() {
  $("#login").classList.remove("hidden");
  $("#app").classList.add("hidden");
}
function showApp() {
  $("#login").classList.add("hidden");
  $("#app").classList.remove("hidden");
}

$("#loginForm").addEventListener("submit", async (e) => {
  e.preventDefault();
  const password = $("#loginPassword").value;
  try {
    await post("/api/login", { password });
    showApp();
    await loadHosts();
    activateTab("dashboard");
  } catch (err) {
    $("#loginError").textContent = "Invalid password · 密码错误";
    $("#loginError").classList.remove("hidden");
  }
});

$("#logoutBtn").addEventListener("click", async () => {
  await post("/api/logout");
  showLogin();
  $("#loginPassword").value = "";
});

// ---------- hosts + tab nav ----------------------------------------------
async function loadHosts() {
  const r = await get("/api/hosts");
  state.hosts = r.data || [];
  const sel = $("#hostSelect");
  sel.innerHTML = state.hosts
    .map(h => `<option value="${h.name}">${h.name} (${h.ssh_alias})</option>`)
    .join("");
  if (state.hosts.length) {
    state.host = state.hosts[0].name;
    sel.value = state.host;
  }
}
$("#hostSelect").addEventListener("change", (e) => {
  state.host = e.target.value;
  activateTab(currentTab());
});

let _currentTab = "dashboard";
function currentTab() { return _currentTab; }

$$(".tab").forEach(btn => {
  btn.addEventListener("click", () => activateTab(btn.dataset.tab));
});

function activateTab(name) {
  _currentTab = name;
  $$(".tab").forEach(b => b.classList.toggle("active", b.dataset.tab === name));
  $$("[data-pane]").forEach(p => p.classList.toggle("hidden", p.dataset.pane !== name));
  if (name === "dashboard") loadDashboard();
  if (name === "accounts")  loadAccounts();
  if (name === "aliases")   loadAliases();
  if (name === "quotas")    loadQuotas();
  if (name === "queue")     loadQueue();
  if (name === "logs")      startLogs();
}

// ---------- dashboard -----------------------------------------------------
async function loadDashboard() {
  const r = await get(`/api/dashboard?host=${encodeURIComponent(state.host)}`);
  const d = r.data || {};
  const running = d.container && d.container.running;
  $("#dashContainer").innerHTML = running
    ? '<span class="dot green"></span>running · 运行中'
    : '<span class="dot red"></span>stopped · 未运行';
  $("#dashQueue").textContent = d.queue_depth ?? "0";
  $("#dashAccounts").textContent = d.account_count ?? "0";
  $("#dashDisk").textContent = (d.disk || "").replace(/\s+/g, " ");
  $("#dashLog").textContent = d.log_tail || "(empty · 空)";
}

// ---------- accounts ------------------------------------------------------
async function loadAccounts() {
  const r = await get(`/api/accounts?host=${encodeURIComponent(state.host)}`);
  const rows = (r.data || []).map(a => {
    const email = a.email;
    return `<tr>
      <td class="py-1.5 font-mono">${email}</td>
      <td class="space-x-1">
        <button class="row-btn primary" onclick="updatePwd('${email}')">Update · 改密</button>
        <button class="row-btn ghost"   onclick="restrict('${email}', true)">Restrict · 锁定</button>
        <button class="row-btn ghost"   onclick="restrict('${email}', false)">Unrestrict · 解锁</button>
        <button class="row-btn ghost"   onclick="setQuota('${email}')">Set quota · 设配额</button>
        <button class="row-btn ghost"   onclick="delQuota('${email}')">Del quota · 删配额</button>
        <button class="row-btn danger"  onclick="delAccount('${email}')">Delete · 删除</button>
      </td>
    </tr>`;
  }).join("");
  $("#accountsBody").innerHTML = rows || `<tr><td colspan="2" class="text-slate-500 py-2">no accounts · 无账号</td></tr>`;
}

$("#addAccountForm").addEventListener("submit", async (e) => {
  e.preventDefault();
  const fd = new FormData(e.target);
  const body = {
    host: state.host,
    email: fd.get("email"),
    password: fd.get("password"),
    quota: fd.get("quota") || "",
  };
  const r = await post("/api/accounts", body);
  $("#addResult").textContent = r.ok ? "Created · 已创建" : `Failed: ${r.error}`;
  if (r.ok) {
    e.target.reset();
    updatePwdMeter("");
    loadAccounts();
  }
});

function updatePwdMeter(pwd) {
  let score = 0;
  if (pwd.length >= 8) score++;
  if (/[A-Z]/.test(pwd)) score++;
  if (/[a-z]/.test(pwd)) score++;
  if (/[0-9]/.test(pwd)) score++;
  if (/[^A-Za-z0-9]/.test(pwd)) score++;
  const pct = Math.min(100, (score / 5) * 100);
  const bar = $("#pwdStrength");
  bar.style.width = pct + "%";
  bar.className = "h-full transition-all " + (
    score < 2 ? "bg-rose-500" :
    score < 4 ? "bg-amber-500" : "bg-emerald-500"
  );
  $("#pwdHint").textContent = ["", "very weak · 极弱", "weak · 弱", "ok · 一般", "good · 良好", "strong · 强"][score] + " · strength";
}
$("#addPassword").addEventListener("input", (e) => updatePwdMeter(e.target.value));

async function updatePwd(email) {
  const pwd = prompt(`New password for ${email} · 新密码`);
  if (!pwd) return;
  await post("/api/accounts/update", { host: state.host, email, password: pwd });
  toast("Password updated · 密码已更新");
}
async function restrict(email, flag) {
  await post("/api/accounts/restrict", { host: state.host, email, restrict: flag });
  toast(flag ? "Restricted · 已锁定" : "Unrestricted · 已解锁");
}
async function delAccount(email) {
  if (!confirm(`Delete ${email}? · 确定删除?`)) return;
  await post("/api/accounts/delete", { host: state.host, email });
  loadAccounts();
}

// ---------- aliases -------------------------------------------------------
async function loadAliases() {
  const r = await get(`/api/aliases?host=${encodeURIComponent(state.host)}`);
  const rows = (r.data || []).map(a => `<tr>
    <td class="py-1.5 font-mono">${a.source}</td>
    <td class="py-1.5 font-mono">${a.target}</td>
    <td><button class="row-btn danger" onclick="delAlias('${a.source}','${a.target}')">Delete · 删除</button></td>
  </tr>`).join("");
  $("#aliasesBody").innerHTML = rows || `<tr><td colspan="3" class="text-slate-500 py-2">no aliases · 无别名</td></tr>`;
}
$("#addAliasForm").addEventListener("submit", async (e) => {
  e.preventDefault();
  const fd = new FormData(e.target);
  await post("/api/aliases", {
    host: state.host,
    source: fd.get("source"),
    target: fd.get("target"),
  });
  e.target.reset();
  loadAliases();
});
async function delAlias(source, target) {
  if (!confirm(`Delete alias ${source} -> ${target}?`)) return;
  await post("/api/aliases/delete", { host: state.host, source, target });
  loadAliases();
}

// ---------- quotas --------------------------------------------------------
async function loadQuotas() {
  const r = await get(`/api/quotas?host=${encodeURIComponent(state.host)}`);
  const data = r.data || { accounts: [], quotas: {} };
  const rows = data.accounts.map(email => `<tr>
    <td class="py-1.5 font-mono">${email}</td>
    <td class="py-1.5 font-mono">${data.quotas[email] || "(none)"}</td>
    <td><input id="q-${cssId(email)}" placeholder="5G" class="w-20" /></td>
    <td>
      <button class="row-btn primary" onclick="setQuotaInline('${email}')">Set · 设置</button>
      <button class="row-btn danger"  onclick="delQuota('${email}')">Delete · 删除</button>
    </td>
  </tr>`).join("");
  $("#quotasBody").innerHTML = rows || `<tr><td colspan="4" class="text-slate-500 py-2">no accounts · 无账号</td></tr>`;
}
function cssId(email) { return email.replace(/[^a-z0-9]/gi, "_"); }
async function setQuotaInline(email) {
  const val = $("#q-" + cssId(email)).value.trim();
  if (!val) return toast("Enter quota (e.g. 5G) · 请输入配额", true);
  await post("/api/quotas", { host: state.host, email, quota: val });
  loadQuotas();
}
async function setQuota(email) {
  const val = prompt(`Quota for ${email} (e.g. 5G) · 配额`);
  if (!val) return;
  await post("/api/quotas", { host: state.host, email, quota: val });
  loadQuotas();
}
async function delQuota(email) {
  if (!confirm(`Delete quota for ${email}?`)) return;
  await post("/api/quotas/delete", { host: state.host, email });
  loadQuotas();
}

// ---------- queue ---------------------------------------------------------
async function loadQueue() {
  const r = await get(`/api/queue?host=${encodeURIComponent(state.host)}`);
  $("#queueOut").textContent = (r.stdout || "").trim() || "(queue empty · 队列为空)";
}
async function flushQueue() {
  if (!confirm("Flush queue? · 立即投递所有队列邮件?")) return;
  await post(`/api/queue/flush?host=${encodeURIComponent(state.host)}`);
  loadQueue();
}

// ---------- logs (SSE) ----------------------------------------------------
function startLogs() {
  if (state.logSource) state.logSource.close();
  const out = $("#logOut");
  out.textContent = "";
  state.logPaused = false;
  $("#logPause").textContent = "Pause · 暂停";
  const url = `/api/logs/tail?host=${encodeURIComponent(state.host)}&lines=200`;
  const es = new EventSource(url, { withCredentials: true });
  state.logSource = es;

  es.addEventListener("snapshot", (ev) => {
    out.textContent = ev.data;
    out.scrollTop = out.scrollHeight;
  });
  es.addEventListener("line", (ev) => {
    if (state.logPaused) return;
    const needle = ($("#logSearch").value || "").toLowerCase();
    const text = ev.data;
    if (needle && !text.toLowerCase().includes(needle)) return;
    out.textContent += text;
    out.scrollTop = out.scrollHeight;
  });
  es.onerror = () => {
    // EventSource will auto-reconnect; show a hint.
    toast("Log stream interrupted, retrying... · 日志流中断，重试中", true);
  };
}
$("#logPause").addEventListener("click", () => {
  state.logPaused = !state.logPaused;
  $("#logPause").textContent = state.logPaused ? "Resume · 继续" : "Pause · 暂停";
});
$("#logSearch").addEventListener("input", () => {
  // Re-render is implicit on the next stream chunk; no-op for now.
});

// ---------- initial probe -------------------------------------------------
(async function init() {
  try {
    await get("/api/hosts");  // 200 if already logged in
    showApp();
    await loadHosts();
    activateTab("dashboard");
  } catch (_e) {
    showLogin();
  }
})();
