// IndexedDB wrapper minimalista pra fila de jobs da extensão.
// SW pode ser suspenso pelo Chrome a qualquer momento; sem persistência a fila
// some. Aqui guardamos cada job como uma row.
//
// Schema:
//   store 'jobs' (keyPath 'id')
//     id: string  — `${platform}:${source_id}` (mesmo key usado no painel)
//     platform: 'tiktok'|'instagram'
//     kind: 'video'|'image'
//     url: string                — URL CDN do arquivo original
//     cover: string|null
//     author: string|null
//     desc: string|null
//     duration_ms: number
//     status: 'queued'|'downloading'|'uploading'|'done'|'failed'|'blocked'
//     attempts: number
//     error: string|null
//     drive_file_id: string|null
//     bytes: number|null
//     started_at: number|null
//     done_at: number|null
//     created_at: number
//     updated_at: number

const DB_NAME = 'is-ext-db';
const DB_VERSION = 1;
const STORE = 'jobs';

let _dbPromise = null;

function open() {
  if (_dbPromise) return _dbPromise;
  _dbPromise = new Promise((resolve, reject) => {
    const req = indexedDB.open(DB_NAME, DB_VERSION);
    req.onupgradeneeded = (event) => {
      const db = event.target.result;
      if (!db.objectStoreNames.contains(STORE)) {
        const store = db.createObjectStore(STORE, { keyPath: 'id' });
        store.createIndex('status', 'status', { unique: false });
        store.createIndex('platform', 'platform', { unique: false });
        store.createIndex('created_at', 'created_at', { unique: false });
      }
    };
    req.onsuccess = () => resolve(req.result);
    req.onerror = () => reject(req.error);
  });
  return _dbPromise;
}

function tx(mode = 'readonly') {
  return open().then(db => db.transaction(STORE, mode).objectStore(STORE));
}

function reqAsPromise(req) {
  return new Promise((resolve, reject) => {
    req.onsuccess = () => resolve(req.result);
    req.onerror = () => reject(req.error);
  });
}

export async function putJob(job) {
  const now = Date.now();
  const merged = {
    attempts: 0,
    status: 'queued',
    error: null,
    drive_file_id: null,
    bytes: null,
    started_at: null,
    done_at: null,
    created_at: now,
    ...job,
    updated_at: now
  };
  const store = await tx('readwrite');
  await reqAsPromise(store.put(merged));
  return merged;
}

export async function putManyJobs(jobs) {
  const now = Date.now();
  const store = await tx('readwrite');
  let added = 0;
  for (const j of jobs) {
    if (!j?.id) continue;
    // Skip se já existe (dedup)
    const existing = await reqAsPromise(store.get(j.id));
    if (existing) continue;
    const merged = {
      attempts: 0,
      status: 'queued',
      error: null,
      drive_file_id: null,
      bytes: null,
      started_at: null,
      done_at: null,
      created_at: now,
      ...j,
      updated_at: now
    };
    await reqAsPromise(store.put(merged));
    added++;
  }
  return added;
}

export async function getJob(id) {
  const store = await tx();
  return reqAsPromise(store.get(id));
}

export async function updateJob(id, patch) {
  const store = await tx('readwrite');
  const cur = await reqAsPromise(store.get(id));
  if (!cur) return null;
  const updated = { ...cur, ...patch, updated_at: Date.now() };
  await reqAsPromise(store.put(updated));
  return updated;
}

export async function deleteJob(id) {
  const store = await tx('readwrite');
  return reqAsPromise(store.delete(id));
}

export async function listJobs(filter = {}) {
  const store = await tx();
  return new Promise((resolve, reject) => {
    const out = [];
    const req = store.openCursor();
    req.onsuccess = (e) => {
      const cur = e.target.result;
      if (!cur) { resolve(out); return; }
      const v = cur.value;
      let match = true;
      if (filter.status && v.status !== filter.status) match = false;
      if (filter.statusIn && !filter.statusIn.includes(v.status)) match = false;
      if (filter.platform && v.platform !== filter.platform) match = false;
      if (match) out.push(v);
      cur.continue();
    };
    req.onerror = () => reject(req.error);
  });
}

export async function countByStatus() {
  const all = await listJobs();
  const counts = { queued: 0, downloading: 0, processing: 0, uploading: 0, done: 0, failed: 0, blocked: 0, total: all.length };
  for (const j of all) {
    counts[j.status] = (counts[j.status] || 0) + 1;
  }
  return counts;
}

export async function clearDone() {
  const all = await listJobs({ statusIn: ['done', 'failed', 'blocked'] });
  for (const j of all) await deleteJob(j.id);
  return all.length;
}

export async function clearAll() {
  const store = await tx('readwrite');
  return reqAsPromise(store.clear());
}

// Pega 1 job pra processar — atomic: marca como 'downloading' antes de retornar
// pra que outro tick do worker não pegue o mesmo.
export async function claimNextQueued() {
  const all = await listJobs({ status: 'queued' });
  if (!all.length) return null;
  // Ordena por created_at (FIFO)
  all.sort((a, b) => (a.created_at || 0) - (b.created_at || 0));
  const job = all[0];
  return updateJob(job.id, { status: 'downloading', started_at: Date.now(), attempts: (job.attempts || 0) + 1 });
}
