// Kolejka „outbox" dla zgłoszeń uszkodzeń — odporna na zerwanie sieci.
// Zgłoszenie (pola + skompresowane zdjęcia jako bloby) zapisujemy w IndexedDB
// z własnym clientUuid ZANIM wyślemy. Wysyłka próbuje od razu; przy braku sieci
// zostaje w kolejce i ponawia się: po zdarzeniu 'online', co ~30 s oraz przy
// następnym otwarciu apki. clientUuid zapewnia idempotencję (brak duplikatów).
(function () {
  const DB_NAME = 'tankfood';
  const STORE = 'incidentOutbox';
  const VERSION = 1;

  function openDB() {
    return new Promise((resolve, reject) => {
      const r = indexedDB.open(DB_NAME, VERSION);
      r.onupgradeneeded = () => {
        const db = r.result;
        if (!db.objectStoreNames.contains(STORE)) db.createObjectStore(STORE, { keyPath: 'id' });
      };
      r.onsuccess = () => resolve(r.result);
      r.onerror = () => reject(r.error);
    });
  }
  const reqP = (r) => new Promise((res, rej) => { r.onsuccess = () => res(r.result); r.onerror = () => rej(r.error); });
  async function withStore(mode, fn) {
    const db = await openDB();
    try {
      const t = db.transaction(STORE, mode);
      const out = await fn(t.objectStore(STORE));
      await new Promise((res, rej) => { t.oncomplete = res; t.onerror = () => rej(t.error); t.onabort = () => rej(t.error); });
      return out;
    } finally { db.close(); }
  }
  const getAll = () => withStore('readonly', s => reqP(s.getAll()));
  const put = (item) => withStore('readwrite', s => reqP(s.put(item)));
  const del = (id) => withStore('readwrite', s => reqP(s.delete(id)));

  const listeners = new Set();
  async function notify() {
    let n = 0;
    try { n = (await getAll()).length; } catch (e) {}
    listeners.forEach(cb => { try { cb(n); } catch (e) {} });
  }
  const uuid = () => (crypto && crypto.randomUUID ? crypto.randomUUID() : 'tf-' + Date.now() + '-' + Math.random().toString(36).slice(2));

  let processing = false;

  async function enqueue({ type = 'uszkodzenie', street, buildingNo, city, catering, photoBlobs = [] }) {
    const id = uuid();
    await put({
      id, status: 'pending', createdAt: Date.now(),
      fields: { type, street, buildingNo, city, catering },
      photos: photoBlobs.map(b => ({ blob: b, type: b.type || 'image/jpeg', key: null })),
      attempts: 0, lastError: null,
    });
    await notify();
    return id;
  }

  // Wysyła jeden element. Rzuca err.offline przy braku sieci (do ponowienia).
  async function sendItem(item) {
    for (const p of item.photos) {
      if (p.key) continue;
      const file = new File([p.blob], 'foto.jpg', { type: p.type || 'image/jpeg' });
      const r = await api.uploadPhoto(file);
      p.key = r.key;
      await put(item); // zapamiętaj klucz od razu — ponowienie nie wyśle zdjęcia drugi raz
    }
    // Idempotentnie po clientUuid: jeśli serwer już to zapisał, zwróci istniejący wiersz.
    await api.createIncident({ ...item.fields, photoKeys: item.photos.map(p => p.key), clientUuid: item.id });
    await del(item.id);
    await notify();
  }

  async function process() {
    if (processing) return;
    if (typeof navigator !== 'undefined' && navigator.onLine === false) return;
    processing = true;
    try {
      const items = (await getAll()).sort((a, b) => a.createdAt - b.createdAt);
      for (const item of items) {
        if (item.status === 'error' && (item.attempts || 0) >= 5) continue; // nie zapętlaj trwałych błędów
        try {
          await sendItem(item);
        } catch (e) {
          if (e && e.offline) break; // brak sieci — reszta poczeka
          item.status = 'error';
          item.attempts = (item.attempts || 0) + 1;
          item.lastError = String((e && e.message) || e).slice(0, 200);
          await put(item);
          await notify();
        }
      }
    } catch (e) {
      // np. IndexedDB niedostępne — nic nie rób, spróbujemy później
    } finally {
      processing = false;
    }
  }

  // Zapisuje i od razu próbuje wysłać. Zwraca 'sent' | 'queued'.
  async function sendNow(payload) {
    const id = await enqueue(payload);
    await process();
    const still = (await getAll()).some(i => i.id === id);
    return still ? 'queued' : 'sent';
  }

  async function pendingCount() { try { return (await getAll()).length; } catch (e) { return 0; } }
  function onChange(cb) { listeners.add(cb); return () => listeners.delete(cb); }

  if (typeof window !== 'undefined') {
    window.addEventListener('online', () => process());
    setInterval(() => process(), 30000);
    setTimeout(() => process(), 1500); // dosyłka zaległych po starcie
  }

  Object.assign(window, { tfOutbox: { enqueue, process, sendNow, pendingCount, onChange } });
})();
