document.addEventListener("DOMContentLoaded", () => {
  const output = document.getElementById("console");
  const triggerType = document.getElementById("trigger_type");
  const scheduleKind = document.getElementById("schedule_kind");
  const addCommandButton = document.getElementById("add-command-step");
  const commandSteps = document.getElementById("command-steps");
  const nodeLive = document.querySelector(".node-live");
  const dashboardNodes = document.getElementById("dashboard-nodes");
  const dashboardSearch = document.getElementById("dashboard-search");
  const authToggle = document.querySelector("[data-auth-toggle]");

  const attachXtermConsole = (consoleOutput) => {
    const wsPath = consoleOutput.dataset.ws;
    if (!wsPath || typeof Terminal === "undefined") {
      return;
    }

    const term = new Terminal({
      cursorBlink: true,
      convertEol: true,
      fontFamily: '"Cascadia Code", "SFMono-Regular", Consolas, "Liberation Mono", monospace',
      fontSize: 14,
      lineHeight: 1.25,
      theme: {
        background: "#0b1220",
        foreground: "#dbeafe",
        cursor: "#93c5fd",
        cursorAccent: "#0b1220",
        selectionBackground: "rgba(147, 197, 253, 0.28)",
      },
    });
    const fitAddon = typeof FitAddon !== "undefined" ? new FitAddon.FitAddon() : null;
    if (fitAddon) {
      term.loadAddon(fitAddon);
    }
    term.open(consoleOutput);

    const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
    const socket = new WebSocket(`${protocol}//${window.location.host}${wsPath}`);
    const fit = () => {
      fitAddon?.fit();
      term.focus();
    };

    window.setTimeout(fit, 0);
    window.addEventListener("resize", fit);
    if (typeof ResizeObserver !== "undefined") {
      const observer = new ResizeObserver(fit);
      observer.observe(consoleOutput);
    }

    socket.addEventListener("message", (event) => {
      term.write(event.data);
    });

    socket.addEventListener("open", () => {
      fit();
    });

    socket.addEventListener("close", () => {
      term.write("\r\n[session closed]\r\n");
    });

    socket.addEventListener("error", () => {
      term.write("\r\n[connection error]\r\n");
    });

    term.onData((value) => {
      if (socket.readyState === WebSocket.OPEN) {
        socket.send(value);
      }
    });
  };

  if (output?.dataset.xterm === "true") {
    attachXtermConsole(output);
  }

  const syncScheduleUi = () => {
    if (!triggerType || !scheduleKind) {
      return;
    }

    const schedulePanel = document.querySelector(".schedule-panel");
    const weeklyRows = document.querySelectorAll(".weekly-only");
    const intervalRows = document.querySelectorAll(".interval-only");
    const showSchedule = triggerType.value !== "triggered";
    const showWeekly = showSchedule && scheduleKind.value === "weekly";
    const showInterval = showSchedule && scheduleKind.value === "interval";

    if (schedulePanel) {
      schedulePanel.classList.toggle("opacity-50", !showSchedule);
    }
    weeklyRows.forEach((row) => row.classList.toggle("d-none", !showWeekly));
    intervalRows.forEach((row) => row.classList.toggle("d-none", !showInterval));
  };

  triggerType?.addEventListener("change", syncScheduleUi);
  scheduleKind?.addEventListener("change", syncScheduleUi);
  syncScheduleUi();

  addCommandButton?.addEventListener("click", () => {
    if (!commandSteps) {
      return;
    }

    const nextIndex = commandSteps.querySelectorAll(".command-step").length + 1;
    const wrapper = document.createElement("div");
    wrapper.className = "input-group command-step";
    wrapper.innerHTML = `
      <span class="input-group-text">${nextIndex}</span>
      <input type="text" class="form-control" name="command_steps[]" placeholder="command ${nextIndex}" required>
      <button class="btn btn-outline-danger remove-command-step" type="button">Remove</button>
    `;
    commandSteps.appendChild(wrapper);
  });

  commandSteps?.addEventListener("click", (event) => {
    const target = event.target;
    if (!(target instanceof HTMLElement) || !target.classList.contains("remove-command-step")) {
      return;
    }

    const steps = commandSteps.querySelectorAll(".command-step");
    if (steps.length <= 1) {
      return;
    }

    target.closest(".command-step")?.remove();
    commandSteps.querySelectorAll(".command-step").forEach((step, index) => {
      const badge = step.querySelector(".input-group-text");
      if (badge) {
        badge.textContent = String(index + 1);
      }
    });
  });

  if (nodeLive instanceof HTMLElement) {
    const nodeId = nodeLive.dataset.nodeId;
    const history = {
      cpu: [],
      ram: [],
      disk: [],
      uptime: [],
    };

    const uptimeLabel = (seconds) => {
      const days = Math.floor(seconds / 86400);
      const hours = Math.floor((seconds % 86400) / 3600);
      const minutes = Math.floor((seconds % 3600) / 60);
      if (days > 0) return `${days}d ${hours}h ${minutes}m`;
      return `${hours}h ${minutes}m`;
    };

    const renderSparkline = (values) => {
      if (values.length === 0) {
        return "";
      }
      const max = Math.max(...values, 1);
      const points = values.map((value, index) => {
        const x = (index / Math.max(values.length - 1, 1)) * 100;
        const y = 100 - (value / max) * 80 - 10;
        return `${x},${y}`;
      }).join(" ");

      return `
        <svg viewBox="0 0 100 100" preserveAspectRatio="none">
          <polyline points="${points}" fill="none" stroke="rgba(255,255,255,0.28)" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"></polyline>
        </svg>
      `;
    };

    const pushValue = (key, value) => {
      history[key].push(value);
      if (history[key].length > 18) {
        history[key].shift();
      }
      const card = nodeLive.querySelector(`.kpi-card[data-kpi="${key}"] .kpi-graph`);
      if (card) {
        card.innerHTML = renderSparkline(history[key]);
      }
    };

    const updateNodeStats = async () => {
      if (!nodeId) {
        return;
      }
      try {
        const response = await fetch(`/nodes/${nodeId}/stats`, { headers: { Accept: "application/json" } });
        if (!response.ok) {
          return;
        }
        const stats = await response.json();
        const cpu = Number(stats.cpu_usage || 0);
        const ram = Number(stats.ram_usage || 0);
        const disk = Number(stats.disk_usage || 0);
        const uptime = Number(stats.uptime_seconds || 0);

        const cpuValue = nodeLive.querySelector('[data-kpi-value="cpu"]');
        const ramValue = nodeLive.querySelector('[data-kpi-value="ram"]');
        const diskValue = nodeLive.querySelector('[data-kpi-value="disk"]');
        const uptimeValue = nodeLive.querySelector('[data-kpi-value="uptime"]');

        if (cpuValue) cpuValue.textContent = `${cpu.toFixed(1)}%`;
        if (ramValue) ramValue.textContent = `${ram.toFixed(1)}%`;
        if (diskValue) diskValue.textContent = `${disk.toFixed(1)}%`;
        if (uptimeValue) uptimeValue.textContent = uptimeLabel(uptime);

        pushValue("cpu", cpu);
        pushValue("ram", ram);
        pushValue("disk", disk);
        pushValue("uptime", Math.min(uptime / 3600, 100));
      } catch (_) {
        // Ignore transient poll failures.
      }
    };

    updateNodeStats();
    window.setInterval(updateNodeStats, 1000);
  }

  if (dashboardNodes instanceof HTMLElement) {
    const url = dashboardNodes.dataset.dashboardNodesUrl;
    if (url) {
      fetch(url, { headers: { Accept: "text/html" } })
        .then((response) => {
          if (!response.ok) {
            throw new Error("failed");
          }
          return response.text();
        })
        .then((html) => {
          dashboardNodes.innerHTML = html;
          dashboardNodes.classList.add("is-loaded");
          dashboardSearch?.dispatchEvent(new Event("input"));
        })
        .catch(() => {
          dashboardNodes.innerHTML = `
            <div class="card border-0 shadow-sm">
              <div class="card-body py-5 text-center text-body-secondary">
                Failed to load nodes.
              </div>
            </div>
          `;
        });
    }
  }

  dashboardSearch?.addEventListener("input", () => {
    const query = dashboardSearch.value.trim().toLowerCase();
    document.querySelectorAll(".dashboard-node-card").forEach((card) => {
      if (!(card instanceof HTMLElement)) {
        return;
      }
      const haystack = card.dataset.nodeSearch || "";
      const matches = query === "" || haystack.includes(query);
      card.classList.toggle("d-none", !matches);
    });
  });

  if (authToggle instanceof HTMLElement) {
    const passwordRadio = document.getElementById("authModePassword");
    const keyRadio = document.getElementById("authModeKey");
    const passwordPanel = document.querySelector('[data-auth-panel="password"]');
    const keyPanel = document.querySelector('[data-auth-panel="key"]');
    const passwordInput = document.querySelector('[data-auth-input="password"]');
    const keyInput = document.querySelector('[data-auth-input="key"]');

    const syncAuthMode = () => {
      const passwordMode = passwordRadio instanceof HTMLInputElement && passwordRadio.checked;

      passwordPanel?.classList.toggle("d-none", !passwordMode);
      keyPanel?.classList.toggle("d-none", passwordMode);

      if (passwordInput instanceof HTMLInputElement) {
        passwordInput.disabled = !passwordMode;
        passwordInput.required = passwordMode;
        if (!passwordMode) {
          passwordInput.value = "";
        }
      }

      if (keyInput instanceof HTMLInputElement) {
        keyInput.disabled = passwordMode;
        keyInput.required = !passwordMode;
        if (passwordMode) {
          keyInput.value = "";
        }
      }
    };

    passwordRadio?.addEventListener("change", syncAuthMode);
    keyRadio?.addEventListener("change", syncAuthMode);
    syncAuthMode();
  }

  if (typeof bootstrap !== "undefined") {
    document.querySelectorAll('[data-bs-toggle-tooltip="tooltip"]').forEach((element) => {
      new bootstrap.Tooltip(element);
    });
  }
});
