/* global supabase */
// ──────────────── Supabase Client ────────────────
// Wrapper รวม Supabase config + helper functions
// ใช้ผ่าน window.KrubankAPI.* ในไฟล์อื่น

const SUPABASE_URL = "https://rnkmwsjcylyagulaffhs.supabase.co";
// ใช้ Legacy anon key (eyJ...) เพราะ Supabase JS UMD CDN ยังไม่รองรับ sb_publishable_* format
const SUPABASE_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InJua213c2pjeWx5YWd1bGFmZmhzIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzkwOTU2MjAsImV4cCI6MjA5NDY3MTYyMH0.Hh3Y6IhNZo_L6yDEXK9uOoaTF_UWDfXPt5S6YuGHIlE";

// Defensive: ถ้า supabase script ยังโหลดไม่เสร็จ → stub กันแอปพัง
if (!window.supabase || !window.supabase.createClient) {
  console.error("[Supabase] script ยังโหลดไม่เสร็จ — ใช้ stub ชั่วคราว");
  window.supabase = {
    createClient: () => ({
      from: () => ({ select: () => ({ limit: () => Promise.resolve({data:[], error:{message:"not loaded"}}), eq: () => ({ maybeSingle: () => Promise.resolve({data:null, error:null}) }) }), upsert: () => Promise.resolve({error:null}), delete: () => ({ eq: () => Promise.resolve({error:null}) }), order: () => Promise.resolve({data:[], error:null}) }),
      auth: {
        getUser: () => Promise.resolve({data:{user:null}}),
        getSession: () => Promise.resolve({data:{session:null}}),
        onAuthStateChange: () => ({data:{subscription:{unsubscribe:()=>{}}}}),
        signUp: () => Promise.resolve({data:{}, error:{message:"Supabase ไม่พร้อม"}}),
        signInWithPassword: () => Promise.resolve({data:{}, error:{message:"Supabase ไม่พร้อม"}}),
        signInWithIdToken: () => Promise.resolve({data:{}, error:{message:"Supabase ไม่พร้อม"}}),
        signOut: () => Promise.resolve(),
      },
      rpc: () => Promise.resolve({data:null, error:null}),
    }),
  };
}

// สร้าง client ตัวเดียว ใช้ทั่วทั้งแอป
const sb = window.supabase.createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
  auth: {
    persistSession: true,
    autoRefreshToken: true,
    detectSessionInUrl: true,
  },
});

// ──────────────── Health check ────────────────
// เรียกตอนแอปโหลดเพื่อยืนยันว่า connect ได้
async function sbPing() {
  try {
    const { data, error } = await sb.from("settings").select("*").limit(1);
    if (error) {
      console.warn("[Supabase] ping error:", error.message);
      return { ok: false, error: error.message };
    }
    console.log("[Supabase] connected ✓", data?.length, "settings rows");
    return { ok: true };
  } catch (e) {
    console.error("[Supabase] connection failed:", e);
    return { ok: false, error: e.message };
  }
}

// ──────────────── Episodes ────────────────
async function sbFetchEpisodes() {
  const { data, error } = await sb
    .from("episodes")
    .select("*")
    .order("position", { ascending: true });
  if (error) throw error;
  // map snake_case → camelCase ที่ frontend ใช้
  return (data || []).map(row => ({
    n: row.id,
    yt: row.yt,
    emoji: row.emoji,
    title: row.title,
    duration: row.duration,
    desc: row.desc,
    manual: row.manual,
    manualUrl: row.manual_url,
    size: row.size,
    price: row.price,
    free: row.free,
    cover: row.cover,
    uploadedAt: row.uploaded_at,
    buyers: row.buyers || 0,
    position: row.position,
  }));
}

async function sbUpsertEpisode(ep) {
  const row = {
    id: ep.n,
    yt: ep.yt,
    emoji: ep.emoji,
    title: ep.title,
    duration: ep.duration,
    desc: ep.desc,
    manual: ep.manual,
    manual_url: ep.manualUrl,
    size: ep.size,
    price: ep.price,
    free: ep.free || false,
    cover: ep.cover,
    uploaded_at: ep.uploadedAt,
    buyers: ep.buyers || 0,
    position: ep.position ?? ep.n,
  };
  const { error } = await sb.from("episodes").upsert(row);
  if (error) throw error;
}

