// Supabase data layer
// Leads: parallel page fetch + cache no IndexedDB (abre na hora, atualiza por trás)
// New accounts start empty — no mock seeding.
// Sets window.DB_PROMISE — awaited by app.jsx and crm-app.jsx before render.

window.DB_PROMISE = (async () => {
  window.LEADS   = [];
  window.CLIENTS = [];

  const sb = window.HOROS_SB;
  if (!sb) return;

  // Aguarda workspace estar disponível (setado pelo auth-guard.js)
  const ws = await window.HOROS_WORKSPACE_PROMISE;
  if (!ws) return;

  const userId = window.HOROS_USER && window.HOROS_USER.id;
  if (!userId) return;

  window.HOROS_WORKSPACE = ws;

  const CACHE_KEY = `horos:db:${userId}:${ws.workspace_id}`;
  const TTL       = 60000; // 60 seconds
  const PAGE      = 1000;
  // Lean select — omit heavy JSONB (data/docs) for fast transfer and cache fit
  const COLS      = 'id,user_id,assigned_to,name,initials,seg,cat,phone,email,value,score,temp,col,pipeline_id,archived,created_at,updated_at,origem:data->>origemLead,origem_canal:data->>origemCanal,repasse:data->repasse';

  function normalize(l) {
    const base = l.col === 'aprovado' ? { ...l, col: 'aprovado_final' } : l;
    return { ...base, broker: base.broker || '' };
  }

  // Cache no IndexedDB (cabe dezenas de milhares de leads e vale entre abas e sessões).
  // A tela abre na hora com o que está guardado; os leads atualizados chegam por trás
  // (evento horos:leads-refreshed). Antes era sessionStorage com 60 s: estourava a cota
  // com muitos leads e toda abertura esperava baixar tudo.
  function idb() {
    return new Promise((res, rej) => {
      const r = indexedDB.open('horos-cache', 1);
      r.onupgradeneeded = () => r.result.createObjectStore('kv');
      r.onsuccess = () => res(r.result);
      r.onerror = () => rej(r.error);
    });
  }
  async function readCache() {
    try {
      const db = await idb();
      return await new Promise(res => {
        const q = db.transaction('kv').objectStore('kv').get(CACHE_KEY);
        q.onsuccess = () => res(q.result && Array.isArray(q.result.leads) ? q.result : null);
        q.onerror = () => res(null);
      });
    } catch (e) { return null; }
  }
  async function writeCache(leads) {
    try {
      const db = await idb();
      db.transaction('kv', 'readwrite').objectStore('kv').put({ ts: Date.now(), leads }, CACHE_KEY);
    } catch (e) {}
  }
  try { sessionStorage.removeItem(CACHE_KEY); } catch (e) {}

  // Fetches all leads using parallel page requests instead of serial loop.
  // Strategy: count first (1 request) → fetch all pages simultaneously.
  // For 19248 leads: was ~20 serial requests → now 1 count + 1 parallel batch.
  async function fetchLeads() {
    const { count, error: cntErr } = await sb
      .from('leads')
      .select('*', { count: 'exact', head: true })
      .eq('archived', false);

    if (cntErr) throw cntErr; // erro ≠ lista vazia: quem chama mantém o que já está na tela
    if (!count) return [];

    const numPages = Math.ceil(count / PAGE);
    const pages = await Promise.all(
      Array.from({ length: numPages }, (_, i) =>
        sb.from('leads')
          .select(COLS)
          .eq('archived', false)
          .order('created_at').order('id') // desempate: importação grava vários no mesmo instante
          .range(i * PAGE, (i + 1) * PAGE - 1)
          .then(r => { if (r.error) throw r.error; return r.data || []; })
      )
    );

    const leads = [].concat(...pages).map(normalize);
    // Rodízio: etapa própria do corretor / etapa de cada corretor para o gerente
    return window.enrichLeadStages ? await window.enrichLeadStages(leads) : leads;
  }

  // Recarrega todos os leads (paginado, com as etapas do rodízio) — usado pelo "Atualizar"
  window.reloadLeads = async () => {
    let fresh;
    try { fresh = await fetchLeads(); }
    catch (e) { console.warn('[db] atualizar falhou:', e.message); return window.LEADS || []; }
    window.LEADS = fresh;
    writeCache(fresh);
    return fresh;
  };

  // Guarda as mudanças feitas na tela (mover, arquivar…) para a próxima abertura
  addEventListener('visibilitychange', () => { if (document.hidden && Array.isArray(window.LEADS) && window.LEADS.length) writeCache(window.LEADS); });

  // ── Cache hit: abre na hora e atualiza por trás ───────────────────────────
  const cached = await readCache();
  if (cached && cached.leads.length > 0) {
    window.LEADS = cached.leads;
    // Lead mudado em tela sem esta camada (ex.: etapa em Mensagens) depois do cache → baixa de novo
    let changedAt = 0;
    try { changedAt = +localStorage.getItem('horos-leads-changed-at') || 0; } catch (e) {}
    if (Date.now() - cached.ts > TTL || changedAt > cached.ts) {
      fetchLeads().then(fresh => {
        if (!fresh || (!fresh.length && cached.leads.length > 50)) return; // falha de rede não zera a tela
        window.LEADS = fresh;
        writeCache(fresh);
        window.dispatchEvent(new CustomEvent('horos:leads-refreshed'));
      }).catch(() => {});
    }
    return;
  }

  // ── Cache miss: fetch fresh (parallel pages) ──────────────────────────────
  try {
    window.LEADS = await fetchLeads();
    writeCache(window.LEADS);

    const { data: clientRows, error: clientsErr } = await sb
      .from('clients')
      .select('data')
      .order('created_at');

    if (!clientsErr && clientRows && clientRows.length > 0) {
      window.CLIENTS = clientRows.map(r => r.data);
    }
  } catch(err) {
    console.warn('[db] erro ao carregar dados:', err.message);
    if (!Array.isArray(window.LEADS)) window.LEADS = [];
  }
})();
