const form = document.querySelector("#archive-form");
const input = document.querySelector("#archive-url");
const button = document.querySelector("#archive-submit");
const progressWrap = document.querySelector("#progress-wrap");
const progressBar = document.querySelector("#progress-bar");
const statusLine = document.querySelector("#status-line");

let pollTimer = null;
let visualTimer = null;
let startedAt = Date.now();

form.addEventListener("submit", (event) => {
  event.preventDefault();
  submitArchive(input.value);
});

const pathUrl = urlFromPath();
if (pathUrl) {
  input.value = pathUrl;
  submitArchive(pathUrl);
} else {
  input.focus();
}

async function submitArchive(rawUrl) {
  stopTimers();
  setBusy(true);
  setStatus("Checking", 8);

  try {
    const response = await fetch("/api/archives", {
      method: "POST",
      headers: {
        "content-type": "application/json"
      },
      body: JSON.stringify({ url: rawUrl })
    });
    const data = await readApiResponse(response);
    if (data.archive?.archiveUrl) {
      openArchive(data.archive.archiveUrl);
      return;
    }
    if (data.job?.id) {
      watchJob(data.job);
      return;
    }
    throw new Error(data.error || "Archive did not start");
  } catch (error) {
    setError(error.message || "Archive failed");
    setBusy(false);
  }
}

function watchJob(job) {
  startedAt = Date.parse(job.startedAt || job.createdAt) || Date.now();
  updateFromJob(job);
  visualTimer = window.setInterval(updateVisualProgress, 250);
  pollTimer = window.setInterval(async () => {
    try {
      const response = await fetch(`/api/jobs/${encodeURIComponent(job.id)}`);
      const data = await readApiResponse(response);
      updateFromJob(data.job);
    } catch (error) {
      stopTimers();
      setError(error.message || "Archive failed");
      setBusy(false);
    }
  }, 850);
}

function updateFromJob(job) {
  if (job.status === "done" && job.archive?.archiveUrl) {
    stopTimers();
    setStatus("Opening", 100);
    openArchive(job.archive.archiveUrl);
    return;
  }

  if (job.status === "failed") {
    stopTimers();
    setError(job.error || "Archive failed");
    setBusy(false);
    return;
  }

  startedAt = Date.parse(job.startedAt || job.createdAt) || startedAt;
  const elapsed = Math.max(0, Math.round((Date.now() - startedAt) / 1000));
  const label = job.status === "queued" ? "Queued" : `Archiving ${elapsed}s`;
  setStatus(label, optimisticProgress());
}

function updateVisualProgress() {
  if (!progressWrap.hidden) {
    progressBar.style.width = `${optimisticProgress()}%`;
  }
}

function optimisticProgress() {
  const elapsed = Math.max(0, (Date.now() - startedAt) / 1000);
  if (elapsed < 1) {
    return 12;
  }
  if (elapsed < 12) {
    return Math.min(88, 12 + elapsed * 6.3);
  }
  return Math.min(96, 88 + (elapsed - 12) * 0.6);
}

async function readApiResponse(response) {
  const data = await response.json().catch(() => null);
  if (!response.ok || data?.ok === false) {
    throw new Error(data?.error || `Request failed with ${response.status}`);
  }
  return data;
}

function setBusy(isBusy) {
  button.disabled = isBusy;
  input.readOnly = isBusy;
}

function setStatus(text, progress) {
  progressWrap.hidden = false;
  statusLine.classList.remove("error");
  statusLine.textContent = text;
  progressBar.style.width = `${Math.max(0, Math.min(100, progress))}%`;
}

function setError(text) {
  progressWrap.hidden = false;
  statusLine.classList.add("error");
  statusLine.textContent = text;
  progressBar.style.width = "100%";
}

function stopTimers() {
  if (pollTimer) {
    window.clearInterval(pollTimer);
    pollTimer = null;
  }
  if (visualTimer) {
    window.clearInterval(visualTimer);
    visualTimer = null;
  }
}

function openArchive(archiveUrl) {
  window.location.assign(archiveUrl);
}

function urlFromPath() {
  const rawPath = window.location.pathname.replace(/^\/+/, "");
  if (!rawPath || rawPath.startsWith("assets/") || rawPath.startsWith("api/") || rawPath.startsWith("archives/")) {
    return "";
  }

  let decoded;
  try {
    decoded = decodeURIComponent(rawPath);
  } catch {
    return "";
  }

  if (!/^https?:\/\//i.test(decoded)) {
    return "";
  }

  return `${decoded}${window.location.search}${window.location.hash}`;
}