async function sbDeleteEpisode(id) {
  const { error } = await sb.from("episodes").delete().eq("id", id);
  if (error) throw error;
}

// ──────────────── Settings (key-value) ────────────────
async function sbGetSetting(key, fallback = null) {
  const { data, error } = await sb
    .from("settings")
    .select("value")
    .eq("key", key)
    .maybeSingle();
  if (error) { console.warn("setting error", error); return fallback; }
  return data?.value ?? fallback;
}

async function sbSetSetting(key, value) {
  const { error } = await sb.from("settings").upsert({ key, value });
  if (error) throw error;
}

// ──────────────── Views (ยอดรับชม) ────────────────
async function sbIncrementView(episodeId) {
  // ใช้ RPC function ที่จะสร้างใน SQL
  const { data, error } = await sb.rpc("increment_view", { ep_id: episodeId });
  if (error) console.warn("incrementView error", error);
  return data;
}

async function sbFetchViews() {
  const { data, error } = await sb.from("episode_views").select("*");
  if (error) { console.warn(error); return {}; }
  const map = {};
  (data || []).forEach(r => { map[r.episode_id] = r.count; });
  return map;
}

// ──────────────── Storage / Covers (Stage 5) ────────────────
const COVERS_BUCKET = "covers";

// อัปโหลด blob ขึ้น Storage → คืน public URL
async function sbUploadCover(episodeId, blob, ext = "jpg") {
  const filename = `ep-${episodeId}-${Date.now()}.${ext}`;
  const { error } = await sb.storage
    .from(COVERS_BUCKET)
    .upload(filename, blob, {
      cacheControl: "31536000", // cache 1 ปี
      upsert: false,
      contentType: blob.type || "image/jpeg",
    });
  if (error) throw error;
  const { data } = sb.storage.from(COVERS_BUCKET).getPublicUrl(filename);
  return data.publicUrl;
}

// ลบรูปเก่าเมื่อเปลี่ยนรูปใหม่หรือลบทิ้ง (filename = ตอน parse จาก URL)
async function sbDeleteCover(filename) {
  if (!filename) return;
  try { await sb.storage.from(COVERS_BUCKET).remove([filename]); } catch (e) { console.warn(e); }
}

// แปลง dataURL (base64) → Blob เพื่ออัปเป็นไฟล์
function dataUrlToBlob(dataUrl) {
  const [meta, b64] = dataUrl.split(",");
  const mime = meta.match(/data:(.*?);/)?.[1] || "image/jpeg";
  const bin = atob(b64);
  const arr = new Uint8Array(bin.length);
  for (let i = 0; i < bin.length; i++) arr[i] = bin.charCodeAt(i);
  return new Blob([arr], { type: mime });
}

// ──────────────── Purchases (Stage 4) ────────────────
// คืนเป็น Set<episode_id> ของ approved purchases
async function sbFetchUserPurchases(userId) {
  if (!userId) return new Set();
  const { data, error } = await sb
    .from("purchases")
    .select("episode_id")
    .eq("user_id", userId)
    .eq("status", "approved");
  if (error) { console.warn("fetchPurchases:", error); return new Set(); }
  return new Set((data || []).map(r => r.episode_id));
}

// บันทึก purchase ใหม่ (free unlock หรือ paid)
async function sbRecordPurchase({ userId, episodeId, amount, slipRef, status }) {
  const row = {
    user_id: userId,
    episode_id: episodeId,
    amount: amount || 0,
    slip_ref: slipRef || null,
    status: status || "approved",
    approved_at: status === "approved" ? new Date().toISOString() : null,
  };
  const { error } = await sb.from("purchases").upsert(row, {
    onConflict: "user_id,episode_id",
  });
  if (error) throw error;
}

