/* ============================================================
   STORE · estado global + persistencia (localStorage)
   ============================================================ */
const { createContext, useContext, useState, useEffect, useRef, useCallback, useMemo } = React;

const LS_KEY = 'granchimu_data_v1';
const LS_SNAP = 'granchimu_snaps_v1';
const LS_SESSION = 'granchimu_admin_session';
const LS_USER = 'granchimu_user';
const LS_KEYAUTH = 'granchimu_key';
const API = 'data.php'; // backend en el mismo directorio (Hostinger)

/* ---- roles y permisos del panel ---- */
const ROLES = {
  admin:    { label: 'Administrador',      perms: ['contenido','reservas','dashboard','restaurante','usuarios'] },
  editor:   { label: 'Editor de carta',    perms: ['contenido','dashboard'] },
  reservas: { label: 'Recepción (reservas)', perms: ['reservas'] },
  caja:     { label: 'Caja / Consumos',    perms: ['dashboard'] },
};

const StoreCtx = createContext(null);
const useStore = () => useContext(StoreCtx);

/* ---- helpers ---- */
const money = (n) => {
  if (n === null || n === undefined || n === '' || isNaN(n)) return '—';
  return '$' + Math.round(n).toLocaleString('es-CL');
};
const todayISO = () => {
  const d = new Date();
  return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;
};
const uid = (base='id') => base.toString().toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'')
  .replace(/[^a-z0-9]+/g,'-').replace(/^-|-$/g,'').slice(0,28) + '-' + Math.random().toString(36).slice(2,7);

// downscale + compress image to keep storage light
function fileToDataURL(file, maxDim = 900, quality = 0.72) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => {
      const img = new Image();
      img.onload = () => {
        let { width, height } = img;
        const scale = Math.min(1, maxDim / Math.max(width, height));
        width = Math.round(width * scale); height = Math.round(height * scale);
        const c = document.createElement('canvas');
        c.width = width; c.height = height;
        const ctx = c.getContext('2d');
        ctx.drawImage(img, 0, 0, width, height);
        resolve(c.toDataURL('image/jpeg', quality));
      };
      img.onerror = reject;
      img.src = reader.result;
    };
    reader.onerror = reject;
    reader.readAsDataURL(file);
  });
}

// asegura campos nuevos (usuarios, etc.) en datos antiguos
function normalize(d) {
  if (!d) return d;
  if (!d.users || !d.users.length) {
    d.users = [{ id:'u-admin', name:'Administrador', username:'admin', password:(d.brand && d.brand.adminPass) || 'chimu2027', role:'admin', active:true }];
  }
  if (d.brand && d.brand.serverKey === undefined) d.brand.serverKey = (d.brand.adminPass) || 'chimu2027';
  if (!d.reservations) d.reservations = [];
  if (!d.sales) d.sales = {};
  return d;
}

function loadData() {
  try {
    const raw = localStorage.getItem(LS_KEY);
    if (raw) return normalize(JSON.parse(raw));
  } catch (e) { console.warn('load failed', e); }
  return normalize(JSON.parse(JSON.stringify(window.SEED_DATA)));
}

/* ---- API servidor ---- */
async function apiGet() {
  try {
    const r = await fetch(API + '?action=get&t=' + Date.now(), { cache:'no-store' });
    if (!r.ok) return null;
    const j = await r.json();
    return (j && j.ok) ? j.data : null;     // data puede ser null si aún no se ha guardado
  } catch (e) { return null; }
}
async function apiSave(data, key) {
  try {
    const r = await fetch(API + '?action=save', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ key, data }) });
    const j = await r.json();
    return j && j.ok;
  } catch (e) { return false; }
}
async function apiAddReservation(reservation) {
  try {
    const r = await fetch(API + '?action=reservation', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ reservation }) });
    const j = await r.json();
    return j && j.ok;
  } catch (e) { return false; }
}

