/* Level-Speicher: lokaler Mini-Server (Firefox), eingebettete Daten, Cache */
(function (global) {
  "use strict";

  const PACK_KEY = "4everWild_levelPack";
  const LOCAL_PORT = 9847;

  function packToDataJs(pack) {
    return "/* auto-generated - sync with levels.json */\nwindow.__4EVER_WILD_PACK__ = "
      + JSON.stringify(pack, null, 2) + ";\n";
  }

  function cachePack(pack) {
    try {
      localStorage.setItem(PACK_KEY, JSON.stringify(pack));
    } catch (_) {}
    if (global.window) {
      try { global.window.__4EVER_WILD_PACK__ = JSON.parse(JSON.stringify(pack)); } catch (_) {}
    }
  }

  function readCachedPack() {
    try {
      const raw = localStorage.getItem(PACK_KEY);
      if (raw) return JSON.parse(raw);
    } catch (_) {}
    return null;
  }

  function readEmbeddedPack() {
    if (global.window?.__4EVER_WILD_PACK__) {
      try { return JSON.parse(JSON.stringify(global.window.__4EVER_WILD_PACK__)); } catch (_) {}
    }
    return null;
  }

  function localPort() {
    const p = location.port;
    return p || (location.protocol === "https:" ? "443" : "80");
  }

  function isOnLocalServerPage() {
    if (location.protocol !== "http:" && location.protocol !== "https:") return false;
    if (localPort() !== String(LOCAL_PORT)) return false;
    const h = location.hostname;
    return h === "127.0.0.1" || h === "localhost" || h === "[::1]" || h === "::1";
  }

  function localPostUrls(path) {
    const urls = [];
    if (isOnLocalServerPage()) urls.push(path);
    urls.push("http://127.0.0.1:" + LOCAL_PORT + path);
    urls.push("http://localhost:" + LOCAL_PORT + path);
    return urls;
  }

  function assetUrl(fileName) {
    return encodeURI(fileName).replace(/#/g, "%23") + "?v=" + Date.now();
  }

  async function tryFetchJson(url) {
    const same = url.startsWith("/");
    const res = await fetch(url, { cache: "no-store", mode: same ? "same-origin" : "cors" });
    if (!res.ok) return null;
    return res.json();
  }

  async function pingLocalServer() {
    for (const url of localPostUrls("/ping")) {
      try {
        const same = url.startsWith("/");
        const pingUrl = same ? "/ping?t=" + Date.now() : url + (url.includes("?") ? "&" : "?") + "t=" + Date.now();
        const ctrl = new AbortController();
        const t = setTimeout(() => ctrl.abort(), 2500);
        const res = await fetch(pingUrl, {
          signal: ctrl.signal,
          cache: "no-store",
          mode: same ? "same-origin" : "cors"
        });
        clearTimeout(t);
        if (res.ok) return true;
      } catch (_) {}
    }
    return false;
  }

  function resetServerProbe() {}

  async function readPack() {
    for (const url of localPostUrls("/levels.json")) {
      try {
        const same = url.startsWith("/");
        const sep = url.includes("?") ? "&" : "?";
        const res = await fetch(url + sep + "t=" + Date.now(), {
          cache: "no-store",
          mode: same ? "same-origin" : "cors"
        });
        if (res.ok) return await res.json();
      } catch (_) {}
    }

    let pack = readEmbeddedPack();
    if (pack) return pack;

    pack = readCachedPack();
    if (pack) return pack;

    try {
      const res = await fetch("levels.json?t=" + Date.now(), { cache: "no-store" });
      if (res.ok) return await res.json();
    } catch (_) {}

    return null;
  }

  async function writePack(pack) {
    const json = JSON.stringify(pack, null, 2);
    cachePack(pack);

    const urls = localPostUrls("/levels.json");
    let lastErr = "Server nicht erreichbar";

    for (let attempt = 0; attempt < 3; attempt++) {
      if (attempt > 0) {
        await new Promise((resolve) => setTimeout(resolve, 350 * attempt));
      }
      if (!(await pingLocalServer())) {
        lastErr = "Server nicht erreichbar (start-local.bat offen?)";
        continue;
      }
      for (const url of urls) {
        try {
          const same = url.startsWith("/");
          const ctrl = new AbortController();
          const t = setTimeout(() => ctrl.abort(), 20000);
          const res = await fetch(url, {
            method: "POST",
            headers: { "Content-Type": "application/json; charset=utf-8" },
            body: json,
            cache: "no-store",
            mode: same ? "same-origin" : "cors",
            signal: ctrl.signal
          });
          clearTimeout(t);
          if (res.ok) return { ok: true, via: url };
          const body = await res.text().catch(() => "");
          lastErr = "HTTP " + res.status + (body ? ": " + body.slice(0, 100) : "");
          console.warn("Speichern HTTP", url, lastErr);
        } catch (err) {
          lastErr = err.name === "AbortError" ? "Zeitueberschreitung" : (err.message || String(err));
          console.warn("Speichern fehlgeschlagen:", url, err);
        }
      }
    }

    return { ok: false, via: null, error: lastErr, json, js: packToDataJs(pack) };
  }

  async function uploadAsset(file) {
    if (!file || !file.name) return false;
    if (!/^[^\\/]+\.(png|jpe?g|mp3)$/i.test(file.name)) return false;

    const q = "?name=" + encodeURIComponent(file.name);
    for (const base of localPostUrls("/upload-asset")) {
      try {
        const same = base.startsWith("/");
        const res = await fetch(base + q, {
          method: "POST",
          headers: { "Content-Type": file.type || "application/octet-stream" },
          body: file,
          cache: "no-store",
          mode: same ? "same-origin" : "cors"
        });
        if (res.ok) return true;
        console.warn("Upload HTTP", base, res.status);
      } catch (err) {
        console.warn("Upload fehlgeschlagen:", base, err);
      }
    }
    return false;
  }

  function downloadText(filename, content, mime) {
    const a = document.createElement("a");
    a.href = URL.createObjectURL(new Blob([content], { type: mime }));
    a.download = filename;
    a.click();
    URL.revokeObjectURL(a.href);
  }

  function downloadPackFallback(pack) {
    const json = JSON.stringify(pack, null, 2);
    downloadText("levels.json", json, "application/json");
    setTimeout(() => downloadText("levels-data.js", packToDataJs(pack), "text/javascript"), 250);
  }

  async function serverStatusText() {
    const alive = await pingLocalServer();
    if (alive && isOnLocalServerPage()) {
      return "Lokalserver aktiv - Speichern schreibt direkt";
    }
    if (alive) {
      return "Lokalserver erreichbar";
    }
    if (isOnLocalServerPage()) {
      return "Server antwortet nicht - start-local.bat pruefen";
    }
    return "Editor ueber http://127.0.0.1:" + LOCAL_PORT + "/level-editor.html oeffnen";
  }

  function audioPreviewUrl(file) {
    if (!file) return "";
    return "audio/" + encodeURI(file).replace(/#/g, "%23");
  }

  global.ProjectFs = {
    PACK_KEY,
    LOCAL_PORT,
    packToDataJs,
    cachePack,
    readCachedPack,
    readEmbeddedPack,
    readPack,
    writePack,
    uploadAsset,
    assetUrl,
    audioPreviewUrl,
    downloadPackFallback,
    pingLocalServer,
    resetServerProbe,
    serverStatusText,
    isOnLocalServerPage,
    supportsProjectFs: () => false,
    loadProjectDir: async () => null,
    pickProjectDir: async () => null,
    readLevelPackJson: readPack,
    writeLevelPack: async (pack) => {
      const r = await writePack(pack);
      if (r.ok) return { ok: true };
      downloadPackFallback(pack);
      return { ok: false, reason: "no-server" };
    }
  };
})(typeof window !== "undefined" ? window : globalThis);
