/* Werkstatt, Teil 1: der Explorer des Arbeitsordners (Abschnitt dateien.explorer),
   die Suche in Dateien (dateien.suche) und die Dateioperationen. Eine Datei
   öffnet editor.js (oeffneDatei). Jeder Ordner wird erst beim Aufklappen
   geladen; Änderungen anderer kommen über das Live-Thema projekt:<id>. */
"use strict";

S.explorer = { pid: null, knoten: {}, offen: new Set(), ordner: null, suche: "", lauf: 0, timer: null };

function explorerZurueck() {
  S.explorer = { pid: S.pid, knoten: {}, offen: new Set(), ordner: null, suche: S.explorer.suche,
                 lauf: S.explorer.lauf, timer: null };
}

const pfadVon = (ordner, name) => (ordner ? ordner + "/" + name : name);
const dateiname = (pfad) => pfad.split("/").pop();
const ordnerVon = (pfad) => pfad.split("/").slice(0, -1).join("/");
const dateienUrl = (rest = "") => `/api/projects/${S.pid}/dateien${rest}`;

async function ladeOrdner(pfad) {
  const r = await api(dateienUrl(`?pfad=${encodeURIComponent(pfad)}`));
  S.explorer.knoten[pfad] = r.eintraege;
  S.explorer.ordner = r.ordner;
  return r;
}

/** Alle bekannten Dateipfade - für das schnelle Öffnen in der Palette. */
function dateiListe() {
  const alle = [];
  for (const [ordner, eintraege] of Object.entries(S.explorer.knoten)) {
    eintraege.forEach((e) => { if (e.typ === "datei") alle.push(pfadVon(ordner, e.name)); });
  }
  return alle.sort();
}

async function explorerAktion(fn) {
  try { await fn(); } catch (e) { meldung(e.message, true); }
}

async function neueDatei(ordner) {
  const name = await nameAbfragen("Neue Datei", "", ordner ? `in ${ordner}/` : "im Arbeitsordner");
  if (!name) return;
  const pfad = pfadVon(ordner, name);
  await explorerAktion(async () => {
    await jsonRuf(dateienUrl("/inhalt"), "PUT", { pfad, inhalt: "", basis_sha: null });
    await explorerNeu(ordner);
    if (typeof oeffneDatei === "function") oeffneDatei(pfad);
  });
}

async function neuerOrdner(ordner) {
  const name = await nameAbfragen("Neuer Ordner", "", ordner ? `in ${ordner}/` : "im Arbeitsordner");
  if (!name) return;
  await explorerAktion(async () => {
    await jsonRuf(dateienUrl("/ordner"), "POST", { pfad: pfadVon(ordner, name) });
    S.explorer.offen.add(ordner);
    await explorerNeu(ordner);
  });
}

async function umbenennen(pfad) {
  const name = await nameAbfragen("Umbenennen", dateiname(pfad));
  if (!name || name === dateiname(pfad)) return;
  const nach = pfadVon(ordnerVon(pfad), name);
  await explorerAktion(async () => {
    await jsonRuf(dateienUrl("/verschieben"), "POST", { von: pfad, nach });
    const tab = S.tabs.liste.find((t) => t.id === "datei:" + pfad);
    if (tab) { schliesseTab(tab.id, true); if (typeof oeffneDatei === "function") oeffneDatei(nach); }
    await explorerNeu(ordnerVon(pfad));
  });
}

async function dateiLoeschen(pfad, typ) {
  if (!confirm(`${typ === "ordner" ? "Ordner" : "Datei"} „${pfad}“ löschen?`)) return;
  await explorerAktion(async () => {
    await api(dateienUrl(`?pfad=${encodeURIComponent(pfad)}`), { method: "DELETE" });
    S.tabs.liste.filter((t) => t.art !== "ansicht" && (t.pfad === pfad || t.pfad.startsWith(pfad + "/")))
      .forEach((t) => schliesseTab(t.id, true));
    await explorerNeu(ordnerVon(pfad));
  });
}

async function hochladen(ordner, dateien) {
  for (const datei of dateien) {
    const form = new FormData();
    form.append("pfad", ordner);
    form.append("datei", datei);
    await explorerAktion(() => api(dateienUrl("/hochladen"), { method: "POST", body: form }));
  }
  meldung(`${dateien.length} Datei(en) in ${ordner || "den Arbeitsordner"} geladen`);
  S.explorer.offen.add(ordner);
  await explorerNeu(ordner);
}

/** Einen Ordner neu laden und den Baum zeichnen - gebündelt, weil Live-Ereignisse
    in Schüben kommen. */
function explorerNeu(ordner) {
  delete S.explorer.knoten[ordner];
  clearTimeout(S.explorer.timer);
  S.explorer.timer = setTimeout(() => abschnittZeichnen("dateien.explorer"), 120);
}

