// pwa-storage.jsx — IndexedDB unifié pour le PWA
// 2 stores :
//   - satellite_cache : résultats BAN / IGN / PVGIS (TTL 7 jours)
//   - capture_queue   : chunks vidéo/audio offline en attente d'upload
// Exposé via window.AE_PWA_DB { sat: {get,set,clear}, queue: {add,list,remove,flush} }

(function () {
  const DB_NAME = 'ae_pwa_v1';
  const DB_VERSION = 1;
  const STORE_SAT   = 'satellite_cache';
  const STORE_QUEUE = 'capture_queue';
  const SAT_TTL_MS = 7 * 24 * 3600 * 1000; // 7 jours

  let _dbPromise = null;
  function openDb() {
    if (_dbPromise) return _dbPromise;
    _dbPromise = new Promise((resolve, reject) => {
      const req = indexedDB.open(DB_NAME, DB_VERSION);
      req.onupgradeneeded = (e) => {
        const db = e.target.result;
        if (!db.objectStoreNames.contains(STORE_SAT))   db.createObjectStore(STORE_SAT,   { keyPath: 'key' });
        if (!db.objectStoreNames.contains(STORE_QUEUE)) db.createObjectStore(STORE_QUEUE, { keyPath: 'id', autoIncrement: true });
      };
      req.onsuccess = (e) => resolve(e.target.result);
      req.onerror   = (e) => reject(e.target.error);
    });
    return _dbPromise;
  }

  function tx(storeName, mode = 'readonly') {
    return openDb().then(db => {
      const t = db.transaction(storeName, mode);
      return { store: t.objectStore(storeName), tx: t };
    });
  }

  // ────────── Satellite cache ──────────
  // Clé canonique : `${lat.toFixed(5)}_${lon.toFixed(5)}` ou adresse normalisée
  async function satGet(key) {
    try {
      const { store } = await tx(STORE_SAT);
      return new Promise(resolve => {
        const r = store.get(key);
        r.onsuccess = () => {
          const rec = r.result;
          if (!rec) return resolve(null);
          if (Date.now() - (rec.cachedAt || 0) > SAT_TTL_MS) {
            satDelete(key); // expiré → purge
            return resolve(null);
          }
          resolve(rec.data);
        };
        r.onerror = () => resolve(null);
      });
    } catch (_) { return null; }
  }
  async function satSet(key, data) {
    try {
      const { store } = await tx(STORE_SAT, 'readwrite');
      store.put({ key, data, cachedAt: Date.now() });
    } catch (_) { /* fail-silent */ }
  }
  async function satDelete(key) {
    try {
      const { store } = await tx(STORE_SAT, 'readwrite');
      store.delete(key);
    } catch (_) {}
  }
  async function satClear() {
    try {
      const { store } = await tx(STORE_SAT, 'readwrite');
      store.clear();
    } catch (_) {}
  }
  function satKeyForLatLon(lat, lon) {
    if (lat == null || lon == null) return null;
    return `${parseFloat(lat).toFixed(5)}_${parseFloat(lon).toFixed(5)}`;
  }

  // ────────── Queue offline upload (tech-capture chunks) ──────────
  // Item : { id (auto), visitId, kind: 'video'|'audio'|'photo', blob, mimeType, size, capturedAt, attempts }
  async function queueAdd(item) {
    try {
      const { store, tx: t } = await tx(STORE_QUEUE, 'readwrite');
      const enriched = { ...item, attempts: 0, queuedAt: new Date().toISOString() };
      return new Promise((resolve) => {
        const r = store.add(enriched);
        r.onsuccess = () => resolve(r.result);
        t.oncomplete = () => window.dispatchEvent(new CustomEvent('ae:queue-changed'));
      });
    } catch (e) { console.warn('[pwa-storage] queueAdd erreur:', e); return null; }
  }
  async function queueList() {
    try {
      const { store } = await tx(STORE_QUEUE);
      return new Promise(resolve => {
        const r = store.getAll();
        r.onsuccess = () => resolve(r.result || []);
        r.onerror = () => resolve([]);
      });
    } catch (_) { return []; }
  }
  async function queueRemove(id) {
    try {
      const { store, tx: t } = await tx(STORE_QUEUE, 'readwrite');
      store.delete(id);
      t.oncomplete = () => window.dispatchEvent(new CustomEvent('ae:queue-changed'));
    } catch (_) {}
  }
  async function queueIncrementAttempts(id) {
    try {
      const { store } = await tx(STORE_QUEUE, 'readwrite');
      const r = store.get(id);
      r.onsuccess = () => {
        const item = r.result;
        if (item) {
          item.attempts = (item.attempts || 0) + 1;
          item.lastAttemptAt = new Date().toISOString();
          store.put(item);
        }
      };
    } catch (_) {}
  }

  // Flush : tente l'upload de tous les items de la queue.
  // Utilisé quand le réseau revient (event 'online' ou bouton manuel).
  async function queueFlush(apiBase) {
    if (!navigator.onLine) return { sent: 0, pending: 0, errors: 0 };
    const items = await queueList();
    if (!items.length) return { sent: 0, pending: 0, errors: 0 };
    const API = apiBase || ((window.AE_API && window.AE_API.BASE) || '');
    let sent = 0, errors = 0;
    for (const item of items) {
      try {
        const fd = new FormData();
        fd.append('file', item.blob, `chunk-${item.id}.${item.mimeType?.split('/')[1] || 'bin'}`);
        fd.append('kind', item.kind);
        if (item.capturedAt) fd.append('capturedAt', item.capturedAt);
        const r = await fetch(`${API}/api/visite/${item.visitId}/media`, { method: 'POST', body: fd });
        if (r.ok) {
          await queueRemove(item.id);
          sent++;
        } else {
          await queueIncrementAttempts(item.id);
          errors++;
        }
      } catch (_) {
        await queueIncrementAttempts(item.id);
        errors++;
      }
    }
    const remaining = await queueList();
    window.dispatchEvent(new CustomEvent('ae:queue-flushed', { detail: { sent, errors, pending: remaining.length } }));
    return { sent, pending: remaining.length, errors };
  }

  // Auto-flush quand le réseau revient
  window.addEventListener('online', () => {
    queueFlush().catch(() => {});
  });

  // Exposition globale
  window.AE_PWA_DB = {
    sat:   { get: satGet, set: satSet, delete: satDelete, clear: satClear, keyForLatLon: satKeyForLatLon },
    queue: { add: queueAdd, list: queueList, remove: queueRemove, flush: queueFlush },
  };
})();
