// supabase.jsx — client, auth, and customer persistence.
// The publishable key is safe to ship: Row Level Security protects the data.

const SUPABASE_URL = 'https://ysvxpakiwrudnbyvoghc.supabase.co';
const SUPABASE_KEY = 'sb_publishable_gF7hQ4990H-2ApO7A1WOOg_FIXgGWaC';

const sbClient = supabase.createClient(SUPABASE_URL, SUPABASE_KEY, {
  auth: { persistSession: true, autoRefreshToken: true, detectSessionInUrl: true },
});

// ── mappers: DB row <-> the app's customer object ──
// The full UI customer object lives in the `data` JSONB column. A few fields are
// promoted to real columns (name, nurturing, ready_date, confirmed) for queries + RLS.
function dbToUi(row) {
  const data = row.data || {};
  return {
    ...data,
    id: row.id,                                   // DB uuid is canonical
    name: data.name ?? row.name,
    nurturing: row.nurturing ?? data.nurturing ?? false,
    readyDate: row.ready_date ?? data.readyDate ?? null,
  };
}

function uiToDb(cust, userId) {
  const { id, ...doc } = cust;                     // strip client id; DB owns id
  const out = {
    name: cust.name || 'Unnamed',
    nurturing: !!cust.nurturing,
    ready_date: cust.readyDate || null,
    confirmed: cust.confirmed ?? true,
    data: doc,
  };
  if (userId) out.user_id = userId;
  return out;
}

// ── auth ──
const SBAuth = {
  async getSession() {
    const { data } = await sbClient.auth.getSession();
    return data.session;
  },
  onChange(cb) {
    return sbClient.auth.onAuthStateChange((_event, session) => cb(session));
  },
  async signIn(email) {
    return sbClient.auth.signInWithOtp({
      email: email.trim(),
      options: { emailRedirectTo: window.location.origin + window.location.pathname },
    });
  },
  async verifyCode(email, token) {
    return sbClient.auth.verifyOtp({ email: email.trim(), token: token.trim(), type: 'email' });
  },
  async signOut() {
    return sbClient.auth.signOut();
  },
};

// ── customer CRUD ──
// ── raw DB ops (throw only on network failure; API errors return null) ──
async function rawCreate(cust, userId) {
  const { data, error } = await sbClient.from('customers').insert(uiToDb(cust, userId)).select().single();
  if (error) { console.error('create api error', error); return null; }
  return dbToUi(data);
}
async function rawUpdate(cust) {
  const { data, error } = await sbClient.from('customers').update(uiToDb(cust)).eq('id', cust.id).select().single();
  if (error) { console.error('update api error', error); return null; }
  return dbToUi(data);
}
async function rawRemove(id) {
  const { error } = await sbClient.from('customers').update({ deleted_at: new Date().toISOString() }).eq('id', id);
  if (error) console.error('remove api error', error);
}

// ── offline write queue (patchy showroom signal) ──
const QKEY = 'alexcrm_pending_ops';
const loadQ = () => { try { return JSON.parse(localStorage.getItem(QKEY) || '[]'); } catch { return []; } };
const saveQ = (q) => { try { localStorage.setItem(QKEY, JSON.stringify(q)); } catch {} };
const enqueue = (op) => { const q = loadQ(); q.push(op); saveQ(q); };
let flushing = false;
async function flushQueue() {
  if (flushing || !navigator.onLine) return;
  flushing = true;
  try {
    let q = loadQ();
    const idMap = {};                              // temp id -> real id within this flush
    while (q.length) {
      const op = q[0];
      try {
        if (op.type === 'create') {
          const saved = await rawCreate(op.cust, op.userId);
          if (saved && op.cust.id) idMap[op.cust.id] = saved.id;
        } else if (op.type === 'update') {
          await rawUpdate({ ...op.cust, id: idMap[op.cust.id] || op.cust.id });
        } else if (op.type === 'remove') {
          await rawRemove(idMap[op.id] || op.id);
        }
        q.shift(); saveQ(q);
      } catch (e) { break; }                        // still offline — keep queue, retry later
    }
  } finally { flushing = false; }
}

const SBData = {
  async load() {
    const { data, error } = await sbClient
      .from('customers').select('*').is('deleted_at', null)
      .order('created_at', { ascending: false });
    if (error) { console.error('SBData.load', error); return []; }
    flushQueue();                                  // opportunistic flush on load
    return data.map(dbToUi);
  },
  async create(cust, userId) {
    try { return await rawCreate(cust, userId); }
    catch (e) { console.warn('offline → queued create', e); enqueue({ type: 'create', cust, userId }); return null; }
  },
  async update(cust) {
    try { return await rawUpdate(cust); }
    catch (e) { console.warn('offline → queued update', e); enqueue({ type: 'update', cust }); return null; }
  },
  async remove(id) {
    try { return await rawRemove(id); }
    catch (e) { console.warn('offline → queued remove', e); enqueue({ type: 'remove', id }); }
  },
};