/* -- Baum ----------------------------------------------------------------- */
function ablegbar(zeile, ordner) {
  zeile.ondragover = (e) => { e.preventDefault(); zeile.classList.add("is-over"); };
  zeile.ondragleave = () => zeile.classList.remove("is-over");
  zeile.ondrop = (e) => {
    e.preventDefault();
    e.stopPropagation();
    zeile.classList.remove("is-over");
    if (e.dataTransfer.files.length) hochladen(ordner, [...e.dataTransfer.files]);
  };
}

function zeichneEbene(baum, ordner, tiefe) {
  (S.explorer.knoten[ordner] || []).forEach((e) => {
    const pfad = pfadVon(ordner, e.name);
    const zeile = el("div", "tree__row explorer__zeile");
    zeile.style.paddingLeft = 8 + tiefe * 14 + "px";
    const kind = el("span", "kind");
    if (e.typ === "ordner") {
      kind.append(ico(S.explorer.offen.has(pfad) ? "chevron-down" : "chevron-right"));
      zeile.append(kind, el("span", "kind"), el("span", "explorer__name", e.name));
      zeile.children[1].append(ico(S.explorer.offen.has(pfad) ? "folder-open" : "folder"));
      zeile.title = `Ordner ${pfad}`;
      zeile.onclick = () => explorerAktion(async () => {
        if (S.explorer.offen.has(pfad)) S.explorer.offen.delete(pfad);
        else { S.explorer.offen.add(pfad); if (!S.explorer.knoten[pfad]) await ladeOrdner(pfad); }
        abschnittZeichnen("dateien.explorer");
      });
      ablegbar(zeile, pfad);
    } else {
      kind.append(ico(dateiSymbol("", e.name)));
      zeile.append(el("span", "kind"), kind, el("span", "explorer__name", e.name));
      zeile.title = `${pfad} · ${bytes(e.groesse)}`;
      const offen = S.tabs.aktiv && S.tabs.aktiv.id === "datei:" + pfad;
      zeile.classList.toggle("is-active", Boolean(offen));
      zeile.onclick = () => { if (typeof oeffneDatei === "function") oeffneDatei(pfad); };
    }
    const aktionen = el("span", "tree__add explorer__aktionen");
    const k = (symbol, titel, fn) => {
      const b = el("button");
      b.type = "button"; b.title = titel;
      b.append(ico(symbol));
      b.onclick = (ev) => { ev.stopPropagation(); fn(); };
      return b;
    };
    if (e.typ === "ordner") {
      aktionen.append(k("file-circle-plus", "Neue Datei hier", () => neueDatei(pfad)),
                      k("folder-plus", "Neuer Ordner hier", () => neuerOrdner(pfad)));
    }
    aktionen.append(k("pen", "Umbenennen", () => umbenennen(pfad)),
                    k("trash", "Löschen", () => dateiLoeschen(pfad, e.typ)));
    zeile.append(aktionen);
    baum.append(bedienbar(zeile, zeile.title));
    if (e.typ === "ordner" && S.explorer.offen.has(pfad)) zeichneEbene(baum, pfad, tiefe + 1);
  });
}

ABSCHNITT_ZEICHNER["dateien.explorer"] = async (ziel, aktionen) => {
  if (!S.pid) {
    ziel.append(el("div", "explorer__hinweis", "Kein Projekt gewählt."));
    return;
  }
  if (S.explorer.pid !== S.pid) explorerZurueck();
  const k = (symbol, titel, fn) => {
    const b = el("button");
    b.type = "button"; b.title = titel;
    b.append(ico(symbol));
    b.onclick = fn;
    return b;
  };
  aktionen.append(k("file-circle-plus", "Neue Datei", () => neueDatei("")),
                  k("folder-plus", "Neuer Ordner", () => neuerOrdner("")),
                  k("file-lines", "Aus Vorlage anlegen", () => typeof oeffneVorlagen === "function" && oeffneVorlagen("arbeitsordner")),
                  k("rotate", "Aktualisieren", () => { S.explorer.knoten = {}; abschnittZeichnen("dateien.explorer"); }),
                  k("minus", "Alles einklappen", () => { S.explorer.offen.clear(); abschnittZeichnen("dateien.explorer"); }));
  const baum = el("div", "tree explorer");
  ablegbar(baum, "");
  ziel.append(baum);
  // Mehrere Live-Ereignisse kurz nacheinander: nur der jüngste Lauf zeichnet,
  // sonst überholt eine ältere Antwort die neuere und zeigt einen alten Stand.
  const lauf = ++S.explorer.lauf;
  try {
    if (!S.explorer.knoten[""]) await ladeOrdner("");
  } catch (e) {
    baum.append(el("div", "explorer__hinweis", e.message));
    return;
  }
  if (lauf !== S.explorer.lauf) return;
  if (!S.explorer.knoten[""].length) {
    baum.append(el("div", "explorer__hinweis", "Leer. Dateien hierher ziehen oder oben anlegen."));
  }
  zeichneEbene(baum, "", 0);
  abonnieren(`projekt:${S.pid}`);
  // Desktop: der Ordner liegt, wo die Person will
  if (document.body.dataset.einzelnutzer) {
    const fuss = el("div", "explorer__ordner");
    const text = el("span", "", S.explorer.ordner || "");
    text.title = S.explorer.ordner || "";
    fuss.append(ico("folder-tree"), text);
    const rolle = S.daten && S.daten.role;
    if (rolle === "manager" || rolle === "admin") {
      const wahl = knopf("btn btn--ghost", "folder", "Ordner wählen");
      wahl.onclick = () => ordnerWaehlen();
      fuss.append(wahl);
    }
    ziel.append(fuss);
  }
};