// เช็คว่า slip ref นี้เคยใช้แล้วหรือยัง (กัน duplicate slip)
async function sbIsSlipUsed(slipRef) {
  if (!slipRef) return false;
  const { data, error } = await sb
    .from("purchases")
    .select("id")
    .eq("slip_ref", slipRef)
    .limit(1);
  if (error) { console.warn("checkSlip:", error); return false; }
  return (data || []).length > 0;
}

// ──────────────── Payments (Stage 7) ────────────────
// บันทึกทุกความพยายามชำระ (approved/pending/rejected/error) ขึ้น Supabase
// เพื่อให้ admin ดูได้ครบทุกอุปกรณ์ (ไม่ใช่แค่ localStorage เครื่องตัวเอง)
async function sbInsertPayment(rec) {
  const row = {
    id: rec.id,
    order_id: rec.orderId,
    user_id: rec.userId || null,
    user_name: rec.userName,
    user_email: rec.userEmail,
    episode_number: rec.episodeNumber,
    episode_title: rec.episodeTitle,
    amount: rec.amount || 0,
    status: rec.status,
    ai: rec.ai || null,
    checks: rec.checks || null,
    slip_image: rec.slipImage || null,
    message: rec.message || null,
    // กันสลิปซ้ำข้ามบัญชี: จองเลขอ้างอิงธนาคารเฉพาะรายการที่ "อนุมัติ" (มี unique index ที่ DB)
    slip_ref: (rec.status === "approved" && rec.ai && rec.ai.referenceId) ? rec.ai.referenceId : null,
    submitted_at: rec.submittedAt || new Date().toISOString(),
    reviewed_at: rec.reviewedAt || null,
    reviewed_by: rec.reviewedBy || null,
  };
  const { error } = await sb.from("payments").upsert(row);
  if (error) throw error;
}

// ดึงรายการชำระทั้งหมด (admin) → map เป็น camelCase ที่ admin panel ใช้
async function sbFetchPayments() {
  const { data, error } = await sb
    .from("payments")
    .select("*")
    .order("submitted_at", { ascending: false });
  if (error) throw error;
  return (data || []).map(r => ({
    id: r.id,
    orderId: r.order_id,
    userId: r.user_id,
    userName: r.user_name,
    userEmail: r.user_email,
    episodeNumber: r.episode_number,
    episodeTitle: r.episode_title,
    amount: r.amount,
    status: r.status,
    ai: r.ai,
    checks: r.checks,
    slipImage: r.slip_image,
    message: r.message,
    submittedAt: r.submitted_at,
    reviewedAt: r.reviewed_at,
    reviewedBy: r.reviewed_by,
  }));
}

// ดึงรายการชำระเงินของ user คนนี้เอง (RLS อนุญาต self-read) → โชว์สถานะสลิปในคลังของฉัน
async function sbFetchMyPayments(userId) {
  if (!userId) return [];
  const { data, error } = await sb
    .from("payments")
    .select("id, episode_number, episode_title, amount, status, message, submitted_at")
    .eq("user_id", userId)
    .order("submitted_at", { ascending: false });
  if (error) { console.warn("fetchMyPayments:", error); return []; }
  return (data || []).map(r => ({
    id: r.id,
    episodeNumber: r.episode_number,
    episodeTitle: r.episode_title,
    amount: r.amount,
    status: r.status,
    message: r.message,
    submittedAt: r.submitted_at,
  }));
}

// เช็คว่า user คนนี้ซื้อ "โปรแกรม/บอท" (สินค้าที่ไม่ใช่บทเรียน) แล้วหรือยัง
// ownership ของสินค้าโปรแกรม = มีแถวใน payments ที่ episode_number = productNumber และ status = approved
// (ใช้ตาราง payments เดิม ไม่ต้องสร้างตารางใหม่)
async function sbHasProductAccess(userId, productNumber) {
  if (!userId || productNumber == null) return false;
  const { data, error } = await sb
    .from("payments")
    .select("id")
    .eq("user_id", userId)
    .eq("episode_number", productNumber)
    .eq("status", "approved")
    .limit(1);
  if (error) { console.warn("hasProductAccess:", error); return false; }
  return (data || []).length > 0;
}

