/* ============================================================
 * Larspeenghar Pedigree — frontend sync layer
 *
 * Bridges the existing localStorage-based app to the PHP backend.
 *
 *   On boot:
 *     - Calls /api.php?action=session to detect existing login
 *     - If logged in, fetches all stores from the server and hydrates
 *       localStorage BEFORE the rest of app.js initializes (so the
 *       existing synchronous reads "just work").
 *
 *   On save:
 *     - The existing code still writes to localStorage immediately
 *       (instant UI). syncPush(store, data) is then called to POST
 *       the change to the server. Failures are queued + retried.
 *
 *   On login:
 *     - doLogin() (in app.js) is overridden to authenticate against the
 *       server instead of the in-browser users list.
 *
 * Keep this file loaded BEFORE app.js so the boot hooks are registered.
 * ============================================================ */

(function () {
  "use strict";

  const API = "api.php";

  // Map of "frontend store name" → "backend store name + localStorage key"
  const STORES = {
    animals:      { backend: "animals",      lsKey: "larspeenghar_v7" },
    owners:       { backend: "owners",       lsKey: "lp_owners" },
    indiv_owners: { backend: "indiv_owners", lsKey: "lp_indiv_owners" },
    messages:     { backend: "messages",     lsKey: "lp_messages_v1" },
    // settings + users are handled separately
  };

  const PENDING_KEY = "lp_sync_pending";

  /* ---- helpers ---- */
  async function apiCall(url, opts) {
    const r = await fetch(url, Object.assign({ credentials: "same-origin" }, opts || {}));
    let body = null;
    try { body = await r.json(); } catch (e) {}
    if (!r.ok) {
      const msg = (body && body.error) || ("HTTP " + r.status);
      const err = new Error(msg);
      err.status = r.status;
      throw err;
    }
    return body || {};
  }

  function getPending() {
    try { return JSON.parse(localStorage.getItem(PENDING_KEY) || "{}"); }
    catch (e) { return {}; }
  }
  function setPending(p) {
    try { localStorage.setItem(PENDING_KEY, JSON.stringify(p)); } catch (e) {}
  }

  function syncIndicator(state) {
    let el = document.getElementById("sync-indicator");
    if (!el) {
      el = document.createElement("div");
      el.id = "sync-indicator";
      el.style.cssText =
        "position:fixed;bottom:14px;left:14px;z-index:998;padding:5px 10px;" +
        "border-radius:14px;font:11px/1.2 sans-serif;color:#fff;" +
        "background:#888;box-shadow:0 2px 6px rgba(0,0,0,.18);" +
        "transition:opacity .25s,background .25s;opacity:0;pointer-events:none";
      document.body.appendChild(el);
    }
    const s = {
      syncing: { bg: "#3a6bc4", text: "⟳ Syncing…", op: 1 },
      saved:   { bg: "#2d7a4f", text: "✓ Saved",    op: 1 },
      error:   { bg: "#c0392b", text: "⚠ Sync error", op: 1 },
      hidden:  { bg: "#888",    text: "",            op: 0 },
    }[state] || { bg: "#888", text: "", op: 0 };
    el.style.background = s.bg;
    el.textContent = s.text;
    el.style.opacity = s.op;
    if (state === "saved") setTimeout(() => syncIndicator("hidden"), 1400);
  }

  /* ---- public sync API exposed on window.LpSync ---- */
  const LpSync = {
    /** Current logged-in user (set after boot or login). */
    user: null,

    /** True once we've confirmed the PHP backend is reachable. */
    hasBackend: false,

    /** Probe api.php once on boot. Returns true if PHP is serving. */
    async probe() {
      try {
        const r = await fetch(API + "?action=session", { credentials: "same-origin" });
        // api.php returns JSON. A static host serving the raw PHP file
        // will return text/x-php or text/plain — that's a "no backend" signal.
        const ct = (r.headers.get("content-type") || "").toLowerCase();
        if (!r.ok || !ct.includes("json")) {
          this.hasBackend = false;
          return false;
        }
        this.hasBackend = true;
        return true;
      } catch (e) {
        this.hasBackend = false;
        return false;
      }
    },

    /** Push a store's full contents to the server. Fire-and-forget; queues on failure. */
    async push(storeName, data) {
      if (!this.hasBackend) return; // offline / no PHP — localStorage is the source of truth
      const def = STORES[storeName];
      if (!def) return;
      syncIndicator("syncing");
      try {
        await apiCall(API + "?action=save&store=" + encodeURIComponent(def.backend), {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ data: data }),
        });
        // Clear any pending entry for this store
        const p = getPending();
        delete p[storeName];
        setPending(p);
        syncIndicator("saved");
      } catch (e) {
        console.warn("syncPush(" + storeName + ") failed:", e);
        // Queue for retry
        const p = getPending();
        p[storeName] = Date.now();
        setPending(p);
        syncIndicator("error");
      }
    },

    /** Fetch all stores and hydrate localStorage. Called on boot after session check. */
    async hydrate() {
      for (const name of Object.keys(STORES)) {
        const def = STORES[name];
        try {
          const r = await apiCall(API + "?action=fetch&store=" + encodeURIComponent(def.backend));
          localStorage.setItem(def.lsKey, JSON.stringify(r.data || []));
        } catch (e) {
          console.warn("hydrate(" + name + ") failed:", e);
        }
      }
    },

    /** Retry any pending writes (e.g. on reconnect). */
    async retryPending() {
      const pending = getPending();
      const names = Object.keys(pending);
      if (!names.length) return;
      for (const name of names) {
        const def = STORES[name];
        if (!def) { delete pending[name]; continue; }
        try {
          const raw = localStorage.getItem(def.lsKey);
          const data = raw ? JSON.parse(raw) : [];
          await this.push(name, data);
        } catch (e) {
          console.warn("retry " + name + " failed:", e);
        }
      }
    },

    /** Call api.php?action=login. On success, hydrate stores and return user. */
    async login(username, password) {
      if (!this.hasBackend) {
        // No PHP backend reachable — throw a tagged error so the caller can
        // fall back to local-only auth (DEFAULT_ADMIN check in app.js).
        const e = new Error("NO_BACKEND");
        e.noBackend = true;
        throw e;
      }
      const r = await apiCall(API + "?action=login", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ username: username, password: password }),
      });
      this.user = r.user;
      await this.hydrate();
      return r.user;
    },

    async logout() {
      try { await apiCall(API + "?action=logout", { method: "POST" }); } catch (e) {}
      this.user = null;
      // Clear local cache so the next user starts clean
      for (const name of Object.keys(STORES)) {
        try { localStorage.removeItem(STORES[name].lsKey); } catch (e) {}
      }
    },

    /** Check if there is an active session on the server. */
    async resumeSession() {
      if (!this.hasBackend) return null;
      try {
        const r = await apiCall(API + "?action=session");
        if (r.user) {
          this.user = r.user;
          await this.hydrate();
          return r.user;
        }
      } catch (e) {
        console.warn("resumeSession failed:", e);
      }
      return null;
    },
  };

  window.LpSync = LpSync;

  /* ---- boot: resume session BEFORE app.js wires up its UI ---- */
  // app.js runs its bootstrap in startApp() which is called from doLogin().
  // We want to try resuming a session so users don't re-login on every reload.
  document.addEventListener("DOMContentLoaded", async function () {
    syncIndicator("syncing");
    await LpSync.probe();
    if (!LpSync.hasBackend) {
      console.info("Larspeenghar: running in local-only mode (no PHP backend detected).");
    }
    const user = await LpSync.resumeSession();
    syncIndicator("hidden");
    if (user) {
      // Hand off to app.js as if doLogin() had just succeeded.
      // app.js exposes startApp() and showUserPortal(); call the right one.
      const gate = document.getElementById("loginGate");
      if (gate) gate.style.display = "none";
      if (user.role === "admin") {
        const hdr = document.querySelector("header");
        const shell = document.querySelector(".shell");
        if (hdr) hdr.style.display = "";
        if (shell) shell.style.display = "";
        if (typeof startApp === "function") startApp();
      } else {
        if (typeof showUserPortal === "function") showUserPortal(user);
      }
    }
    // Try to flush any queued writes from a previous session
    LpSync.retryPending();
  });

  /* ---- retry queue when the user comes back online ---- */
  window.addEventListener("online", () => LpSync.retryPending());
})();