if (typeof window !== 'undefined') window.addEventListener('online', flushQueue);

// ── personal to-do items (Today screen, not linked to customers) ──
const SBTodos = {
  async load() {
    const { data, error } = await sbClient.from('todos').select('*').order('created_at', { ascending: true });
    if (error) { console.error('SBTodos.load', error); return []; }
    return data;
  },
  async add(text, userId) {
    const { data, error } = await sbClient.from('todos').insert({ text, user_id: userId }).select().single();
    if (error) { console.error('SBTodos.add', error); return null; }
    return data;
  },
  async setDone(id, done) {
    const { error } = await sbClient.from('todos').update({ done }).eq('id', id);
    if (error) console.error('SBTodos.setDone', error);
  },
  async remove(id) {
    const { error } = await sbClient.from('todos').delete().eq('id', id);
    if (error) console.error('SBTodos.remove', error);
  },
};

// ── AI capture (Cloudflare Worker reads a photo with Claude Haiku vision) ──
// Set AI_API to the deployed Worker URL (see worker/). Until then, photo→AI is off.
const AI_API = window.AI_API_URL || 'https://alex-crm-api.aeguevara0204.workers.dev';
const AI = {
  configured() { return !AI_API.includes('REPLACE'); },
  async fromPhoto(base64, mediaType) {
    const { data } = await sbClient.auth.getSession();
    const token = data.session && data.session.access_token;
    const res = await fetch(AI_API + '/capture/photo', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + token },
      body: JSON.stringify({ image: base64, mediaType, today: (window.todayISO ? todayISO() : new Date().toISOString().slice(0, 10)) }),
    });
    if (!res.ok) { const t = await res.text().catch(() => ''); throw new Error('AI ' + res.status + ' ' + t); }
    return res.json();
  },
  // Chat assistant: one Anthropic turn. messages = Anthropic-format array,
  // customers = compact pipeline snapshot. Returns { content, stop_reason }.
  async chat(messages, customers) {
    const { data } = await sbClient.auth.getSession();
    const token = data.session && data.session.access_token;
    const res = await fetch(AI_API + '/chat', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + token },
      body: JSON.stringify({
        messages, customers,
        today: (window.todayISO ? todayISO() : new Date().toISOString().slice(0, 10)),
      }),
    });
    if (!res.ok) { const t = await res.text().catch(() => ''); throw new Error('chat ' + res.status + ' ' + t); }
    return res.json();
  },
};

// ── Web push subscription ──
const VAPID_PUBLIC = 'BDQsTPR2UTHueusI52tbLmeMEVQTz5AD4PBjvKti8RlNFvSJ7lbIShnOWzYZxO4WnFgl5Bj2j3QP323Sl0gH9AA';
function urlB64ToUint8(base64) {
  const pad = '='.repeat((4 - (base64.length % 4)) % 4);
  const b64 = (base64 + pad).replace(/-/g, '+').replace(/_/g, '/');
  const raw = atob(b64); const arr = new Uint8Array(raw.length);
  for (let i = 0; i < raw.length; i++) arr[i] = raw.charCodeAt(i);
  return arr;
}
const Push = {
  supported() { return 'serviceWorker' in navigator && 'PushManager' in window && 'Notification' in window; },
  async status() {
    if (!Push.supported()) return 'unsupported';
    if (Notification.permission === 'denied') return 'denied';
    const reg = await navigator.serviceWorker.ready;
    const sub = await reg.pushManager.getSubscription();
    return sub ? 'on' : 'off';
  },
  async enable() {
    if (!Push.supported()) return { ok: false, reason: 'unsupported' };
    const perm = await Notification.requestPermission();
    if (perm !== 'granted') return { ok: false, reason: 'denied' };
    const reg = await navigator.serviceWorker.ready;
    let sub = await reg.pushManager.getSubscription();
    if (!sub) sub = await reg.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: urlB64ToUint8(VAPID_PUBLIC) });
    const j = sub.toJSON();
    const { data } = await sbClient.auth.getSession();
    const uid = data.session && data.session.user && data.session.user.id;
    const { error } = await sbClient.from('push_subscriptions').upsert({
      user_id: uid, endpoint: j.endpoint, p256dh: j.keys.p256dh, auth: j.keys.auth, user_agent: navigator.userAgent,
    }, { onConflict: 'endpoint' });
    if (error) { console.error('push save', error); return { ok: false, reason: 'save_failed' }; }
    return { ok: true };
  },
};

Object.assign(window, { sbClient, SBAuth, SBData, SBTodos, AI, Push });