// ดึง payment ของสินค้าโปรแกรม (ล่าสุด ที่ approved) ของ user → ใช้อ่าน license key ที่แอดมินออกให้
async function sbFetchUserProductPayment(userId, productNumber) {
  if (!userId || productNumber == null) return null;
  const { data, error } = await sb
    .from("payments")
    .select("id, status, ai, episode_title, submitted_at")
    .eq("user_id", userId)
    .eq("episode_number", productNumber)
    .eq("status", "approved")
    .order("submitted_at", { ascending: false })
    .limit(1);
  if (error) { console.warn("fetchUserProductPayment:", error); return null; }
  return (data && data[0]) || null;
}

// admin: บันทึก license key ลงใน payment — เก็บใน ai.licenseKey (jsonb) เพื่อไม่ต้องเพิ่มคอลัมน์ใหม่
async function sbSetPaymentLicense(id, licenseKey, currentAi) {
  const ai = { ...(currentAi || {}), licenseKey: (licenseKey || "").trim() || null };
  const { error } = await sb.from("payments").update({ ai }).eq("id", id);
  if (error) throw error;
  return ai;
}

// ─── License pool (Stage 8-10) ───
// ลูกค้าเรียกหลังซื้อสำเร็จ → ได้คีย์ว่างของ "โปรแกรม" ที่ซื้อ (atomic, idempotent)
// product = 'autogen' | 'farmstudio' · episodeNo = sentinel ในตาราง payments (9001/9002)
async function sbClaimLicense(product, episodeNo) {
  const { data, error } = await sb.rpc("claim_license", { p_product: product, p_episode: episodeNo });
  if (error) { console.warn("claimLicense:", error); return null; }
  return data || null;
}

// admin: เพิ่มคีย์เข้าคลัง ตามโปรแกรม+แผน — ข้ามคีย์ซ้ำให้อัตโนมัติ
async function sbAddLicenses(keys, product, plan) {
  const rows = (keys || []).map(k => ({ key: k, product: product || null, plan: plan || null }));
  if (!rows.length) return 0;
  const { error } = await sb.from("licenses").upsert(rows, { onConflict: "key", ignoreDuplicates: true });
  if (error) throw error;
  return rows.length;
}

// admin: นับคีย์ว่าง / แจกไปแล้ว ของโปรแกรมหนึ่ง แยกตามแผน
async function sbLicenseCounts(product) {
  const q = (plan, status) =>
    sb.from("licenses").select("*", { count: "exact", head: true })
      .eq("product", product).eq("plan", plan).eq("status", status);
  const [mf, mu, lf, lu] = await Promise.all([
    q("monthly", "free"), q("monthly", "assigned"),
    q("lifetime", "free"), q("lifetime", "assigned"),
  ]);
  return {
    monthly:  { free: mf.count || 0, used: mu.count || 0 },
    lifetime: { free: lf.count || 0, used: lu.count || 0 },
  };
}

async function sbUpdatePaymentStatus(id, status) {
  const { error } = await sb
    .from("payments")
    .update({ status, reviewed_at: new Date().toISOString(), reviewed_by: "admin" })
    .eq("id", id);
  if (error) throw error;
}

async function sbDeletePayment(id) {
  const { error } = await sb.from("payments").delete().eq("id", id);
  if (error) throw error;
}

