{% load i18n %}
{% trans "log lines" as nb_job_log_lines_suffix %}
{% trans "Copy logs" as nb_copy_logs_label %}
{% trans "Copied!" as nb_copied_label %}
<div class="alert alert-info alert-dismissible" role="alert">
  <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="{% trans "Close" %}"></button>
  <div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-2">
    <div class="d-flex flex-wrap align-items-center gap-2">
      <strong>{% trans "Live job updates" %}</strong>
      <span id="nb-job-live-status-{{ job.pk }}"></span>
    </div>
    <button
      type="button"
      class="btn btn-sm btn-outline-secondary"
      id="nb-job-copy-logs-{{ job.pk }}"
      data-label-copy="{{ nb_copy_logs_label|escape }}"
      data-label-copied="{{ nb_copied_label|escape }}"
    >
      {{ nb_copy_logs_label }}
    </button>
  </div>
  <div class="nb-job-progress-wrap mt-2" style="display:none">
    <div class="progress" style="height: 1.35rem;" role="progressbar"
         aria-label="{% trans "Sync progress" %}"
         aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
      <div
        id="nb-job-progress-bar-{{ job.pk }}"
        class="progress-bar bg-info progress-bar-striped progress-bar-animated"
        style="width: 0%; min-width: 2rem; transition: width 0.3s ease;"
      >
        <span class="nb-job-progress-label small fw-semibold text-white"></span>
      </div>
    </div>
  </div>
  <div id="nb-job-live-log-{{ job.pk }}" class="nb-job-live-log mt-2"></div>
