// files-overlay/dialogs.js — Phase A, Step 3.
//
// Owns the three inline dialogs that surround the file manager:
//   * #files-new-folder-modal  — "+ folder" / mkdir prompt
//   * #files-delete-modal      — delete-confirm for files and folders
//   * #files-conflict-modal    — overwrite / keep-both / cancel choice
//                                shown when save / upload / move hits 409
//
// Dispatch:
//   * "new-folder" registered into __filesOverlay → openNewFolder(path)
//   * "delete"     registered into __filesOverlay → openDelete(path,
//                  kind, name) — kind + name read from actionEl
//                  dataset.
//
// askConflict(path) → Promise<"overwrite" | "keep-both" | "cancel">
// is exposed on __filesOverlay so legacy upload / drag-drop callers in
// files-overlay.js (lines runUpload at 452 and the drop handler at
// 569) can still reach it after the function moved out of their file.
// editor.js's save 409-conflict path also goes through this same
// exposure.
//
// All three dialogs use a single delegated click listener per dialog,
// reading per-dialog state from module-scope. The clone-and-rebind
// pattern from the legacy file (replaceWith(cloneNode(true)) to drop
// stale listeners) is gone — the listener is attached once and reads
// freshly-set state each time the dialog opens.
//
// Dialogs open through window.modals.openInline / closeInline (the
// consolidated modal API at static/js/modals.js) rather than calling
// dialog.showModal() / close() directly, completing the inline-modal
// convention from commit c51089d.

(function () {
  "use strict";

  const fo = window.__filesOverlay;
  if (!fo) return;

  const { baseUrl, csrfToken } = fo;
  const { joinPath, parentOf, basename, postJson, postForm, scheduleRefresh } = fo.helpers;

  const newFolderDialog = document.getElementById("files-new-folder-modal");
  const deleteDialog = document.getElementById("files-delete-modal");
  const conflictDialog = document.getElementById("files-conflict-modal");

  function openModal(dialog) {
    if (window.modals && typeof window.modals.openInline === "function") {
      window.modals.openInline(dialog);
    } else {
      dialog.showModal();
    }
  }
  function closeModal(dialog) {
    if (window.modals && typeof window.modals.closeInline === "function") {
      window.modals.closeInline(dialog);
    } else {
      dialog.close();
    }
  }

  // ---------- conflict modal ----------------------------------------------

  // While a conflict dialog is open, conflictState.resolve is the
  // pending Promise resolver. The delegated click handler calls it and
  // nulls it so subsequent clicks don't double-resolve.
  let conflictState = null;

  function askConflict(path) {
    return new Promise((resolve) => {
      if (!conflictDialog) {
        resolve("cancel");
        return;
      }
      conflictDialog.querySelector(".files-conflict-path").textContent = path;
      conflictState = { resolve };
      openModal(conflictDialog);
    });
  }
  fo.askConflict = askConflict;

  if (conflictDialog) {
    conflictDialog.addEventListener("click", (event) => {
      const action = event.target?.dataset?.filesConflictAction;
      if (!action) return;
      const state = conflictState;
      conflictState = null;
      closeModal(conflictDialog);
      if (state && typeof state.resolve === "function") {
        state.resolve(action);
      }
    });
    // If the user dismisses the dialog any other way (Esc, backdrop,
    // programmatic close), resolve as "cancel" so callers don't hang.
    conflictDialog.addEventListener("close", () => {
      const state = conflictState;
      conflictState = null;
      if (state && typeof state.resolve === "function") {
        state.resolve("cancel");
      }
    });
  }

  // ---------- delete-confirm modal ----------------------------------------

  let deleteState = null;

  function openDelete(targetPath, kind, name) {
    if (!deleteDialog) return;
    deleteDialog.querySelector(".files-delete-name").textContent = name;
    const errEl = deleteDialog.querySelector(".files-delete-error");
    errEl.hidden = true;
    errEl.textContent = "";
    deleteState = { path: targetPath, kind, errEl };
    openModal(deleteDialog);
  }

  if (deleteDialog) {
    deleteDialog.addEventListener("click", async (event) => {
      const btn = event.target?.closest?.(".files-delete-confirm");
      if (!btn) return;
      if (!deleteState) return;
      const { path, errEl } = deleteState;
      const fd = new FormData();
      fd.append("path", path);
      fd.append("csrf_token", csrfToken);
      const r = await postForm(`${baseUrl}/files/delete`, fd);
      if (r.ok) {
        deleteState = null;
        closeModal(deleteDialog);
        scheduleRefresh(parentOf(path));
      } else {
        errEl.hidden = false;
        errEl.textContent =
          (r.body && r.body.error) || `Delete failed (HTTP ${r.status}).`;
      }
    });
    deleteDialog.addEventListener("close", () => {
      deleteState = null;
    });
  }

  // ---------- new-folder modal --------------------------------------------

  let newFolderState = null;

  function openNewFolder(targetFolder) {
    if (!newFolderDialog) return;
    const folder = targetFolder || "";
    newFolderDialog.querySelector(".files-new-folder-target").textContent =
      folder ? folder + "/" : "/";
    const input = newFolderDialog.querySelector(".files-new-folder-name");
    const errEl = newFolderDialog.querySelector(".files-new-folder-error");
    input.value = "";
    errEl.hidden = true;
    errEl.textContent = "";
    newFolderState = { folder, input, errEl };
    openModal(newFolderDialog);
    setTimeout(() => input.focus(), 0);
  }

  async function newFolderSubmit() {
    if (!newFolderState) return;
    const { folder, input, errEl } = newFolderState;
    const name = input.value.trim().replace(/^\/+|\/+$/g, "");
    if (!name) return;
    const fullPath = joinPath(folder, name);
    const r = await postJson(`${baseUrl}/files/mkdir`, { path: fullPath });
    if (r.ok) {
      newFolderState = null;
      closeModal(newFolderDialog);
      scheduleRefresh(folder);
    } else {
      errEl.hidden = false;
      errEl.textContent =
        (r.body && r.body.error) ||
        (r.status === 409
          ? "A file or folder with that name already exists."
          : `Failed (HTTP ${r.status}).`);
    }
  }

  if (newFolderDialog) {
    newFolderDialog.addEventListener("click", (event) => {
      if (event.target?.closest?.(".files-new-folder-create")) {
        newFolderSubmit();
      }
    });
    // Enter inside the name input submits. Direct-bound to the
    // persistent input — high-frequency keydown, no benefit from
    // delegation.
    newFolderDialog
      .querySelector(".files-new-folder-name")
      .addEventListener("keydown", (event) => {
        if (event.key !== "Enter") return;
        event.preventDefault();
        newFolderSubmit();
      });
    newFolderDialog.addEventListener("close", () => {
      newFolderState = null;
    });
  }

  // ---------- register action-registry handlers ----------

  fo.registerHandler("new-folder", (path) => openNewFolder(path));

  fo.registerHandler("delete", (path, actionEl) => {
    const kind = actionEl?.dataset?.rowKind;
    const name = actionEl?.dataset?.rowName || basename(path);
    openDelete(path, kind, name);
  });
})();