// รวมรายการชำระเงิน = รายการใหม่ (payments, มีสลิป+AI) + ประวัติขายเก่า (purchases, amount>0)
// กันซ้ำ: ถ้า (user × episode) มีใน payments แล้ว จะไม่เอา purchase เก่ามาแสดงอีก
// historical:true = รายการเก่าที่ไม่มีรูปสลิป/ผล AI
async function sbFetchPaymentsWithHistory(episodes) {
  const epTitle = {};
  (episodes || []).forEach(e => { epTitle[e.n] = e.title; });

  const [payments, purRes, profRes] = await Promise.all([
    sbFetchPayments(),
    sb.from("purchases").select("*"),
    sb.from("profiles").select("id, name, email"),
  ]);
  if (purRes.error) console.warn("history purchases:", purRes.error);
  if (profRes.error) console.warn("history profiles:", profRes.error);

  const profiles = {};
  (profRes.data || []).forEach(p => { profiles[p.id] = p; });

  const seen = new Set(payments.map(p => `${p.userId}|${p.episodeNumber}`));

  const historical = (purRes.data || [])
    .filter(r => (r.amount || 0) > 0)                                  // เฉพาะที่จ่ายเงินจริง (ตัด free unlock)
    .filter(r => !seen.has(`${r.user_id}|${r.episode_id}`))            // กันซ้ำกับ payments
    .map(r => {
      const prof = profiles[r.user_id] || {};
      return {
        id: "purchase_" + r.id,
        orderId: null,
        userId: r.user_id,
        userName: prof.name || (prof.email ? prof.email.split("@")[0] : "—"),
        userEmail: prof.email || "",
        episodeNumber: r.episode_id,
        episodeTitle: epTitle[r.episode_id] || `บทที่ ${r.episode_id}`,
        amount: r.amount,
        status: r.status || "approved",
        ai: null, checks: null, slipImage: null, message: null,
        submittedAt: r.approved_at || r.created_at,
        reviewedAt: r.approved_at,
        reviewedBy: null,
        historical: true,
      };
    });

  const all = [...payments, ...historical];
  all.sort((a, b) => new Date(b.submittedAt) - new Date(a.submittedAt));
  return all;
}

// ──────────────── Members (Stage 7) ────────────────
// ดึงสมาชิกจาก profiles + นับจำนวนบทที่ปลดล็อก (approved) จาก purchases
async function sbFetchMembers() {
  const [{ data: profs, error: e1 }, { data: purch, error: e2 }] = await Promise.all([
    sb.from("profiles").select("*").order("created_at", { ascending: false }),
    sb.from("purchases").select("user_id, status"),
  ]);
  if (e1) throw e1;
  if (e2) console.warn("fetchMembers purchases:", e2);
  const counts = {};
  (purch || []).forEach(p => {
    if (p.status === "approved") counts[p.user_id] = (counts[p.user_id] || 0) + 1;
  });
  return (profs || []).map(p => ({
    id: p.id,
    name: p.name || (p.email ? p.email.split("@")[0] : "ผู้ใช้"),
    email: p.email || "",
    avatar: p.avatar || "",
    provider: p.provider || "email",
    createdAt: p.created_at,
    unlockedCount: counts[p.id] || 0,
  }));
}

// admin ปลดล็อกหลายบทให้ user คนอื่น
async function sbGrantEpisodesToUser(userId, episodeIds) {
  if (!userId || !episodeIds || !episodeIds.length) return;
  const rows = episodeIds.map(eid => ({
    user_id: userId,
    episode_id: eid,
    amount: 0,
    status: "approved",
    approved_at: new Date().toISOString(),
  }));
  const { error } = await sb.from("purchases").upsert(rows, { onConflict: "user_id,episode_id" });
  if (error) throw error;
}

// admin ยกเลิกบทเรียนทั้งหมดของ user
async function sbRevokeAllFromUser(userId) {
  if (!userId) return;
  const { error } = await sb.from("purchases").delete().eq("user_id", userId);
  if (error) throw error;
}