</div>
<script>
(function () {
  var pk = {{ job.pk|default:"0" }};
  var statusEl = document.getElementById("nb-job-live-status-" + pk);
  var logEl = document.getElementById("nb-job-live-log-" + pk);
  var copyBtn = document.getElementById("nb-job-copy-logs-" + pk);
  if (!statusEl) return;
  var terminal = { completed: 1, errored: 1, failed: 1 };
  var timer = null;
  var logSuffix = " {{ nb_job_log_lines_suffix|escapejs }}";
  var lastEntries = [];
  var progressBarEl = document.getElementById("nb-job-progress-bar-" + pk);
  var sseSource = null;
  var isStreaming = false;

  function jobStatusParts(status) {
    if (status && typeof status === "object") {
      var v = status.value != null ? String(status.value) : "";
      var lbl = status.label != null ? String(status.label) : v;
      return { value: v, label: lbl };
    }
    var s = status != null ? String(status) : "";
    return { value: s, label: s };
  }

  function appendLog(message, eventType, data) {
    if (!logEl) return;
    var api = window.NbProxboxJobLogView;
    var entry = {
      timestamp: new Date().toISOString(),
      level: eventType || "info",
      message: message
    };
    if (!lastEntries) lastEntries = [];
    lastEntries.push(entry);
    if (lastEntries.length > 200) lastEntries.shift();
      if (api && typeof api.renderLiveLog === "function") {
      logEl.replaceChildren();
      api.renderLiveLog(logEl, lastEntries);
    } else {
      var span = document.createElement("div");
      span.className = "nb-job-live-log-entry";
      span.textContent = entry.timestamp + " [" + entry.level + "] " + entry.message;
      logEl.appendChild(span);
    }
    logEl.scrollTop = logEl.scrollHeight;
  }

  function updateProgress(progress) {
    if (!progressBarEl) return;
    var wrap = progressBarEl.parentElement.parentElement;
    if (wrap && wrap.classList.contains("nb-job-progress-wrap")) {
      wrap.style.display = "";
    }
    var pct = progress.percent !== undefined ? progress.percent : 0;
    progressBarEl.style.width = pct + "%";
    progressBarEl.setAttribute("aria-valuenow", pct);
    var label = progressBarEl.querySelector(".nb-job-progress-label");
    if (label) {
      if (progress.stage) {
        label.textContent = progress.stage + " (" + progress.current + "/" + progress.total + ")";
      } else {
        label.textContent = progress.current + "/" + progress.total;
      }
    }
  }

  function handleSSEProgress(data) {
    var progress = data.progress;
    var message = data.message || "";
    var status = data.status || "";
    
    appendLog(message, "info", data);
    
    if (progress) {
      updateProgress(progress);
    }
    
    if (status === "completed" || status === "failed") {
      if (progressBarEl) {
        progressBarEl.classList.remove("progress-bar-striped", "progress-bar-animated");
        if (status === "completed") {
          progressBarEl.classList.remove("bg-info");
          progressBarEl.classList.add("bg-success");
          var label = progressBarEl.querySelector(".nb-job-progress-label");
          if (label) label.textContent = "Done";
        } else {
          progressBarEl.classList.remove("bg-info");
          progressBarEl.classList.add("bg-danger");
        }
      }
      if (sseSource) {
        sseSource.close();
        sseSource = null;
      }
      isStreaming = false;
    }
  }

  function startSSE() {
    if (isStreaming) return;
    var streamUrl = "{% url 'plugins:netbox_proxbox:job_stream' pk=job.pk|default:0 %}";
    if (!pk || streamUrl.indexOf("0") > -1) {
      startPollingFallback();
      return;
    }
    isStreaming = true;
    try {
      sseSource = new EventSource(streamUrl);
      sseSource.addEventListener("step", function(e) {
        try {
          var data = JSON.parse(e.data);
          if (data.step === "progress" || data.step === "job") {
            handleSSEProgress(data);
          }
        } catch (err) {}
      });
      sseSource.addEventListener("error", function(e) {
        isStreaming = false;
        if (sseSource) {
          sseSource.close();
          sseSource = null;
        }
        startPollingFallback();
      });
      sseSource.addEventListener("complete", function(e) {
        isStreaming = false;
        if (sseSource) {
          sseSource.close();
          sseSource = null;
        }
      });
      statusEl.textContent = " — Streaming...";
    } catch (err) {
      isStreaming = false;
      startPollingFallback();
    }
  }

  function applyLog(entries) {
    if (!logEl) return;
    var api = window.NbProxboxJobLogView;
    lastEntries = entries;
      if (api && typeof api.renderLiveLog === "function") {
      api.renderLiveLog(logEl, lastEntries);
    } else {
      logEl.textContent = "";
    }
  }

  function tick() {
    fetch("/api/core/jobs/" + pk + "/", {
      credentials: "same-origin",
      headers: { Accept: "application/json" },
    })
      .then(function (r) {
        if (!r.ok) throw new Error("poll failed");
        return r.json();
      })
      .then(function (d) {
        var st = jobStatusParts(d.status);
        var entries = Array.isArray(d.log_entries) ? d.log_entries : [];
        lastEntries = entries;
        var n = entries.length;
        var display = st.label || st.value || "—";
        var detail =
          st.value && st.label && st.label !== st.value ? " (" + st.value + ")" : "";
        statusEl.textContent = " — " + display + detail + " · " + n + logSuffix;
        applyLog(entries);
        var api = window.NbProxboxJobLogView;
        if (api && typeof api.applyProgress === "function") {
          api.applyProgress(progressBarEl, d, entries);
        }
        if (terminal[st.value]) {
          if (timer) clearInterval(timer);
          timer = null;
          if (progressBarEl) {
            progressBarEl.classList.remove("progress-bar-striped", "progress-bar-animated");
            if (st.value === "errored" || st.value === "failed") {
              progressBarEl.classList.remove("bg-info", "bg-success");
              progressBarEl.classList.add("bg-danger");
            }
          }
        }
      })
      .catch(function () {});
  }

  function startPollingFallback() {
    tick();
    timer = setInterval(tick, 2500);
  }

  function startPolling() {
    startSSE();
    setTimeout(function() {
      if (!isStreaming) startPollingFallback();
    }, 1000);
  }

  if (copyBtn) {
    copyBtn.addEventListener("click", function () {
      var api = window.NbProxboxJobLogView;
      var text =
        api && typeof api.formatLogEntriesForClipboard === "function"
          ? api.formatLogEntriesForClipboard(lastEntries)
          : "";
      var labelCopy = copyBtn.getAttribute("data-label-copy") || "Copy logs";
      var labelCopied = copyBtn.getAttribute("data-label-copied") || "Copied!";

      function restoreLabel() {
        copyBtn.textContent = labelCopy;
      }

      function ok() {
        copyBtn.textContent = labelCopied;
        window.setTimeout(restoreLabel, 2000);
      }

      if (!text) {
        return;
      }

      if (navigator.clipboard && navigator.clipboard.writeText) {
        navigator.clipboard.writeText(text).then(ok).catch(function () {
          fallbackCopy(text, ok);
        });
      } else {
        fallbackCopy(text, ok);
      }
    });
  }

  function fallbackCopy(text, onOk) {
    var ta = document.createElement("textarea");
    ta.value = text;
    ta.setAttribute("readonly", "");
    ta.style.position = "fixed";
    ta.style.left = "-9999px";
    document.body.appendChild(ta);
    ta.select();
    try {
      if (document.execCommand("copy")) {
        onOk();
      }
    } catch (e) {}
    document.body.removeChild(ta);
  }

  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", startPolling);
  } else {
    startPolling();
  }
})();
</script>