/* Store partagé — TOUT le contenu éditable est désormais sur le SERVEUR
   (liste d'invités + infos du mariage + page Informations), partagé entre
   tous les appareils. Un cache localStorage sert de secours hors ligne. */

const useLocalStorage = (key, initial) => {
  const [val, setVal] = React.useState(() => {
    try {
      const raw = localStorage.getItem(key);
      return raw ? JSON.parse(raw) : initial;
    } catch { return initial; }
  });
  React.useEffect(() => {
    try {
      if (val == null) localStorage.removeItem(key);
      else localStorage.setItem(key, JSON.stringify(val));
    } catch {}
  }, [key, val]);
  return [val, setVal];
};

const StoreContext = React.createContext(null);

const GUESTS_CACHE_KEY = "wedding-guests-cache-v3";
const CONTENT_CACHE_KEY = "wedding-content-cache-v3";
const ADDRESS_ECHO_KEY = "wedding-address-echo-v1";

const readCache = (key) => {
  try {
    const raw = localStorage.getItem(key);
    return raw ? JSON.parse(raw) : null;
  } catch { return null; }
};
const writeCache = (key, value) => {
  try { localStorage.setItem(key, JSON.stringify(value)); } catch {}
};

/* Fusionne les contenus serveur avec les valeurs par défaut. */
const mergeContent = (content) => {
  const c = content || {};
  return {
    wedding: { ...WEDDING_DEFAULTS, ...(c.wedding || {}) },
    info: { ...INFO_DEFAULTS, ...(c.info || {}) }
  };
};

