/* Gaby · store global mínimo.
   - State único reactivo.
   - useGabyStore(selector) re-renderiza el componente cuando el slice cambia.
   - window.GABY_DISPATCH(accion) aplica una acción que Gaby devolvió desde el endpoint.

   Sin Redux, sin Zustand — solo Set de listeners + useSyncExternalStore. */

(function () {
  const seed = window.GABY_DATA;
  const USER_ID = "cinthia";
  const PERSISTED_KEYS = ["pendientes", "agenda", "personal", "repos", "borradoresMail"];

  let state = {
    pendientes: seed.pendientes.slice(),
    agenda: seed.agenda.slice(),
    repos: seed.repos.slice(),
    personal: { ...seed.personal, items: seed.personal.items.slice() },
    correo: { ...seed.correo, mensajes: seed.correo.mensajes.slice() },
    borradoresMail: [], // borradores que arma Gaby, todavía no enviados
    toasts: [],         // mensajes efímeros que pinta la UI ("Listo, te lo dejé en Pendientes")
    _hydrated: false,   // true cuando ya leímos Firestore
  };
  const listeners = new Set();

  function getState() { return state; }
  function setState(updater) {
    const next = typeof updater === "function" ? updater(state) : { ...state, ...updater };
    state = next;
    listeners.forEach((fn) => { try { fn(state); } catch (e) { console.error("store listener:", e); } });
    // Persistir async con debounce (toasts y _hydrated no cuentan)
    schedulePersist();
  }
  function subscribe(fn) { listeners.add(fn); return () => listeners.delete(fn); }

  // ----- Persistencia: hidrato al boot, persisto con debounce -----
  let persistTimer = null;
  function schedulePersist() {
    if (!state._hydrated) return; // no persistir antes de hidratar
    if (persistTimer) clearTimeout(persistTimer);
    persistTimer = setTimeout(persist, 600);
  }
  async function persist() {
    try {
      const slice = {};
      for (const k of PERSISTED_KEYS) slice[k] = state[k];
      await fetch("/api/gaby/state", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ userId: USER_ID, state: slice }),
      });
    } catch (e) {
      console.error("[store] persist:", e.message);
    }
  }
  async function hydrate() {
    try {
      const r = await fetch(`/api/gaby/state?userId=${encodeURIComponent(USER_ID)}`);
      if (r.ok) {
        const { state: remote } = await r.json();
        if (remote && typeof remote === "object") {
          const merged = { ...state };
          for (const k of PERSISTED_KEYS) if (Array.isArray(remote[k]) || (remote[k] && typeof remote[k] === "object")) merged[k] = remote[k];
          state = { ...merged, _hydrated: true };
          listeners.forEach((fn) => fn(state));
          return;
        }
      }
    } catch (e) {
      console.warn("[store] hydrate falló, usando seed:", e.message);
    }
    state = { ...state, _hydrated: true };
    listeners.forEach((fn) => fn(state));
  }
  // arranca al cargar el script
  hydrate();

  function nextId(list) {
    let max = 0;
    for (const x of list) if (typeof x.id === "number" && x.id > max) max = x.id;
    return max + 1;
  }
  function pushToast(msg) {
    const id = Date.now() + Math.random();
    setState((s) => ({ ...s, toasts: [...s.toasts, { id, msg }] }));
    setTimeout(() => {
      setState((s) => ({ ...s, toasts: s.toasts.filter((t) => t.id !== id) }));
    }, 3200);
  }

  // ----- Handlers por tipo de acción -----
  const handlers = {
    pendiente(datos) {
      setState((s) => ({
        ...s,
        pendientes: [
          {
            id: nextId(s.pendientes),
            cliente: datos.cliente || "—",
            accion: datos.accion || "",
            monto: datos.monto || "",
            cuando: datos.vence || datos.cuando || "",
            urgent: !!datos.urgente,
            hecho: false,
            categoria: datos.categoria || "laboral",
          },
          ...s.pendientes,
        ],
      }));
      pushToast("Lo dejé en Pendientes.");
    },

    agenda(datos) {
      setState((s) => ({
        ...s,
        agenda: [
          ...s.agenda,
          {
            id: nextId(s.agenda),
            hora: datos.hora || "—:—",
            dur: datos.duracion || "30m",
            titulo: datos.titulo || "",
            lugar: datos.lugar || "",
            tono: datos.tono || "petrol",
            listo: false,
            fecha: datos.fecha || null,
            recordatorio_min: datos.recordatorio_min ?? null,
          },
        ].sort((a, b) => (a.hora || "").localeCompare(b.hora || "")),
      }));
      pushToast("Te lo agendé.");
    },

    personal(datos) {
      const accion = datos.accion || "crear";
      setState((s) => {
        const items = s.personal.items.slice();
        if (accion === "crear") {
          items.unshift({
            id: nextId(items),
            label: datos.label || "",
            sub: datos.sub || "",
            on: datos.activo !== false,
            fecha: datos.fecha || null,
          });
        } else if (accion === "actualizar") {
          const idx = items.findIndex((i) => String(i.id) === String(datos.item_id));
          if (idx >= 0) items[idx] = { ...items[idx], label: datos.label ?? items[idx].label, sub: datos.sub ?? items[idx].sub, on: datos.activo ?? items[idx].on, fecha: datos.fecha ?? items[idx].fecha };
        } else if (accion === "eliminar") {
          const i = items.findIndex((x) => String(x.id) === String(datos.item_id));
          if (i >= 0) items.splice(i, 1);
        }
        return { ...s, personal: { ...s.personal, items } };
      });
      pushToast("Anotado en Personal.");
    },

    pagina(datos) {
      // La publicación real a bridge la maneja el backend (futuro).
      // Por ahora dejamos un asiento en Repositorios para que Cinthia vea que existe.
      setState((s) => ({
        ...s,
        repos: [
          {
            id: nextId(s.repos),
            nombre: datos.titulo || datos.slug || "Página sin título",
            tipo: (datos.tipo || "otro").toUpperCase(),
            estado: datos.privacidad === "compartible" ? "Compartible" : "Privada",
            fecha: new Date().toLocaleDateString("es-CL", { day: "2-digit", month: "short" }),
            peso: "v1",
            slug: datos.slug || null,
            url: datos.url || null,
          },
          ...s.repos,
        ],
      }));
      pushToast(datos.url ? "Lista, te dejé el link." : "Lista, queda en Repositorios.");
    },

    mail(datos) {
      // Gaby NO envía. Siempre deja borrador.
      setState((s) => ({
        ...s,
        borradoresMail: [
          {
            id: nextId(s.borradoresMail),
            para: datos.para || "",
            asunto: datos.asunto || "",
            cuerpo: datos.cuerpo_borrador || "",
            ts: Date.now(),
          },
          ...s.borradoresMail,
        ],
      }));
      pushToast("Te dejé el borrador listo.");
    },
  };

  function dispatch(accion) {
    if (!accion || typeof accion !== "object") return;
    const { tipo, datos } = accion;
    const h = handlers[tipo];
    if (!h) {
      console.warn("[GABY_DISPATCH] tipo desconocido:", tipo);
      return;
    }
    try { h(datos || {}); } catch (e) { console.error("[GABY_DISPATCH] error en", tipo, e); }
  }

  // Hook estilo Zustand: selector + igualdad referencial (default ===).
  function useGabyStore(selector, isEqual = Object.is) {
    const getSnapshot = React.useCallback(() => selector(state), [selector]);
    // useSyncExternalStore no acepta isEqual; usamos un ref para forzar igualdad estructural si hace falta.
    const lastRef = React.useRef(getSnapshot());
    const subscribeWithEq = React.useCallback((onChange) => {
      return subscribe(() => {
        const next = selector(state);
        if (!isEqual(next, lastRef.current)) {
          lastRef.current = next;
          onChange();
        }
      });
    }, [selector, isEqual]);
    return React.useSyncExternalStore(subscribeWithEq, () => lastRef.current);
  }

  window.GabyStore = { getState, setState, subscribe };
  window.GABY_DISPATCH = dispatch;
  window.useGabyStore = useGabyStore;
})();