// admin: ให้สิทธิ์โปรแกรมแก่สมาชิก (ลูกค้าเก่าที่ซื้อนอกระบบ)
// สร้าง payment approved + sentinel โปรแกรม + ใส่ License key (ถ้ามี) — ต้องรัน stage 13 (admin insert RLS)
async function sbAdminGrantProgram({ userId, userName, userEmail, productNo, productName, plan, planLabel, licenseKey }) {
  if (!userId || productNo == null) throw new Error("ข้อมูลไม่ครบ");
  const now = new Date().toISOString();
  const row = {
    id: `grant_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
    order_id: `ADMIN-GRANT-${productNo}-${Date.now().toString(36).slice(-5).toUpperCase()}`,
    user_id: userId,
    user_name: userName || null,
    user_email: userEmail || null,
    episode_number: productNo,
    episode_title: `${productName}${planLabel ? ` · แผน${planLabel}` : ""} (เพิ่มโดยแอดมิน)`,
    amount: 0,
    status: "approved",
    ai: { plan: plan || "monthly", licenseKey: (licenseKey || "").trim() || null, grantedByAdmin: true },
    checks: null,
    slip_image: null,
    message: "เพิ่มสิทธิ์โดยแอดมิน (ลูกค้าเก่า)",
    submitted_at: now,
    reviewed_at: now,
    reviewed_by: "admin",
  };
  const { error } = await sb.from("payments").insert(row);
  if (error) throw error;
  return row;
}

// ──────────────── Affiliate / Commission (Stage 12) ────────────────
// คืนโค้ดนายหน้าของ user ปัจจุบัน (สร้างให้ครั้งแรกถ้ายังไม่มี)
async function sbGetOrCreateRefCode() {
  const { data, error } = await sb.rpc("get_or_create_ref_code");
  if (error) { console.warn("getOrCreateRefCode:", error); return null; }
  return data || null;
}

// ผูกนายหน้าให้ user ปัจจุบัน (เรียกหลังสมัคร/ล็อกอินถ้ามี ?ref=) — server set ครั้งเดียว
async function sbSetReferrer(refCode) {
  if (!refCode) return false;
  const { data, error } = await sb.rpc("set_referrer", { p_ref_code: refCode });
  if (error) { console.warn("setReferrer:", error); return false; }
  return !!data;
}

// บันทึกค่าคอมจาก payment ที่อนุมัติแล้ว (idempotent · server คิดยอดเอง) — คืนยอดค่าคอม
async function sbRecordCommission(paymentId) {
  if (!paymentId) return 0;
  const { data, error } = await sb.rpc("record_commission", { p_payment_id: paymentId });
  if (error) { console.warn("recordCommission:", error); return 0; }
  return data || 0;
}

// นายหน้าดูรายชื่อ "คนที่สมัครผ่านลิงก์ของตัวเอง" (อีเมล + วันที่ + ซื้อแล้วหรือยัง) ผ่าน RPC ปลอดภัย
async function sbFetchMyReferrals() {
  const { data, error } = await sb.rpc("my_referrals");
  if (error) { console.warn("fetchMyReferrals:", error); return []; }
  return data || [];
}

// นายหน้าดูค่าคอมของตัวเอง
async function sbFetchMyCommissions(userId) {
  if (!userId) return [];
  const { data, error } = await sb
    .from("commissions").select("*")
    .eq("referrer_id", userId)
    .order("created_at", { ascending: false });
  if (error) { console.warn("fetchMyCommissions:", error); return []; }
  return data || [];
}

// admin: ดูค่าคอมทั้งหมด + เติมชื่อ/อีเมลของนายหน้า & ลูกค้าที่ถูกชวน
async function sbFetchAllCommissions() {
  const [{ data: comms, error: e1 }, { data: profs, error: e2 }] = await Promise.all([
    sb.from("commissions").select("*").order("created_at", { ascending: false }),
    sb.from("profiles").select("id, name, email"),
  ]);
  if (e1) throw e1;
  if (e2) console.warn("commissions profiles:", e2);
  const pmap = {};
  (profs || []).forEach(p => { pmap[p.id] = p; });
  return (comms || []).map(c => ({
    ...c,
    referrerName:  pmap[c.referrer_id]?.name  || "—",
    referrerEmail: pmap[c.referrer_id]?.email || "",
    referredName:  pmap[c.referred_id]?.name  || "—",
    referredEmail: pmap[c.referred_id]?.email || "",
  }));
}

// admin: ทำเครื่องหมาย "จ่ายแล้ว"/"ยังไม่จ่าย" หลายรายการพร้อมกัน
async function sbMarkCommissionsPaid(ids, paid = true) {
  if (!ids || !ids.length) return;
  const patch = paid
    ? { status: "paid", paid_at: new Date().toISOString() }
    : { status: "unpaid", paid_at: null };
  const { error } = await sb.from("commissions").update(patch).in("id", ids);
  if (error) throw error;
}

// admin: ลบรายการค่าคอม (กรณีคืนเงิน/สลิปปลอม)
async function sbDeleteCommission(id) {
  if (!id) return;
  const { error } = await sb.from("commissions").delete().eq("id", id);
  if (error) throw error;
}

// ──────────────── Auth ────────────────
async function sbSignUp({ email, password, name }) {
  return await sb.auth.signUp({
    email,
    password,
    options: { data: { name } },
  });
}

async function sbSignIn({ email, password }) {
  return await sb.auth.signInWithPassword({ email, password });
}

async function sbSignInWithGoogleCredential(credential) {
  return await sb.auth.signInWithIdToken({
    provider: "google",
    token: credential,
  });
}

async function sbSignOut() {
  return await sb.auth.signOut();
}

async function sbGetUser() {
  const { data } = await sb.auth.getUser();
  return data?.user || null;
}

// ──────────────── Export ────────────────
// expose auth ตรงๆ ด้วย กันบางบราวเซอร์ที่ optional chain เป็น undefined
window.KrubankAPI = {
  client: sb,
  auth: sb && sb.auth ? sb.auth : null,
  ping: sbPing,
  // episodes
  fetchEpisodes: sbFetchEpisodes,
  upsertEpisode: sbUpsertEpisode,
  deleteEpisode: sbDeleteEpisode,
  // settings
  getSetting: sbGetSetting,
  setSetting: sbSetSetting,
  // views
  incrementView: sbIncrementView,
  fetchViews: sbFetchViews,
  // purchases
  fetchUserPurchases: sbFetchUserPurchases,
  recordPurchase: sbRecordPurchase,
  isSlipUsed: sbIsSlipUsed,
  // payments (Stage 7)
  insertPayment: sbInsertPayment,
  fetchPayments: sbFetchPayments,
  fetchMyPayments: sbFetchMyPayments,
  fetchPaymentsWithHistory: sbFetchPaymentsWithHistory,
  updatePaymentStatus: sbUpdatePaymentStatus,
  deletePayment: sbDeletePayment,
  hasProductAccess: sbHasProductAccess,
  fetchUserProductPayment: sbFetchUserProductPayment,
  setPaymentLicense: sbSetPaymentLicense,
  claimLicense: sbClaimLicense,
  addLicenses: sbAddLicenses,
  licenseCounts: sbLicenseCounts,
  // members (Stage 7)
  fetchMembers: sbFetchMembers,
  grantEpisodesToUser: sbGrantEpisodesToUser,
  revokeAllFromUser: sbRevokeAllFromUser,
  adminGrantProgram: sbAdminGrantProgram,
  // affiliate / commission (Stage 12)
  getOrCreateRefCode: sbGetOrCreateRefCode,
  setReferrer: sbSetReferrer,
  recordCommission: sbRecordCommission,
  fetchMyReferrals: sbFetchMyReferrals,
  fetchMyCommissions: sbFetchMyCommissions,
  fetchAllCommissions: sbFetchAllCommissions,
  markCommissionsPaid: sbMarkCommissionsPaid,
  deleteCommission: sbDeleteCommission,
  // storage
  uploadCover: sbUploadCover,
  deleteCover: sbDeleteCover,
  dataUrlToBlob,
  // auth
  signUp: sbSignUp,
  signIn: sbSignIn,
  signInWithGoogleCredential: sbSignInWithGoogleCredential,
  signOut: sbSignOut,
  getUser: sbGetUser,
};

// debug log สั้นๆ ดูใน console
console.log("[Supabase] SB ready:", {
  hasClient: !!window.KrubankAPI.client,
  hasAuth: !!(window.KrubankAPI.client && window.KrubankAPI.client.auth),
  hasFrom: !!(window.KrubankAPI.client && window.KrubankAPI.client.from),
  authKeys: window.KrubankAPI.client && window.KrubankAPI.client.auth ? Object.keys(window.KrubankAPI.client.auth).slice(0,5) : [],
});

// ping ทันทีหลังโหลด
sbPing();