const StoreProvider = ({ children }) => {
  const [adminToken, setAdminToken] = useLocalStorage("wedding-admin-token-v2", null);

  const initialContent = mergeContent(readCache(CONTENT_CACHE_KEY));
  const [wedding, setWeddingState] = React.useState(initialContent.wedding);
  const [info, setInfoState] = React.useState(initialContent.info);

  const [guests, setGuests] = React.useState(() => readCache(GUESTS_CACHE_KEY) || []);
  const [addressEcho, setAddressEcho] = React.useState(() => readCache(ADDRESS_ECHO_KEY) || {});
  const [ready, setReady] = React.useState(false);
  const [offline, setOffline] = React.useState(false);

  // Refs : valeurs les plus récentes (pour saveSite après plusieurs setState).
  const weddingRef = React.useRef(wedding);
  const infoRef = React.useRef(info);
  weddingRef.current = wedding;
  infoRef.current = info;

  const applyGuests = React.useCallback((next) => {
    setGuests(next);
    writeCache(GUESTS_CACHE_KEY, next);
  }, []);

  const applyContent = React.useCallback((content) => {
    const merged = mergeContent(content);
    setWeddingState(merged.wedding);
    setInfoState(merged.info);
    writeCache(CONTENT_CACHE_KEY, content || {});
  }, []);

  const refresh = React.useCallback(async () => {
    try {
      const data = await api.get("/api/bootstrap");
      applyGuests((data && data.guests) || []);
      applyContent((data && data.content) || {});
      setOffline(false);
    } catch {
      setOffline(true);
      setGuests((prev) => (prev && prev.length ? prev : GUESTS_DEFAULTS));
    } finally {
      setReady(true);
    }
  }, [applyGuests, applyContent]);

  React.useEffect(() => { refresh(); }, [refresh]);

  /* --- Contenu du site (infos mariage + page Informations) --- */
  // Mise à jour locale instantanée (saisie fluide), sans appel réseau.
  const setWedding = (next) => setWeddingState(next);
  const setInfo = (next) => setInfoState(next);

  // Persistance serveur du contenu courant (réservé aux mariés).
  const saveSite = async () => {
    const content = { wedding: weddingRef.current, info: infoRef.current };
    writeCache(CONTENT_CACHE_KEY, content);
    const data = await api.put("/api/admin/content", { content }, adminToken);
    return data;
  };

  /* --- Auto-inscription d'un invité (public, modèle Tricount) --- */
  const addGuest = async (name, side) => {
    const data = await api.post("/api/guests", { name, side });
    const guest = data.guest;
    applyGuests([...guests.filter((g) => g.id !== guest.id), guest]);
    return guest;
  };

  // Retrouve l'invité déjà réservé par cette adresse e-mail (null sinon).
  const findClaimedGuest = async (email) => {
    const data = await api.post("/api/guest/lookup", { email });
    return data.guest || null;
  };

  // Réserve un invité avec l'e-mail saisi (verrou + liaison e-mail ↔ invité).
  const claimGuest = async (guestId, email) => {
    const data = await api.post("/api/guest/claim", { guestId, email });
    const g = data.guest;
    const exists = guests.some((x) => x.id === g.id);
    applyGuests(exists ? guests.map((x) => (x.id === g.id ? { ...x, ...g } : x)) : [...guests, g]);
    return g;
  };

  // Réponse de présence de l'invité (oui / non).
  const setRsvp = async (guestId, rsvp) => {
    const data = await api.post("/api/guest/rsvp", { guestId, rsvp });
    const g = data.guest;
    applyGuests(guests.map((x) => (x.id === g.id ? { ...x, ...g } : x)));
    return g;
  };

  // Adresse pour le faire-part. L'adresse n'est pas exposée dans la liste
  // publique (vie privée) : on garde un écho local pour pré-remplir le champ.
  const setAddress = async (guestId, address) => {
    const data = await api.post("/api/guest/address", { guestId, address });
    const next = { ...addressEcho, [guestId]: data.address };
    setAddressEcho(next);
    writeCache(ADDRESS_ECHO_KEY, next);
    return data.address;
  };

  /* --- Connexion admin auprès du serveur --- */
  const adminLogin = async (password) => {
    const data = await api.post("/api/admin/login", { password });
    setAdminToken(data.token);
    return data.token;
  };
  const adminLogout = () => setAdminToken(null);

  /* --- CRUD invités côté admin (nécessite le jeton serveur) --- */
  const adminAddGuest = async () => {
    const data = await api.post(
      "/api/admin/guests",
      { name: "Nouvel invité", side: wedding.bride, table: "" },
      adminToken
    );
    applyGuests([...guests, data.guest]);
    return data.guest;
  };
  // `fields` est l'objet invité complet (toutes les colonnes éditables).
  const adminSaveGuest = async (id, fields) => {
    const data = await api.put(
      `/api/admin/guests/${encodeURIComponent(id)}`,
      {
        name: fields.name,
        side: fields.side || "",
        table: fields.table || "",
        message: fields.message || "",
        email: fields.email || "",
        address: fields.address || "",
        rsvp: fields.rsvp === "yes" ? "yes" : fields.rsvp === "no" ? "no" : ""
      },
      adminToken
    );
    // Met à jour la liste publique (l'objet renvoyé contient aussi les champs publics).
    applyGuests(guests.map((g) => (g.id === id ? { ...g, ...data.guest } : g)));
    return data.guest;
  };
  const adminDeleteGuest = async (id) => {
    await api.del(`/api/admin/guests/${encodeURIComponent(id)}`, adminToken);
    applyGuests(guests.filter((g) => g.id !== id));
  };

  return (
    <StoreContext.Provider value={{
      wedding,
      info,
      setWedding,
      setInfo,
      saveSite,
      guests,
      ready,
      offline,
      refresh,
      addGuest,
      claimGuest,
      findClaimedGuest,
      setRsvp,
      setAddress,
      addressEcho,
      adminToken,
      adminLogin,
      adminLogout,
      adminAddGuest,
      adminSaveGuest,
      adminDeleteGuest
    }}>
      {children}
    </StoreContext.Provider>
  );
};

const useStore = () => React.useContext(StoreContext);

Object.assign(window, { useLocalStorage, StoreContext, StoreProvider, useStore });
