(() => {
  const errorEl = document.getElementById("error");
  const healthEl = document.getElementById("health");
  const statusFields = document.getElementById("status-fields");
  const pathInput = document.getElementById("file-path");
  const fileMeta = document.getElementById("file-meta");
  const fileRows = document.getElementById("file-rows");
  const shotGallery = document.getElementById("shot-gallery");
  const execOut = document.getElementById("exec-out");
  const execMeta = document.getElementById("exec-meta");

  let objectUrls = [];

  function showError(message) {
    errorEl.textContent = message || "";
    errorEl.classList.toggle("show", Boolean(message));
  }

  async function readError(res) {
    try {
      const data = await res.json();
      return data.error || res.statusText;
    } catch {
      return res.statusText || "request failed";
    }
  }

  async function api(url, options) {
    showError("");
    const res = await fetch(url, options);
    if (!res.ok) {
      throw new Error(await readError(res));
    }
    return res;
  }

  function formatBytes(n) {
    if (n < 1024) return `${n} B`;
    const units = ["KB", "MB", "GB", "TB"];
    let value = n / 1024;
    let i = 0;
    while (value >= 1024 && i < units.length - 1) {
      value /= 1024;
      i += 1;
    }
    return `${value.toFixed(value >= 10 ? 0 : 1)} ${units[i]}`;
  }

  function joinPath(base, name) {
    if (!base) return name;
    if (/[\\/]$/.test(base)) return base + name;
    return `${base}\\${name}`;
  }

  function parentPath(path) {
    const trimmed = String(path || "").replace(/[\\/]+$/, "");
    const idx = Math.max(trimmed.lastIndexOf("\\"), trimmed.lastIndexOf("/"));
    if (idx <= 2) return trimmed.slice(0, 3);
    return trimmed.slice(0, idx);
  }

  function revokeShots() {
    for (const url of objectUrls) URL.revokeObjectURL(url);
    objectUrls = [];
  }

  async function loadHealth() {
    try {
      const res = await api("/health");
      const data = await res.json();
      healthEl.textContent = data.status === "ok" ? "online" : data.status;
      healthEl.className = "badge ok";
    } catch (err) {
      healthEl.textContent = "offline";
      healthEl.className = "badge bad";
      showError(err.message);
    }
  }

  async function loadStatus() {
    const res = await api("/api/v1/status");
    const data = await res.json();
    const fields = [
      ["Hostname", data.hostname],
      ["User", data.user],
      ["OS", data.os],
      ["Architecture", data.architecture],
      ["Version", data.agent_version],
      ["Listen", data.listen_address],
      ["Uptime", `${data.uptime_seconds}s`],
      ["Local IPs", (data.local_ips || []).join(", ") || "—"],
    ];
    statusFields.replaceChildren(
      ...fields.flatMap(([label, value]) => {
        const dt = document.createElement("dt");
        dt.textContent = label;
        const dd = document.createElement("dd");
        dd.textContent = value || "—";
        return [dt, dd];
      })
    );
  }

  async function listFiles(path) {
    const query = new URLSearchParams();
    if (path) query.set("path", path);
    query.set("depth", "0");
    const res = await api(`/api/v1/files?${query}`);
    const data = await res.json();
    pathInput.value = data.path || "";
    const entries = data.entries || [];
    fileMeta.textContent = `${entries.length} entries`;
    fileRows.replaceChildren();
    for (const entry of entries) {
      const tr = document.createElement("tr");
      const name = document.createElement("td");
      name.className = entry.type === "dir" ? "name" : "name file";
      name.textContent = entry.name;
      name.addEventListener("click", () => {
        const full = joinPath(data.path, entry.name);
        if (entry.type === "dir") {
          listFiles(full).catch((err) => showError(err.message));
        } else {
          const link = document.createElement("a");
          link.href = `/api/v1/download?path=${encodeURIComponent(full)}`;
          link.download = entry.name;
          document.body.append(link);
          link.click();
          link.remove();
        }
      });
      const type = document.createElement("td");
      type.textContent = entry.type;
      const size = document.createElement("td");
      size.textContent = entry.type === "dir" ? "—" : formatBytes(entry.size || 0);
      const modified = document.createElement("td");
      modified.textContent = entry.modified_time ? new Date(entry.modified_time).toLocaleString() : "";
      tr.append(name, type, size, modified);
      fileRows.append(tr);
    }
  }

  async function captureScreen() {
    revokeShots();
    shotGallery.replaceChildren();
    const format = document.getElementById("shot-format").value;
    const quality = document.getElementById("shot-quality").value;
    const res = await api(`/api/v1/screenshot?format=${encodeURIComponent(format)}&quality=${encodeURIComponent(quality)}`);
    const contentType = res.headers.get("content-type") || "";
    if (contentType.includes("application/json")) {
      const data = await res.json();
      for (const image of data.images || []) {
        addShot(base64Blob(image.data_base64, image.content_type), `Monitor ${image.monitor}`);
      }
      return;
    }
    addShot(await res.blob(), "Display");
  }

  function base64Blob(data, contentType) {
    const binary = atob(data);
    const bytes = new Uint8Array(binary.length);
    for (let i = 0; i < binary.length; i += 1) {
      bytes[i] = binary.charCodeAt(i);
    }
    return new Blob([bytes], { type: contentType || "application/octet-stream" });
  }

  function addShot(blob, caption) {
    const url = URL.createObjectURL(blob);
    objectUrls.push(url);
    const figure = document.createElement("figure");
    const img = document.createElement("img");
    img.src = url;
    img.alt = caption;
    const figcaption = document.createElement("figcaption");
    figcaption.className = "meta";
    figcaption.textContent = caption;
    figure.append(img, figcaption);
    shotGallery.append(figure);
  }

  async function runCommand() {
    const command = document.getElementById("exec-command").value.trim();
    const timeout = Number(document.getElementById("exec-timeout").value);
    const res = await api("/api/v1/exec", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ command, timeout_sec: timeout }),
    });
    const data = await res.json();
    execMeta.textContent = `exit code ${data.exit_code}`;
    const stdout = data.stdout || "";
    const stderr = data.stderr || "";
    execOut.textContent = [stdout, stderr && `STDERR:\n${stderr}`].filter(Boolean).join("\n\n");
  }

  document.querySelectorAll("nav button").forEach((button) => {
    button.addEventListener("click", () => {
      document.querySelectorAll("nav button").forEach((item) => item.classList.remove("active"));
      document.querySelectorAll(".panel").forEach((panel) => panel.classList.remove("active"));
      button.classList.add("active");
      document.getElementById(button.dataset.tab).classList.add("active");
    });
  });

  document.getElementById("refresh-status").addEventListener("click", () => {
    Promise.all([loadHealth(), loadStatus()]).catch((err) => showError(err.message));
  });
  document.getElementById("file-list").addEventListener("click", () => {
    listFiles(pathInput.value.trim()).catch((err) => showError(err.message));
  });
  document.getElementById("file-up").addEventListener("click", () => {
    listFiles(parentPath(pathInput.value.trim())).catch((err) => showError(err.message));
  });
  document.getElementById("shot-capture").addEventListener("click", () => {
    captureScreen().catch((err) => showError(err.message));
  });
  document.getElementById("exec-run").addEventListener("click", () => {
    runCommand().catch((err) => showError(err.message));
  });

  Promise.all([loadHealth(), loadStatus(), listFiles("")]).catch((err) => showError(err.message));
})();
