function values(form: HTMLFormElement): Record<string, string | string[]> {
  const result: Record<string, string | string[]> = {};
  for (const control of Array.from(form.elements)) {
    if (
      (control instanceof HTMLInputElement ||
        control instanceof HTMLTextAreaElement ||
        control instanceof HTMLSelectElement) &&
      control.name
    ) {
      result[control.name] = control.value;
    }
  }
  for (const key of ["successCriteria", "taskTitles"]) {
    const value = result[key];
    if (typeof value === "string") {
      result[key] = value
        .split(/\n+/)
        .map((item) => item.trim())
        .filter(Boolean);
    }
  }
  return result;
}

let refreshRunning = false;
let mutationRunning = false;

function connectionStatus(message: string): void {
  const status = document.querySelector<HTMLElement>("[data-live-connection]");
  if (status) status.textContent = message;
}

async function refreshLivePage(): Promise<void> {
  if (refreshRunning || mutationRunning || !location.pathname.startsWith("/projects/")) return;
  const active = document.activeElement;
  if (
    active instanceof HTMLInputElement ||
    active instanceof HTMLTextAreaElement ||
    active instanceof HTMLSelectElement
  ) {
    return;
  }
  refreshRunning = true;
  try {
    const response = await fetch(location.href, {
      headers: { accept: "text/html", "x-awp-live-refresh": "1" },
      cache: "no-store",
    });
    if (!response.ok) throw new Error(`Live refresh returned ${response.status}`);
    const nextDocument = new DOMParser().parseFromString(await response.text(), "text/html");
    const currentMain = document.querySelector("main");
    const nextMain = nextDocument.querySelector("main");
    if (currentMain && nextMain && currentMain.innerHTML !== nextMain.innerHTML) {
      currentMain.replaceWith(nextMain);
      bindLiveForms(nextMain);
    }
    connectionStatus("Live control plane");
  } catch {
    connectionStatus("Reconnecting to control plane…");
  } finally {
    refreshRunning = false;
  }
}

function bindLiveForms(root: ParentNode): void {
  root.querySelectorAll<HTMLFormElement>("[data-live-form]").forEach((form) => {
    if (form.dataset.liveBound === "1") return;
    form.dataset.liveBound = "1";
    form.addEventListener("submit", async (event) => {
      event.preventDefault();
      const error = form.querySelector<HTMLElement>("[data-form-error]");
      if (error) error.textContent = "";
      const button = form.querySelector<HTMLButtonElement>("button");
      if (button) button.disabled = true;
      mutationRunning = true;
      try {
        const response = await fetch("/api" + form.getAttribute("action"), {
          method: "POST",
          headers: { "content-type": "application/json" },
          body: JSON.stringify(values(form)),
        });
        const body = (await response.json()) as {
          project?: { id?: string };
          message?: string;
          error?: string;
        };
        if (!response.ok) throw new Error(body.message ?? body.error ?? "Request failed");
        if (body.project?.id && form.getAttribute("action") === "/internal/projects") {
          window.location.assign("/projects/" + encodeURIComponent(body.project.id));
          return;
        }
        form.reset();
      } catch (cause) {
        if (error) error.textContent = cause instanceof Error ? cause.message : String(cause);
        if (button) button.disabled = false;
      } finally {
        mutationRunning = false;
      }
      await refreshLivePage();
    });
  });
}

bindLiveForms(document);
window.setInterval(() => void refreshLivePage(), 1_000);