function StoreProvider({ children }) {
  const [data, setData] = useState(loadData);
  const [toasts, setToasts] = useState([]);
  const [currentUser, setCurrentUser] = useState(() => { try { return JSON.parse(sessionStorage.getItem(LS_USER)||'null'); } catch(e){ return null; } });
  const [admin, setAdmin] = useState(() => { try { return !!JSON.parse(sessionStorage.getItem(LS_USER)||'null'); } catch(e){ return false; } });
  const [online, setOnline] = useState(null); // null=probando, true=servidor ok, false=solo local
  const [syncing, setSyncing] = useState(false);
  const adminRef = useRef(admin);
  const dataRef = useRef(data);
  const saveTimer = useRef(null);
  const syncingRef = useRef(false);
  const lastEditRef = useRef(0);
  useEffect(() => { dataRef.current = data; }, [data]);
  useEffect(() => { adminRef.current = admin; }, [admin]);
  const setSync = (v) => { syncingRef.current = v; setSyncing(v); };

  // persist (cache local)
  useEffect(() => {
    try { localStorage.setItem(LS_KEY, JSON.stringify(data)); }
    catch (e) { toast('No se pudo guardar localmente (almacenamiento lleno). Reduce el peso de las fotos.', 'err'); }
  }, [data]);

  // ---- carga inicial desde el servidor ----
  useEffect(() => {
    (async () => {
      try {
        const r = await fetch(API + '?action=get&t=' + Date.now(), { cache:'no-store' });
        const txt = await r.text();
        let j = null;
        try { j = JSON.parse(txt); } catch (e) { j = null; }
        if (r.ok && j && j.ok === true) {            // backend PHP respondió correctamente
          setOnline(true);
          if (j.data) setData(normalize(j.data));
        } else {
          setOnline(false);                          // sin backend válido -> modo local
        }
      } catch (e) { setOnline(false); }
    })();
  }, []);

  // ---- sincronización automática entre dispositivos ----
  useEffect(() => {
    if (online !== true) return;
    const tick = async () => {
      if (document.hidden) return;
      if (syncingRef.current) return;
      if (Date.now() - lastEditRef.current < 8000) return; // proteger edición en curso
      const server = await apiGet();
      if (server && JSON.stringify(server) !== JSON.stringify(dataRef.current)) setData(normalize(server));
    };
    const id = setInterval(tick, 12000);
    const onFocus = () => { if (Date.now() - lastEditRef.current >= 4000) tick(); };
    window.addEventListener('focus', onFocus);
    document.addEventListener('visibilitychange', onFocus);
    return () => { clearInterval(id); window.removeEventListener('focus', onFocus); document.removeEventListener('visibilitychange', onFocus); };
  }, [online]);

  // ---- guardar en el servidor (debounced) ----
  const scheduleServerSave = useCallback((next) => {
    lastEditRef.current = Date.now();
    if (online !== true || !adminRef.current) return; // solo admin con backend disponible
    if (saveTimer.current) clearTimeout(saveTimer.current);
    setSync(true);
    saveTimer.current = setTimeout(async () => {
      const ok = await apiSave(next, (next.brand && next.brand.serverKey) || '');
      setSync(false);
      if (!ok) toast('No se pudo guardar en el servidor. Revisa la "clave del servidor" o los permisos de la carpeta.', 'err');
    }, 500);
  }, [online]);

  // ---- publicar manualmente (botón) ----
  const publish = async () => {
    if (online !== true) { toast('Sin conexión al servidor: los datos quedan solo en este equipo. Sube data.php a tu hosting.', 'err'); return; }
    setSync(true);
    const ok = await apiSave(dataRef.current, (dataRef.current.brand && dataRef.current.brand.serverKey) || '');
    setSync(false);
    toast(ok ? 'Cambios publicados ✓ (visibles en todos los dispositivos)' : 'No se pudo publicar. Revisa la clave del servidor o los permisos.', ok ? 'ok' : 'err');
  };

  const toast = useCallback((msg, kind='ok') => {
    const id = Math.random().toString(36).slice(2);
    setToasts(t => [...t, { id, msg, kind }]);
    setTimeout(() => setToasts(t => t.filter(x => x.id !== id)), 2600);
  }, []);

  // commit: clone-mutate pattern with snapshot history (keep last 3)
  const commit = useCallback((mutator, msg) => {
    setData(prev => {
      const snaps = JSON.parse(localStorage.getItem(LS_SNAP) || '[]');
      snaps.push(prev);
      while (snaps.length > 3) snaps.shift();
      localStorage.setItem(LS_SNAP, JSON.stringify(snaps));
      const next = structuredClone(prev);
      mutator(next);
      scheduleServerSave(next);
      return next;
    });
    if (msg) toast(msg);
  }, [toast, scheduleServerSave]);

  const undo = useCallback(() => {
    const snaps = JSON.parse(localStorage.getItem(LS_SNAP) || '[]');
    if (!snaps.length) { toast('No hay cambios para deshacer', 'err'); return; }
    const last = snaps.pop();
    localStorage.setItem(LS_SNAP, JSON.stringify(snaps));
    setData(last);
    scheduleServerSave(last);
    toast('Se restauró la versión anterior');
  }, [toast, scheduleServerSave]);

  /* ---- finders ---- */
  const find = {
    carta: (d, cid) => d.cartas.find(c => c.id === cid),
    cat: (carta, catId) => carta.categories.find(c => c.id === catId),
    sub: (cat, subId) => cat.subcats.find(s => s.id === subId),
  };

  /* ---- brand ---- */
  const updateBrand = (patch) => commit(d => { Object.assign(d.brand, patch); }, 'Información guardada');

  /* ---- generic move within array ---- */
  const moveInArr = (arr, id, dir) => {
    const i = arr.findIndex(x => x.id === id);
    const j = i + dir;
    if (i < 0 || j < 0 || j >= arr.length) return;
    [arr[i], arr[j]] = [arr[j], arr[i]];
  };

  /* ---- categories ---- */
  const addCategory = (cid, payload) => commit(d => {
    find.carta(d, cid).categories.push({ id: uid(payload.name), name: payload.name, subtitle: payload.subtitle||'', subcats: [] });
  }, 'Categoría agregada');
  const updateCategory = (cid, catId, patch) => commit(d => {
    Object.assign(find.cat(find.carta(d, cid), catId), patch);
  }, 'Categoría actualizada');
  const deleteCategory = (cid, catId) => commit(d => {
    const carta = find.carta(d, cid); carta.categories = carta.categories.filter(c => c.id !== catId);
  }, 'Categoría eliminada');
  const moveCategory = (cid, catId, dir) => commit(d => { moveInArr(find.carta(d, cid).categories, catId, dir); });

  /* ---- subcategories ---- */
  const addSub = (cid, catId, payload) => commit(d => {
    find.cat(find.carta(d, cid), catId).subcats.push({ id: uid(payload.name||'grupo'), name: payload.name||'', products: [] });
  }, 'Subcategoría agregada');
  const updateSub = (cid, catId, subId, patch) => commit(d => {
    Object.assign(find.sub(find.cat(find.carta(d, cid), catId), subId), patch);
  }, 'Subcategoría actualizada');
  const deleteSub = (cid, catId, subId) => commit(d => {
    const cat = find.cat(find.carta(d, cid), catId); cat.subcats = cat.subcats.filter(s => s.id !== subId);
  }, 'Subcategoría eliminada');
  const moveSub = (cid, catId, subId, dir) => commit(d => { moveInArr(find.cat(find.carta(d, cid), catId).subcats, subId, dir); });

  /* ---- products ---- */
  const blankProduct = () => ({ id: uid('prod'), name:'', desc:'', price:null, priceText:null, cost:null, recipe:'', photo:null, featured:false, active:true, variants:[] });
  const addProduct = (cid, catId, subId, payload) => commit(d => {
    find.sub(find.cat(find.carta(d, cid), catId), subId).products.push({ ...blankProduct(), ...payload });
  }, 'Producto agregado');
  const updateProduct = (cid, catId, subId, prodId, patch) => commit(d => {
    const sub = find.sub(find.cat(find.carta(d, cid), catId), subId);
    Object.assign(sub.products.find(p => p.id === prodId), patch);
  }, 'Producto guardado');
  const deleteProduct = (cid, catId, subId, prodId) => commit(d => {
    const sub = find.sub(find.cat(find.carta(d, cid), catId), subId);
    sub.products = sub.products.filter(p => p.id !== prodId);
  }, 'Producto eliminado');
  const moveProduct = (cid, catId, subId, prodId, dir) => commit(d => {
    moveInArr(find.sub(find.cat(find.carta(d, cid), catId), subId).products, prodId, dir);
  });
  // move product to another subcategory
  const reassignProduct = (cid, catId, subId, prodId, newCatId, newSubId) => commit(d => {
    const carta = find.carta(d, cid);
    const sub = find.sub(find.cat(carta, catId), subId);
    const idx = sub.products.findIndex(p => p.id === prodId);
    const [p] = sub.products.splice(idx, 1);
    find.sub(find.cat(carta, newCatId), newSubId).products.push(p);
  }, 'Producto movido');

  /* ---- sales / consumos ---- */
  const setSale = (date, prodId, qty) => commit(d => {
    if (!d.sales[date]) d.sales[date] = {};
    if (qty > 0) d.sales[date][prodId] = qty; else delete d.sales[date][prodId];
    if (Object.keys(d.sales[date]).length === 0) delete d.sales[date];
  });
  const saveSalesBatch = (date, entries) => commit(d => {
    const clean = {};
    Object.entries(entries).forEach(([pid, q]) => { if (q > 0) clean[pid] = q; });
    if (Object.keys(clean).length) d.sales[date] = clean; else delete d.sales[date];
  }, 'Consumos guardados');

  /* ---- flat product index (for dashboard / search) ---- */
  const productIndex = useMemo(() => {
    const map = {};
    data.cartas.forEach(carta => carta.categories.forEach(cat => cat.subcats.forEach(sub => sub.products.forEach(p => {
      map[p.id] = { ...p, cartaId: carta.id, cartaName: carta.name, catName: cat.name, subName: sub.name };
    }))));
    return map;
  }, [data]);

  /* ---- reservations ---- */
  const addReservation = (r) => {
    const full = { id: uid('rsv'), createdAt: new Date().toISOString(), status:'nueva', ...r };
    commit(d => { if (!d.reservations) d.reservations = []; d.reservations.unshift(full); });
    if (online === true) apiAddReservation(full); // persistir en el servidor (cliente sin clave)
  };
  const updateReservation = (id, patch) => commit(d => {
    const r = (d.reservations||[]).find(x => x.id === id); if (r) Object.assign(r, patch);
  });
  const deleteReservation = (id) => commit(d => { d.reservations = (d.reservations||[]).filter(x => x.id !== id); }, 'Reserva eliminada');

  /* ---- export / import ---- */
  const exportData = () => {
    const blob = new Blob([JSON.stringify(data, null, 2)], { type:'application/json' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url; a.download = `granchimu-datos-${todayISO()}.json`; a.click();
    URL.revokeObjectURL(url);
    toast('Datos exportados');
  };
  const importData = (json) => {
    try {
      const parsed = typeof json === 'string' ? JSON.parse(json) : json;
      if (!parsed.cartas || !parsed.brand) throw new Error('formato');
      commit(d => { Object.assign(d, parsed); }, 'Datos importados');
    } catch (e) { toast('Archivo inválido', 'err'); }
  };
  const resetData = () => { commit(d => { const s = JSON.parse(JSON.stringify(window.SEED_DATA)); Object.keys(d).forEach(k=>delete d[k]); Object.assign(d, s); }, 'Datos restablecidos'); };

  /* ---- email (EmailJS · sin servidor) ---- */
  const sendMail = async ({ to, subject, message }) => {
    const cfg = data.brand.emailjs;
    if (!cfg || !cfg.enabled || !cfg.publicKey || !cfg.serviceId || !cfg.templateId || !to) return false;
    try {
      if (!window.emailjs) {
        await new Promise((res, rej) => {
          const s = document.createElement('script');
          s.src = 'https://cdn.jsdelivr.net/npm/@emailjs/browser@4/dist/email.min.js';
          s.onload = res; s.onerror = rej; document.head.appendChild(s);
        });
      }
      window.emailjs.init({ publicKey: cfg.publicKey });
      await window.emailjs.send(cfg.serviceId, cfg.templateId, { to_email: to, subject, message, from_name: data.brand.name });
      return true;
    } catch (e) { console.warn('EmailJS error', e); return false; }
  };

  /* ---- usuarios ---- */
  const addUser = (u) => commit(d => {
    if (!d.users) d.users = [];
    d.users.push({ id: uid('user'), name:u.name||'', username:(u.username||'').trim().toLowerCase(), password:u.password||'', role:u.role||'editor', active:true });
  }, 'Usuario creado');
  const updateUser = (id, patch) => commit(d => {
    const u = (d.users||[]).find(x => x.id === id);
    if (u) { if (patch.username) patch.username = patch.username.trim().toLowerCase(); Object.assign(u, patch); }
  }, 'Usuario actualizado');
  const deleteUser = (id) => commit(d => { d.users = (d.users||[]).filter(x => x.id !== id); }, 'Usuario eliminado');

  /* ---- refrescar desde el servidor ---- */
  const refreshFromServer = async () => {
    const server = await apiGet();
    if (server) { setData(normalize(server)); setOnline(true); toast('Datos actualizados desde el servidor'); }
    else toast('No se pudo conectar al servidor', 'err');
  };

  /* ---- auth (usuarios + roles) ---- */
  const login = (username, pass) => {
    const u = (data.users||[]).find(x => x.active && x.username === (username||'').trim().toLowerCase() && x.password === pass);
    const legacy = (!username && pass === data.brand.adminPass);
    if (u || legacy) {
      const user = u || { id:'u-admin', name:'Administrador', username:'admin', role:'admin' };
      setAdmin(true); setCurrentUser(user); adminRef.current = true;
      sessionStorage.setItem(LS_SESSION,'1');
      sessionStorage.setItem(LS_USER, JSON.stringify(user));
      return true;
    }
    return false;
  };
  const logout = () => {
    setAdmin(false); setCurrentUser(null); adminRef.current = false;
    sessionStorage.removeItem(LS_SESSION); sessionStorage.removeItem(LS_USER);
  };
  const can = (perm) => {
    if (!currentUser) return false;
    const role = ROLES[currentUser.role] || ROLES.admin;
    return role.perms.includes(perm);
  };

  const store = {
    data, find, money, todayISO, fileToDataURL, uid,
    toast, undo, admin, currentUser, login, logout, can, ROLES,
    online, syncing, refreshFromServer, publish,
    updateBrand,
    addCategory, updateCategory, deleteCategory, moveCategory,
    addSub, updateSub, deleteSub, moveSub,
    blankProduct, addProduct, updateProduct, deleteProduct, moveProduct, reassignProduct,
    setSale, saveSalesBatch, productIndex,
    addReservation, updateReservation, deleteReservation,
    addUser, updateUser, deleteUser,
    exportData, importData, resetData, sendMail,
  };

  return (
    <StoreCtx.Provider value={store}>
      {children}
      <div className="toast-wrap">
        {toasts.map(t => (
          <div key={t.id} className={'toast ' + (t.kind==='ok'?'ok':'')}>
            <Icon name={t.kind==='ok'?'check':'alert'} size={15}/>{t.msg}
          </div>
        ))}
      </div>
    </StoreCtx.Provider>
  );
}

window.StoreProvider = StoreProvider;
window.useStore = useStore;
window.gcMoney = money;
window.gcTodayISO = todayISO;