async function ordnerWaehlen() {
  let pfad = null;
  if (window.pywebview && window.pywebview.api && window.pywebview.api.ordner_waehlen) {
    pfad = await window.pywebview.api.ordner_waehlen();
    if (pfad === null) return;
  } else {
    pfad = prompt("Voller Pfad des Arbeitsordners (leer = Vorgabe):", S.explorer.ordner || "");
    if (pfad === null) return;
  }
  await explorerAktion(async () => {
    await jsonRuf(`/api/projects/${S.pid}/arbeitsordner`, "PUT", { pfad: pfad || null });
    explorerZurueck();
    abschnittZeichnen("dateien.explorer");
    meldung("Arbeitsordner gesetzt");
  });
}

/* -- Suche ----------------------------------------------------------------- */
ABSCHNITT_ZEICHNER["dateien.suche"] = (ziel) => {
  const feld = el("input", "explorer__suche");
  feld.type = "search";
  feld.id = "explorer-suche";
  feld.placeholder = "In Dateien suchen … (Enter)";
  feld.value = S.explorer.suche || "";
  const liste = el("div", "explorer__treffer");
  const suchen = async () => {
    S.explorer.suche = feld.value.trim();
    liste.replaceChildren();
    if (!S.pid || S.explorer.suche.length < 2) return;
    try {
      const r = await api(dateienUrl(`/suche?q=${encodeURIComponent(S.explorer.suche)}`));
      if (!r.treffer.length) liste.append(el("div", "explorer__hinweis", "Nichts gefunden."));
      r.treffer.forEach((t) => {
        const z = el("div", "tree__row explorer__treffer-zeile");
        z.append(el("span", "explorer__treffer-pfad", `${t.pfad}:${t.zeile}`), el("span", "explorer__treffer-text", t.text));
        z.title = `${t.pfad}, Zeile ${t.zeile}`;
        z.onclick = () => { if (typeof oeffneDatei === "function") oeffneDatei(t.pfad, false, t.zeile); };
        liste.append(bedienbar(z, z.title));
      });
      if (r.abgebrochen) liste.append(el("div", "explorer__hinweis", "Gedeckelt - bitte genauer suchen."));
    } catch (e) { meldung(e.message, true); }
  };
  feld.onkeydown = (e) => { if (e.key === "Enter") suchen(); };
  ziel.append(feld, liste);
  if (S.explorer.suche) suchen();
};

BEFEHLE["werkstatt.suchen"] = () => {
  aktivitaetWechseln("dateien");
  const d = $('.panel details[data-abschnitt="dateien.suche"]');
  if (d) d.open = true;
  const feld = $("#explorer-suche");
  if (feld) { feld.focus(); feld.select(); }
};
BEFEHLE["werkstatt.neue_datei"] = () => { if (S.pid) neueDatei(""); };

/* -- Projektwechsel und Live ------------------------------------------------ */
document.addEventListener("projekt-geladen", () => {
  if (S.explorer.pid !== S.pid) {
    explorerZurueck();
    abschnittZeichnen("dateien.explorer");
    abschnittZeichnen("dateien.suche");
  }
});
document.addEventListener("live", (e) => {
  const d = e.detail;
  if (!d || d.projekt !== S.pid || !d.art) return;
  if (d.art === "arbeitsordner") { explorerZurueck(); abschnittZeichnen("dateien.explorer"); return; }
  const ordner = ordnerVon(d.pfad || "");
  if (S.explorer.knoten[ordner] !== undefined) explorerNeu(ordner);
  else if (ordner && S.explorer.knoten[ordnerVon(ordner)] !== undefined) explorerNeu(ordnerVon(ordner));
});
document.addEventListener("tab-gewechselt", () => {
  document.querySelectorAll(".explorer__zeile").forEach((z) => {
    const aktiv = S.tabs.aktiv && z.title.startsWith(S.tabs.aktiv.pfad + " ·");
    z.classList.toggle("is-active", Boolean(aktiv));
  });
});
