// supabase-client.jsx — Supabase client + data access layer

const SUPABASE_URL  = "https://jhxcdnflugwwrnzjexpk.supabase.co";
const SUPABASE_ANON = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImpoeGNkbmZsdWd3d3JuempleHBrIiwicm9sZSI6ImFub24iLCJpYXQiOjE3Nzg1NDAyMDMsImV4cCI6MjA5NDExNjIwM30.YlpdPStxnhKXZHJtWD8MFL_VgyJGosnJF_8Sgn6M4RY";

const sb = window.supabase.createClient(SUPABASE_URL, SUPABASE_ANON, {
  auth: { persistSession: true, autoRefreshToken: true },
});

// ── DB → App mappers ──────────────────────────────────────────

function dbToProfile(p) {
  p = p || {};
  return {
    name:          p.name          || "",
    initials:      p.initials      || "",
    email:         p.email         || "",
    phone:         p.phone         || "",
    brandName:     p.brand_name    || "",
    businessType:  p.business_type || "",
    setupComplete: p.setup_complete || false,
    logoText:      p.logo_text     || "N",
    logoUrl:       p.logo_url      || "",
    photoUrl:      p.photo_url     || "",
    logoBg:        (!p.logo_bg || p.logo_bg === "#efa403") ? "#006e78" : p.logo_bg,
    logoColor:     p.logo_color    || "#ffffff",
    accent:        (!p.accent || p.accent === "#efa403") ? "#006e78" : p.accent,
    title:         p.title         || "",
    bio:           p.bio           || "",
    bookingUrl:    p.booking_url   || "",
    currency:      p.currency      || "GBP",
    theme:         p.theme         || "dark",
    whiteLabel:    p.white_label   || false,
    notifications: p.notifications || {},
    // company info
    vatNumber:     p.vat_number    || "",
    companyName:   p.company_name  || "",
    address1:      p.address1      || "",
    address2:      p.address2      || "",
    city:          p.city          || "",
    postcode:      p.postcode      || "",
    country:       p.country       || "GB",
    companyEmail:  p.company_email || "",
    companyPhone:  p.company_phone || "",
    website:       p.website       || "",
    whatsapp:      p.whatsapp      || "",
    facebook:      p.facebook      || "",
    instagram:     p.instagram_url || "",
    twitter:       p.twitter       || "",
    linkedin:      p.linkedin      || "",
    reviewLink:    p.review_link   || "",
    invoicePrefix: p.invoice_prefix || "INV",
    hideEmail:     p.hide_email      || false,
    hideAddress:   p.hide_address    || false,
    companyLat:    p.company_lat     || null,
    companyLon:    p.company_lon     || null,
    isSuperAdmin:          p.is_super_admin          || false,
    createdAt:             p.created_at              || null,
    plan:                  p.plan                    || "starter",
    suspended:             p.suspended               || false,
    twoFactor: {
      enabled: p.two_factor_enabled || false,
      method:  p.two_factor_method  || null,
    },
    // Payment provider keys (public — safe to expose)
    stripePublishableKey:       p.stripe_publishable_key       || "",
    stripeConnected:            p.stripe_connected             || false,
    paypalClientId:             p.paypal_client_id             || "",
    paypalConnected:            p.paypal_connected             || false,
    // Google Calendar
    googleCalendarConnected:    p.google_calendar_connected    || false,
    googleCalendarEmail:        p.google_calendar_email        || "",
    // Twilio SMS
    twilioConnected:            p.twilio_connected             || false,
    twilioPhone:                p.twilio_phone                 || "",
    // WhatsApp Business
    whatsappConnected:          !!(p.whatsapp_business_phone),
    whatsappPhone:              p.whatsapp_business_phone      || "",
    // Apple Calendar (CalDAV)
    appleCalendarConnected:     p.apple_calendar_connected     || false,
    appleCalendarEmail:         p.apple_calendar_email         || "",
    // Account role: 'owner' | 'guest'
    role:                       p.role                         || "owner",
  };
}

function dbToEvent(e) {
  return normalizeEvent({
    id:                    e.id,
    name:                  e.name,
    duration:              e.duration,
    slotInterval:          e.slot_interval,
    color:                 e.color,
    price:                 Number(e.price || 0),
    maxSpaces:             Number(e.max_spaces || 0),
    paymentMode:           e.payment_mode   || "none",
    depositAmount:         e.deposit_amount != null ? Number(e.deposit_amount) : "",
    termWeeks:             Number(e.term_weeks   || 0),
    classPrice:            Number(e.class_price  || 0),
    location:              e.location        || "",
    description:           e.description    || "",
    questions:             e.questions      || [],
    requireTerms:          e.require_terms  || false,
    termsText:             e.terms_text     || "",
    useCustomAvailability: e.use_custom_availability || false,
    customAvailability:    e.custom_availability     || [],
    enabled:               e.enabled !== false,
    cancellationPolicy:    e.cancellation_policy     || "flexible",
    cancellationHours:     e.cancellation_hours      != null ? Number(e.cancellation_hours) : 24,
  });
}

function dbToAvailability(rows = []) {
  const DAYS = ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"];
  const map = {};
  rows.forEach(r => { map[r.day_name] = r; });
  return DAYS.map(name => {
    const r = map[name] || {};
    return { name, on: r.enabled || false, start: r.start_time || "09:00", end: r.end_time || "17:00" };
  });
}

function dbToBooking(b) {
  return {
    id:              b.id,
    date:            b.date,
    time:            b.time,
    eventId:         b.event_id,
    eventName:       b.event_name,
    guestName:       b.guest_name,
    guestEmail:      b.guest_email,
    guestPhone:      b.guest_phone    || "",
    notes:           b.notes          || "",
    answers:         b.answers        || {},
    location:        b.location       || "",
    status:          b.status         || "Confirmed",
    paymentStatus:   b.payment_status,
    paymentProvider: b.payment_provider,
    paymentAmount:   b.payment_amount,
    source:          b.source         || "Public page",
    createdAt:       b.created_at,
    manageToken:     b.manage_token   || "",
    couponCode:      b.coupon_code    || "",
    discountApplied: Number(b.discount_applied || 0),
    staffId:               b.staff_id                  || null,
    staffName:             b.staff_name                || null,
    googleCalendarEventId: b.google_calendar_event_id  || null,
    reviewSent:            b.review_sent               || false,
  };
}

function dbToBlockedTime(b) {
  return { id: b.id, date: b.date, start: b.start_time, end: b.end_time, reason: b.reason || "" };
}

function dbToReview(r) {
  return {
    id:          r.id,
    userId:      r.user_id,
    bookingId:   r.booking_id,
    guestName:   r.guest_name   || "Anonymous",
    eventName:   r.event_name   || "",
    rating:      Number(r.rating || 0),
    comment:     r.comment      || "",
    approved:    r.approved     !== false,
    createdAt:   r.created_at,
  };
}

function dbToStaff(s) {
  return {
    id:                    s.id,
    name:                  s.name,
    email:                 s.email             || "",
    bio:                   s.bio               || "",
    photoUrl:              s.photo_url         || "",
    color:                 s.color             || "#7aa7ff",
    active:                s.active            !== false,
    serviceIds:            s.service_ids       || [],
    useCustomAvailability: s.use_custom_availability || false,
    customAvailability:    s.custom_availability     || [],
    createdAt:             s.created_at,
  };
}

function dbToWaitlistEntry(w) {
  return {
    id:         w.id,
    date:       w.date,
    time:       w.time,
    eventId:    w.event_id,
    eventName:  w.event_name,
    guestName:  w.guest_name,
    guestEmail: w.guest_email,
    guestPhone: w.guest_phone  || "",
    notes:      w.notes        || "",
    status:     w.status       || "Waiting",
    createdAt:  w.created_at,
  };
}

// ── App → DB mappers ──────────────────────────────────────────

function profileToDb(p, userId) {
  return {
    id:             userId,
    name:           p.name          || null,
    initials:       p.initials      || null,
    email:          p.email         || null,
    phone:          p.phone         || null,
    brand_name:     p.brandName,
    business_type:  p.businessType,
    setup_complete: p.setupComplete,
    logo_text:      p.logoText,
    logo_url:       p.logoUrl       || null,
    photo_url:      p.photoUrl      || null,
    logo_bg:        p.logoBg,
    logo_color:     p.logoColor,
    accent:         p.accent,
    title:          p.title,
    bio:            p.bio,
    booking_url:    p.bookingUrl,
    currency:       p.currency,
    theme:          p.theme,
    white_label:    p.whiteLabel    || false,
    notifications:  p.notifications || {},
    // company info
    vat_number:     p.vatNumber     || null,
    company_name:   p.companyName   || null,
    address1:       p.address1      || null,
    address2:       p.address2      || null,
    city:           p.city          || null,
    postcode:       p.postcode      || null,
    country:        p.country       || null,
    company_email:  p.companyEmail  || null,
    company_phone:  p.companyPhone  || null,
    website:        p.website       || null,
    whatsapp:       p.whatsapp      || null,
    facebook:       p.facebook      || null,
    instagram_url:  p.instagram     || null,
    twitter:        p.twitter       || null,
    linkedin:       p.linkedin      || null,
    review_link:    p.reviewLink    || null,
    invoice_prefix: p.invoicePrefix || null,
    hide_email:          p.hideEmail          || false,
    hide_address:        p.hideAddress        || false,
    company_lat:         p.companyLat         || null,
    company_lon:         p.companyLon         || null,
    two_factor_enabled:  p.twoFactor?.enabled || false,
    two_factor_method:   p.twoFactor?.method  || null,
    updated_at:          new Date().toISOString(),
  };
}

function eventToDb(e, userId) {
  return {
    id:                      e.id,
    user_id:                 userId,
    name:                    e.name,
    duration:                Number(e.duration),
    slot_interval:           Number(e.slotInterval || e.duration),
    color:                   e.color,
    price:                   Number(e.price || 0),
    max_spaces:              Number(e.maxSpaces || 0),
    payment_mode:            e.paymentMode || "none",
    deposit_amount:          e.depositAmount === "" ? null : Number(e.depositAmount || 0),
    class_price:             Number(e.classPrice  || 0),
    location:                e.location    || "",
    description:             e.description || "",
    questions:               e.questions   || [],
    require_terms:           !!e.requireTerms,
    terms_text:              e.termsText   || "",
    use_custom_availability: !!e.useCustomAvailability,
    custom_availability:     e.customAvailability || [],
    enabled:                 e.enabled !== false,
    cancellation_policy:     e.cancellationPolicy  || "flexible",
    cancellation_hours:      Number(e.cancellationHours ?? 24),
  };
}

// ── Data fetchers ─────────────────────────────────────────────

async function fetchUserData(userId) {
  const [
    { data: profile },
    { data: events },
    { data: avail },
    { data: bookings },
    { data: bt },
    { data: activity },
    { data: waitlist },
    { data: staffRows },
    { data: msToken },
    { data: zoomCreds },
  ] = await Promise.all([
    sb.from("profiles").select("*").eq("id", userId).single(),
    sb.from("events").select("*").eq("user_id", userId).order("created_at"),
    sb.from("availability").select("*").eq("user_id", userId),
    sb.from("bookings").select("*").eq("user_id", userId).order("created_at", { ascending: false }),
    sb.from("blocked_times").select("*").eq("user_id", userId),
    sb.from("activity").select("*").eq("user_id", userId).order("created_at", { ascending: false }).limit(8),
    sb.from("waitlist").select("*").eq("user_id", userId).order("created_at", { ascending: true }),
    sb.from("staff").select("*").eq("user_id", userId).order("created_at"),
    sb.from("oauth_tokens").select("provider").eq("user_id", userId).eq("provider", "microsoft").maybeSingle(),
    sb.from("zoom_credentials").select("account_id").eq("user_id", userId).maybeSingle(),
  ]);
  return {
    userId,
    profile:       dbToProfile(profile),
    events:        (events    || []).map(dbToEvent),
    availability:  dbToAvailability(avail),
    bookings:      (bookings  || []).map(dbToBooking),
    blockedTimes:  (bt        || []).map(dbToBlockedTime),
    blockedDates:  [],
    activity:      (activity  || []).map(a => ({ id: a.id, text: a.text, at: "Just now" })),
    waitlist:      (waitlist   || []).map(dbToWaitlistEntry),
    staff:         (staffRows  || []).map(dbToStaff),
    integrations:  buildIntegrations(profile, { microsoftConnected: !!msToken, zoomConnected: !!zoomCreds }),
    notifications: profile?.notifications || {},
    // Platform data is fetched separately for super admins via the Platform view.
    // Never seed fake customers/billing here — new accounts must start blank.
    platform: {
      customers:     [],
      users:         [],
      billing:       [],
      announcements: [],
      settings: {
        supportEmail:     "support@nexusbooking.app",
        platformCurrency: "GBP",
        allowTrials:      true,
        trialDays:        14,
      },
    },
  };
}

async function fetchPublicData(slug) {
  const { data: profile, error } = await sb.from("profiles").select("*").eq("booking_url", slug).single();
  if (error || !profile) return null;
  const userId = profile.id;
  const [
    { data: events },
    { data: avail },
    { data: bookings },
    { data: bt },
    { data: pubStaff },
    { data: reviews },
  ] = await Promise.all([
    sb.from("events").select("*").eq("user_id", userId).eq("enabled", true).order("created_at"),
    sb.from("availability").select("*").eq("user_id", userId),
    sb.from("bookings").select("*").eq("user_id", userId),
    sb.from("blocked_times").select("*").eq("user_id", userId),
    sb.from("staff").select("*").eq("user_id", userId).eq("active", true).order("created_at"),
    sb.from("reviews").select("*").eq("user_id", userId).eq("approved", true).order("created_at", { ascending: false }),
  ]);
  return {
    userId,
    profile:      dbToProfile(profile),
    events:       (events    || []).map(dbToEvent),
    availability: dbToAvailability(avail),
    bookings:     (bookings  || []).map(dbToBooking),
    blockedTimes: (bt        || []).map(dbToBlockedTime),
    blockedDates: [],
    staff:        (pubStaff  || []).map(dbToStaff),
    reviews:      (reviews   || []).map(dbToReview),
    activity:     [],
    integrations: buildIntegrations(profile),
    notifications: {},
    platform:     getInitialData().platform,
  };
}

async function fetchAllProfiles() {
  const [
    { data: profiles },
    { data: bookings },
    { data: events },
  ] = await Promise.all([
    sb.from("profiles").select("*").order("created_at", { ascending: false }),
    sb.from("bookings").select("user_id"),
    sb.from("events").select("user_id"),
  ]);
  const bookingCounts = {};
  const eventCounts   = {};
  (bookings || []).forEach(b => { bookingCounts[b.user_id] = (bookingCounts[b.user_id] || 0) + 1; });
  (events   || []).forEach(e => { eventCounts[e.user_id]   = (eventCounts[e.user_id]   || 0) + 1; });
  return (profiles || []).map(p => ({
    ...dbToProfile(p),
    id:           p.id,
    bookingCount: bookingCounts[p.id] || 0,
    eventCount:   eventCounts[p.id]   || 0,
    createdAt:    p.created_at,
  }));
}

async function fetchBookingByToken(token) {
  const { data: booking, error } = await sb.from("bookings").select("*").eq("manage_token", token).single();
  if (error || !booking) return null;
  const userId = booking.user_id;
  const [
    { data: profile },
    { data: events },
    { data: avail },
    { data: allBookings },
    { data: bt },
  ] = await Promise.all([
    sb.from("profiles").select("*").eq("id", userId).single(),
    sb.from("events").select("*").eq("user_id", userId).eq("enabled", true).order("created_at"),
    sb.from("availability").select("*").eq("user_id", userId),
    sb.from("bookings").select("*").eq("user_id", userId),
    sb.from("blocked_times").select("*").eq("user_id", userId),
  ]);
  return {
    booking: dbToBooking(booking),
    publicData: {
      userId,
      profile:      dbToProfile(profile || {}),
      events:       (events || []).map(dbToEvent),
      availability: dbToAvailability(avail),
      bookings:     (allBookings || []).map(dbToBooking),
      blockedTimes: (bt || []).map(dbToBlockedTime),
      blockedDates: [],
      activity: [], integrations: {}, notifications: {},
      platform: getInitialData().platform,
    },
  };
}

// ── Coupon validation ─────────────────────────────────────────

async function validateCoupon(code, ownerId) {
  // ownerId filters to that business's coupons (or platform-wide if null)
  const today = new Date().toISOString().slice(0, 10);
  let query = sb.from("coupons")
    .select("*")
    .eq("code", code.toUpperCase().trim())
    .eq("active", true)
    .or(`expires_at.is.null,expires_at.gte.${today}`);

  if (ownerId) {
    query = query.eq("user_id", ownerId);
  } else {
    query = query.is("user_id", null);
  }

  const { data, error } = await query.single();
  if (error || !data) return { valid: false, error: "Coupon not found or has expired." };
  if (Number(data.max_uses) > 0 && Number(data.times_used) >= Number(data.max_uses))
    return { valid: false, error: "This coupon has reached its usage limit." };
  return {
    valid: true,
    coupon: {
      id:            data.id,
      code:          data.code,
      description:   data.description || "",
      discountType:  data.discount_type,
      discountValue: Number(data.discount_value || 0),
      trialDays:     Number(data.trial_days || 0),
      maxUses:       Number(data.max_uses || 0),
      timesUsed:     Number(data.times_used || 0),
    },
  };
}

async function incrementCouponUsage(couponId) {
  const { data: curr } = await sb.from("coupons").select("times_used").eq("id", couponId).single();
  if (curr) await sb.from("coupons").update({ times_used: (curr.times_used || 0) + 1 }).eq("id", couponId);
}

// ── Integration helpers ───────────────────────────────────────

function buildIntegrations(profile, extras = {}) {
  const p = profile || {};
  return {
    Stripe: {
      connected:      !!(p.stripe_connected || p.stripeConnected),
      publishableKey: p.stripe_publishable_key || p.stripePublishableKey || "",
    },
    PayPal: {
      connected: !!(p.paypal_connected || p.paypalConnected),
      clientId:  p.paypal_client_id || p.paypalClientId || "",
    },
    "Google Calendar": {
      connected: !!(p.google_calendar_connected || p.googleCalendarConnected),
      email:     p.google_calendar_email || p.googleCalendarEmail || "",
    },
    "Apple Calendar": {
      connected: !!(p.apple_calendar_connected || p.appleCalendarConnected),
      email:     p.apple_calendar_email || p.appleCalendarEmail || "",
    },
    WhatsApp:          { connected: false },
    Square:            { connected: false },
    Twilio: {
      connected: !!(p.twilio_connected || p.twilioConnected),
      phone:     p.twilio_phone || p.twilioPhone || "",
    },
    "Microsoft Teams": { connected: !!(extras.microsoftConnected) },
    "Zoom":            { connected: !!(extras.zoomConnected) },
  };
}

// ── Payment credentials (owner only) ─────────────────────────

async function saveStripeCredentials(userId, publishableKey, secretKey) {
  // Update public key + connected flag on profiles
  await sb.from("profiles").update({
    stripe_publishable_key: publishableKey,
    stripe_connected: !!(publishableKey && secretKey),
  }).eq("id", userId);
  // Upsert secret into payment_credentials
  await sb.from("payment_credentials").upsert({
    user_id:       userId,
    stripe_secret: secretKey,
    updated_at:    new Date().toISOString(),
  }, { onConflict: "user_id" });
}

async function savePaypalCredentials(userId, clientId, secret) {
  await sb.from("profiles").update({
    paypal_client_id: clientId,
    paypal_connected: !!(clientId && secret),
  }).eq("id", userId);
  await sb.from("payment_credentials").upsert({
    user_id:       userId,
    paypal_secret: secret,
    updated_at:    new Date().toISOString(),
  }, { onConflict: "user_id" });
}

async function removePaymentProvider(userId, provider) {
  if (provider === "Stripe") {
    await sb.from("profiles").update({
      stripe_publishable_key: "",
      stripe_connected: false,
    }).eq("id", userId);
    await sb.from("payment_credentials").upsert({
      user_id: userId, stripe_secret: "", updated_at: new Date().toISOString(),
    }, { onConflict: "user_id" });
  }
  if (provider === "PayPal") {
    await sb.from("profiles").update({
      paypal_client_id: "",
      paypal_connected: false,
    }).eq("id", userId);
    await sb.from("payment_credentials").upsert({
      user_id: userId, paypal_secret: "", updated_at: new Date().toISOString(),
    }, { onConflict: "user_id" });
  }
}

// ── Platform billing ───────────────────────────────────────────

// Creates a Stripe Checkout session for the Nexus Booking platform subscription.
// Returns { url } on success or { error } on failure.
async function createPlatformCheckout(userId, email, plan, billing) {
  try {
    const { data, error } = await sb.functions.invoke("create-platform-checkout", {
      body: { userId, email, plan, billing },
    });
    if (error) return { error: error.message };
    return data || {};
  } catch (e) {
    return { error: e.message };
  }
}

// ── Payment edge function callers ─────────────────────────────

async function createStripePaymentIntent(userId, amount, currency) {
  try {
    const { data, error } = await sb.functions.invoke("stripe-payment-intent", {
      body: { userId, amount, currency },
    });
    if (error) return { error: error.message };
    return data || {};
  } catch (e) {
    return { error: e.message };
  }
}

async function createPaypalOrder(userId, amount, currency) {
  try {
    const { data, error } = await sb.functions.invoke("paypal-payment", {
      body: { type: "create_order", userId, amount, currency },
    });
    if (error) return { error: error.message };
    return data || {};
  } catch (e) {
    return { error: e.message };
  }
}

async function capturePaypalOrder(userId, orderId) {
  try {
    const { data, error } = await sb.functions.invoke("paypal-payment", {
      body: { type: "capture_order", userId, orderId },
    });
    if (error) return { error: error.message };
    return data || {};
  } catch (e) {
    return { error: e.message };
  }
}

// ── Google Calendar ───────────────────────────────────────────

async function getGoogleAuthUrl(userId, returnUrl) {
  try {
    const { data, error } = await sb.functions.invoke("google-auth-url", {
      body: { userId, returnUrl: returnUrl || "" },
    });
    if (error) return { error: error.message };
    return data || {};
  } catch (e) {
    return { error: e.message };
  }
}

async function clearGoogleCalendar(userId) {
  await sb.from("oauth_tokens").delete().eq("user_id", userId).eq("provider", "google");
  await sb.from("profiles").update({
    google_calendar_connected: false,
    google_calendar_email:     null,
  }).eq("id", userId);
}

async function getMicrosoftAuthUrl(userId, returnUrl) {
  try {
    const { data, error } = await sb.functions.invoke("microsoft-auth-url", {
      body: { userId, returnUrl: returnUrl || "" },
    });
    if (error) return { error: error.message };
    return data || {};
  } catch (e) {
    return { error: e.message };
  }
}

async function clearMicrosoftTeams(userId) {
  await sb.from("oauth_tokens").delete().eq("user_id", userId).eq("provider", "microsoft");
}

async function saveZoomCredentials(userId, accountId, clientId, clientSecret) {
  await sb.from("zoom_credentials").upsert({
    user_id:       userId,
    account_id:    accountId,
    client_id:     clientId,
    client_secret: clientSecret,
    updated_at:    new Date().toISOString(),
  }, { onConflict: "user_id" });
}

async function removeZoomCredentials(userId) {
  await sb.from("zoom_credentials").delete().eq("user_id", userId);
}

async function syncCalendarEvent(action, userId, booking, duration) {
  try {
    await sb.functions.invoke("google-calendar-event", {
      body: { action, userId, booking, duration },
    });
  } catch (_) { /* silent — calendar sync is best-effort */ }
}

const VIDEO_MEETING_FUNCTIONS = {
  google_meet: "google-calendar-event",
  zoom:        "create-zoom-meeting",
  teams:       "create-teams-meeting",
};

async function createVideoMeeting(meetingType, userId, booking, duration) {
  if (!VIDEO_MEETING_FUNCTIONS[meetingType]) return;
  try {
    if (meetingType === "google_meet") {
      await sb.functions.invoke("google-calendar-event", {
        body: { action: "create", userId, booking: { ...booking, meetingProvider: "google_meet" }, duration },
      });
    } else {
      await sb.functions.invoke(VIDEO_MEETING_FUNCTIONS[meetingType], {
        body: { bookingId: booking.id, userId, booking },
      });
    }
  } catch (_) { /* silent — meeting creation is best-effort */ }
}

// ── Email notifications (Supabase Edge Function → Resend) ─────

async function sendBookingEmail(type, booking, profile) {
  try {
    await sb.functions.invoke("send-booking-email", { body: { type, booking, profile } });
  } catch (_) { /* silent — edge function may not be deployed yet */ }
}

// ── Staff ─────────────────────────────────────────────────────

async function saveStaffMember(member, userId) {
  const row = {
    id:                      member.id,
    user_id:                 userId,
    name:                    member.name,
    email:                   member.email              || "",
    bio:                     member.bio                || "",
    photo_url:               member.photoUrl           || "",
    color:                   member.color              || "#7aa7ff",
    active:                  member.active             !== false,
    service_ids:             member.serviceIds         || [],
    use_custom_availability: !!member.useCustomAvailability,
    custom_availability:     member.customAvailability || [],
  };
  await sb.from("staff").upsert(row);
}

async function deleteStaffMember(id) {
  await sb.from("staff").delete().eq("id", id);
}

// ── Waitlist ──────────────────────────────────────────────────

async function joinWaitlist(entry) {
  const row = {
    id:          entry.id,
    user_id:     entry.userId,
    event_id:    entry.eventId,
    event_name:  entry.eventName,
    date:        entry.date,
    time:        entry.time,
    guest_name:  entry.guestName,
    guest_email: entry.guestEmail,
    guest_phone: entry.guestPhone || "",
    notes:       entry.notes      || "",
    status:      "Waiting",
  };
  const { error } = await sb.from("waitlist").insert(row);
  return !error;
}

async function notifyWaitlistGuest(entryId) {
  await sb.from("waitlist").update({ status: "Notified" }).eq("id", entryId);
}

async function removeFromWaitlist(entryId) {
  await sb.from("waitlist").delete().eq("id", entryId);
}

// ── Platform admin fetchers ───────────────────────────────────

async function fetchCoupons() {
  const { data } = await sb.from("coupons").select("*").is("user_id", null).order("created_at", { ascending: false });
  return (data || []).map(dbToCoupon);
}

async function fetchOwnerCoupons(userId) {
  const { data } = await sb.from("coupons").select("*").eq("user_id", userId).order("created_at", { ascending: false });
  return (data || []).map(dbToCoupon);
}

function dbToCoupon(c) {
  return {
    id: c.id, code: c.code, description: c.description || "",
    discountType:  c.discount_type  || "percent",
    discountValue: c.discount_value != null ? Number(c.discount_value) : 0,
    trialDays:     Number(c.trial_days  || 0),
    maxUses:       Number(c.max_uses    || 0),
    timesUsed:     Number(c.times_used  || 0),
    active:        c.active !== false,
    expiresAt:     c.expires_at || "",
    createdAt:     c.created_at,
    userId:        c.user_id    || null,
  };
}

async function saveCoupon(coupon, userId) {
  const row = {
    id:             coupon.id || uid("cpn"),
    code:           coupon.code,
    description:    coupon.description || "",
    discount_type:  coupon.discountType,
    discount_value: coupon.discountType === "free" ? 0 : Number(coupon.discountValue || 0),
    trial_days:     Number(coupon.trialDays || 0),
    max_uses:       Number(coupon.maxUses   || 0),
    times_used:     Number(coupon.timesUsed || 0),
    active:         coupon.active !== false,
    expires_at:     coupon.expiresAt || null,
    user_id:        userId || coupon.userId || null,
  };
  await sb.from("coupons").upsert(row);
  return row.id;
}

async function deleteCoupon(id) {
  await sb.from("coupons").delete().eq("id", id);
}

async function fetchAnnouncements() {
  const { data } = await sb.from("announcements").select("*").order("created_at", { ascending: false });
  return (data || []).map(a => ({
    id: a.id, title: a.title || "", message: a.message || "",
    type: a.type || "info",
    active: a.active !== false,
    expiresAt: a.expires_at || "",
    createdAt: a.created_at,
  }));
}

async function fetchActiveAnnouncements() {
  const today = new Date().toISOString().slice(0, 10);
  const { data } = await sb.from("announcements").select("*")
    .eq("active", true)
    .or(`expires_at.is.null,expires_at.gte.${today}`)
    .order("created_at", { ascending: false });
  return (data || []).map(a => ({
    id: a.id, title: a.title || "", message: a.message || "", type: a.type || "info",
  }));
}

async function saveAnnouncement(ann) {
  const row = {
    id: ann.id || uid("ann"),
    title: ann.title || "",
    message: ann.message || "",
    type: ann.type || "info",
    active: ann.active !== false,
    expires_at: ann.expiresAt || null,
  };
  await sb.from("announcements").upsert(row);
  return row.id;
}

async function deleteAnnouncement(id) {
  await sb.from("announcements").delete().eq("id", id);
}

async function fetchPlatformNotes(profileId) {
  const { data } = await sb.from("platform_notes").select("*").eq("profile_id", profileId).order("created_at", { ascending: false });
  return (data || []).map(n => ({ id: n.id, note: n.note || "", createdAt: n.created_at }));
}

async function addPlatformNote(profileId, note) {
  const row = { id: uid("note"), profile_id: profileId, note };
  await sb.from("platform_notes").insert(row);
  return row;
}

async function fetchPlatformActivity() {
  const { data } = await sb.from("activity").select("*").order("created_at", { ascending: false }).limit(100);
  return (data || []).map(a => ({ id: a.id, userId: a.user_id, text: a.text, createdAt: a.created_at }));
}

async function updateAccountPlan(userId, plan) {
  await sb.from("profiles").update({ plan }).eq("id", userId);
}

async function updateAccountSuspended(userId, suspended) {
  await sb.from("profiles").update({ suspended }).eq("id", userId);
}

// expose globally (used by app-state + booking page)
window.createPlatformCheckout = createPlatformCheckout;
window.sb               = sb;
window.fetchUserData    = fetchUserData;
window.fetchPublicData  = fetchPublicData;
window.profileToDb      = profileToDb;
window.eventToDb        = eventToDb;
window.dbToBooking      = dbToBooking;
window.dbToEvent        = dbToEvent;
window.dbToAvailability      = dbToAvailability;
window.fetchBookingByToken   = fetchBookingByToken;
window.fetchAllProfiles      = fetchAllProfiles;
window.validateCoupon        = validateCoupon;
window.incrementCouponUsage  = incrementCouponUsage;
window.sendBookingEmail      = sendBookingEmail;
window.fetchCoupons          = fetchCoupons;
window.fetchOwnerCoupons     = fetchOwnerCoupons;
window.saveCoupon            = saveCoupon;
window.deleteCoupon          = deleteCoupon;
window.fetchAnnouncements    = fetchAnnouncements;
window.fetchActiveAnnouncements = fetchActiveAnnouncements;
window.saveAnnouncement      = saveAnnouncement;
window.deleteAnnouncement    = deleteAnnouncement;
window.fetchPlatformNotes    = fetchPlatformNotes;
window.addPlatformNote       = addPlatformNote;
window.fetchPlatformActivity = fetchPlatformActivity;
window.updateAccountPlan      = updateAccountPlan;
window.updateAccountSuspended = updateAccountSuspended;
window.joinWaitlist           = joinWaitlist;
window.notifyWaitlistGuest    = notifyWaitlistGuest;
window.removeFromWaitlist     = removeFromWaitlist;
window.saveStaffMember        = saveStaffMember;
window.deleteStaffMember      = deleteStaffMember;
window.buildIntegrations      = buildIntegrations;
window.saveStripeCredentials  = saveStripeCredentials;
window.savePaypalCredentials  = savePaypalCredentials;
window.removePaymentProvider  = removePaymentProvider;
window.createStripePaymentIntent = createStripePaymentIntent;
window.createPaypalOrder         = createPaypalOrder;
window.capturePaypalOrder        = capturePaypalOrder;
window.getGoogleAuthUrl          = getGoogleAuthUrl;
window.clearGoogleCalendar       = clearGoogleCalendar;
window.syncCalendarEvent         = syncCalendarEvent;
window.createVideoMeeting        = createVideoMeeting;
window.getMicrosoftAuthUrl       = getMicrosoftAuthUrl;
window.clearMicrosoftTeams       = clearMicrosoftTeams;
window.saveZoomCredentials       = saveZoomCredentials;
window.removeZoomCredentials     = removeZoomCredentials;
window.saveTwilioCredentials     = saveTwilioCredentials;
window.removeTwilioCredentials   = removeTwilioCredentials;
window.sendSmsNotification       = sendSmsNotification;
window.saveWhatsAppSetup         = saveWhatsAppSetup;
window.removeWhatsAppSetup       = removeWhatsAppSetup;
window.saveAppleCalendarSetup    = saveAppleCalendarSetup;
window.removeAppleCalendarSetup  = removeAppleCalendarSetup;
window.syncAppleCalendarEvent    = syncAppleCalendarEvent;

// ── Twilio SMS ────────────────────────────────────────────────

async function saveTwilioCredentials(userId, accountSid, authToken, fromNumber) {
  await sb.from("profiles").update({
    twilio_connected: !!(accountSid && authToken && fromNumber),
    twilio_phone:     fromNumber || null,
  }).eq("id", userId);
  await sb.from("sms_credentials").upsert({
    user_id:          userId,
    twilio_account_sid:  accountSid  || "",
    twilio_auth_token:   authToken   || "",
    twilio_from_number:  fromNumber  || "",
    updated_at:          new Date().toISOString(),
  }, { onConflict: "user_id" });
}

async function removeTwilioCredentials(userId) {
  await sb.from("profiles").update({ twilio_connected: false, twilio_phone: null }).eq("id", userId);
  await sb.from("sms_credentials").upsert({
    user_id: userId, twilio_account_sid: "", twilio_auth_token: "", twilio_from_number: "",
    updated_at: new Date().toISOString(),
  }, { onConflict: "user_id" });
}

// ── WhatsApp Business ─────────────────────────────────────────

async function saveWhatsAppSetup(userId, phone) {
  await sb.from("profiles").update({
    whatsapp_business_phone: phone || null,
  }).eq("id", userId);
}

async function removeWhatsAppSetup(userId) {
  await sb.from("profiles").update({ whatsapp_business_phone: null }).eq("id", userId);
}

// ── Apple Calendar (CalDAV) ───────────────────────────────────

async function saveAppleCalendarSetup(userId, appleId, appPassword) {
  // Store non-sensitive fields on profiles
  await sb.from("profiles").update({
    apple_calendar_connected: true,
    apple_calendar_email:     appleId,
  }).eq("id", userId);
  // Store app-specific password in dedicated credentials table
  await sb.from("calendar_credentials").upsert({
    user_id:            userId,
    apple_app_password: appPassword,
    updated_at:         new Date().toISOString(),
  }, { onConflict: "user_id" });
}

async function removeAppleCalendarSetup(userId) {
  await sb.from("profiles").update({
    apple_calendar_connected: false,
    apple_calendar_email:     null,
  }).eq("id", userId);
  await sb.from("calendar_credentials").upsert({
    user_id:            userId,
    apple_app_password: "",
    updated_at:         new Date().toISOString(),
  }, { onConflict: "user_id" });
}

async function syncAppleCalendarEvent(action, userId, booking, duration) {
  try {
    await sb.functions.invoke("apple-calendar-event", {
      body: { action, userId, booking, duration },
    });
  } catch (_) { /* silent — best-effort, same as Google Calendar */ }
}

async function sendSmsNotification(type, booking, profile) {
  if (!profile?.twilioConnected) return;
  try {
    await sb.functions.invoke("send-sms", { body: { type, booking, userId: profile.id || booking.userId } });
  } catch (_) { /* silent — best-effort */ }
}

// ── Reviews ───────────────────────────────────────────────────

async function submitReview({ bookingId, manageToken, userId, rating, comment, guestName, eventName }) {
  // Look up booking to get user_id if not passed
  let ownerId = userId;
  if (!ownerId) {
    const { data: b } = await sb.from("bookings").select("user_id").eq("manage_token", manageToken).single();
    ownerId = b?.user_id;
  }
  if (!ownerId) return { error: "Booking not found." };

  const row = {
    id:         uid("rev"),
    user_id:    ownerId,
    booking_id: bookingId || null,
    guest_name: guestName || "Anonymous",
    event_name: eventName || "",
    rating:     Number(rating),
    comment:    (comment || "").trim(),
    approved:   true, // auto-approve; set false here if you want manual moderation
    created_at: new Date().toISOString(),
  };
  const { error } = await sb.from("reviews").insert(row);
  if (error) return { error: error.message };
  return { ok: true };
}

async function fetchOwnerReviews(userId) {
  const { data, error } = await sb
    .from("reviews")
    .select("*")
    .eq("user_id", userId)
    .order("created_at", { ascending: false });
  if (error) return [];
  return (data || []).map(dbToReview);
}

// ── Guest accounts ────────────────────────────────────────────

async function fetchGuestBookings(userId) {
  const { data } = await sb
    .from("bookings")
    .select("*, profiles!user_id(id, brand_name, booking_url, accent, logo_text, logo_url, logo_bg, logo_color)")
    .eq("customer_id", userId)
    .order("date", { ascending: false })
    .limit(50);
  return (data || []).map(b => ({ ...dbToBooking(b), business: b.profiles || {} }));
}

async function registerGuest(email, password) {
  const { data, error } = await sb.auth.signUp({ email, password });
  if (error) return { error: error.message };
  if (data.user) {
    // Upsert profile with role='guest' (trigger may or may not have run yet)
    await sb.from("profiles").upsert(
      { id: data.user.id, email, role: "guest", created_at: new Date().toISOString() },
      { onConflict: "id" }
    );
  }
  return { user: data.user, session: data.session };
}

async function loginGuest(email, password) {
  const { data, error } = await sb.auth.signInWithPassword({ email, password });
  if (error) return { error: error.message };
  return { user: data.user, session: data.session };
}

window.fetchGuestBookings = fetchGuestBookings;
window.registerGuest      = registerGuest;
window.loginGuest         = loginGuest;
window.submitReview       = submitReview;
window.fetchOwnerReviews  = fetchOwnerReviews;
window.dbToReview         = dbToReview;

// ── Gift Vouchers ─────────────────────────────────────────────

function dbToGiftVoucher(v) {
  return {
    id:             v.id,
    code:           v.code,
    amount:         Number(v.amount || 0),
    currency:       v.currency || "GBP",
    purchaserName:  v.purchaser_name  || "",
    purchaserEmail: v.purchaser_email || "",
    recipientName:  v.recipient_name  || "",
    recipientEmail: v.recipient_email || "",
    message:        v.message         || "",
    expiresAt:      v.expires_at      || "",
    redeemedAt:     v.redeemed_at     || null,
    bookingId:      v.booking_id      || null,
    createdAt:      v.created_at,
  };
}

async function fetchGiftVouchers(userId) {
  const { data } = await sb.from("gift_vouchers").select("*").eq("user_id", userId).order("created_at", { ascending: false });
  return (data || []).map(dbToGiftVoucher);
}

async function saveGiftVoucher(v, userId) {
  const row = {
    id:              v.id || uid("vch"),
    user_id:         userId,
    code:            (v.code || "").toUpperCase().trim(),
    amount:          Number(v.amount || 0),
    currency:        v.currency  || "GBP",
    purchaser_name:  v.purchaserName  || null,
    purchaser_email: v.purchaserEmail || null,
    recipient_name:  v.recipientName  || null,
    recipient_email: v.recipientEmail || null,
    message:         v.message        || null,
    expires_at:      v.expiresAt      || null,
  };
  await sb.from("gift_vouchers").upsert(row);
  return row.id;
}

async function deleteGiftVoucher(id) {
  await sb.from("gift_vouchers").delete().eq("id", id);
}

async function validateGiftVoucherCode(code, userId) {
  const today = new Date().toISOString().slice(0, 10);
  const { data, error } = await sb.from("gift_vouchers")
    .select("*")
    .eq("code", code.toUpperCase().trim())
    .eq("user_id", userId)
    .is("redeemed_at", null)
    .or(`expires_at.is.null,expires_at.gte.${today}`)
    .single();
  if (error || !data) return { valid: false, error: "Voucher not found, already used, or has expired." };
  return { valid: true, voucher: dbToGiftVoucher(data) };
}

async function redeemGiftVoucher(id, bookingId) {
  await sb.from("gift_vouchers").update({
    redeemed_at: new Date().toISOString(),
    booking_id:  bookingId,
  }).eq("id", id).is("redeemed_at", null);
}

window.fetchGiftVouchers      = fetchGiftVouchers;
window.saveGiftVoucher        = saveGiftVoucher;
window.deleteGiftVoucher      = deleteGiftVoucher;
window.validateGiftVoucherCode = validateGiftVoucherCode;
window.redeemGiftVoucher      = redeemGiftVoucher;

// ── Session Packages ──────────────────────────────────────────

function dbToPackage(p) {
  return {
    id:           p.id,
    name:         p.name,
    description:  p.description   || "",
    sessionCount: Number(p.session_count || 0),
    price:        Number(p.price        || 0),
    eventIds:     p.event_ids           || [],
    active:       p.active !== false,
    createdAt:    p.created_at,
  };
}

function dbToPackagePurchase(pp) {
  return {
    id:                pp.id,
    packageId:         pp.package_id         || null,
    packageName:       pp.package_name       || "",
    guestName:         pp.guest_name         || "",
    guestEmail:        pp.guest_email        || "",
    sessionsTotal:     Number(pp.sessions_total     || 0),
    sessionsRemaining: Number(pp.sessions_remaining || 0),
    purchasedAt:       pp.purchased_at,
    expiresAt:         pp.expires_at || null,
  };
}

async function fetchPackages(userId) {
  const { data } = await sb.from("packages").select("*").eq("user_id", userId).order("created_at");
  return (data || []).map(dbToPackage);
}

async function savePackage(pkg, userId) {
  const row = {
    id:            pkg.id || uid("pkg"),
    user_id:       userId,
    name:          pkg.name,
    description:   pkg.description  || "",
    session_count: Number(pkg.sessionCount || 0),
    price:         Number(pkg.price        || 0),
    event_ids:     pkg.eventIds            || [],
    active:        pkg.active !== false,
  };
  await sb.from("packages").upsert(row);
  return row.id;
}

async function deletePackage(id) {
  await sb.from("packages").delete().eq("id", id);
}

async function fetchPackagePurchases(userId) {
  const { data } = await sb.from("package_purchases").select("*").eq("user_id", userId).order("purchased_at", { ascending: false });
  return (data || []).map(dbToPackagePurchase);
}

async function savePackagePurchase(pp, userId) {
  const total = Number(pp.sessionsTotal || 0);
  const row = {
    id:                 pp.id || uid("ppu"),
    user_id:            userId,
    package_id:         pp.packageId         || null,
    package_name:       pp.packageName       || "",
    guest_name:         pp.guestName         || "",
    guest_email:        (pp.guestEmail || "").toLowerCase().trim(),
    sessions_total:     total,
    sessions_remaining: pp.id ? Number(pp.sessionsRemaining || 0) : total,  // only set on new
    expires_at:         pp.expiresAt         || null,
  };
  await sb.from("package_purchases").upsert(row);
  return row.id;
}

async function deletePackagePurchase(id) {
  await sb.from("package_purchases").delete().eq("id", id);
}

async function lookupPackagesByEmail(email, userId) {
  const today = new Date().toISOString().slice(0, 10);
  const { data } = await sb.from("package_purchases")
    .select("*")
    .eq("user_id", userId)
    .eq("guest_email", email.toLowerCase().trim())
    .gt("sessions_remaining", 0)
    .or(`expires_at.is.null,expires_at.gte.${today}`);
  return (data || []).map(dbToPackagePurchase);
}

async function redeemPackageSession(purchaseId, bookingId) {
  // Decrement sessions_remaining by 1 (RLS public redeem policy allows this)
  const { data: current } = await sb.from("package_purchases").select("sessions_remaining").eq("id", purchaseId).single();
  if (!current || current.sessions_remaining <= 0) return false;
  await sb.from("package_purchases").update({
    sessions_remaining: current.sessions_remaining - 1,
  }).eq("id", purchaseId);
  // Note the package purchase on the booking
  await sb.from("bookings").update({ package_purchase_id: purchaseId }).eq("id", bookingId);
  return true;
}

window.fetchPackages           = fetchPackages;
window.savePackage             = savePackage;
window.deletePackage           = deletePackage;
window.fetchPackagePurchases   = fetchPackagePurchases;
window.savePackagePurchase     = savePackagePurchase;
window.deletePackagePurchase   = deletePackagePurchase;
window.lookupPackagesByEmail   = lookupPackagesByEmail;
window.redeemPackageSession    = redeemPackageSession;
window.dbToPackage             = dbToPackage;
window.dbToPackagePurchase     = dbToPackagePurchase;

// ── Invoices ──────────────────────────────────────────────────

function dbToInvoice(inv) {
  return {
    id:            inv.id,
    bookingId:     inv.booking_id     || null,
    invoiceNumber: inv.invoice_number || "",
    guestName:     inv.guest_name     || "",
    guestEmail:    inv.guest_email    || "",
    amount:        Number(inv.amount  || 0),
    currency:      inv.currency       || "GBP",
    status:        inv.status         || "sent",
    createdAt:     inv.created_at,
  };
}

async function fetchInvoices(userId) {
  const { data } = await sb.from("invoices").select("*").eq("user_id", userId).order("created_at", { ascending: false });
  return (data || []).map(dbToInvoice);
}

async function saveInvoice(inv, userId) {
  const row = {
    id:             inv.id || uid("inv"),
    user_id:        userId,
    booking_id:     inv.bookingId     || null,
    invoice_number: inv.invoiceNumber || "",
    guest_name:     inv.guestName     || "",
    guest_email:    inv.guestEmail    || "",
    amount:         Number(inv.amount || 0),
    currency:       inv.currency      || "GBP",
    status:         inv.status        || "sent",
  };
  await sb.from("invoices").upsert(row);
  return row.id;
}

async function generateInvoice(booking, profile) {
  try {
    const { data, error } = await sb.functions.invoke("generate-invoice", {
      body: { booking, profile },
    });
    if (error) return { error: error.message };
    return data || {};
  } catch (e) {
    return { error: e.message };
  }
}

window.fetchInvoices   = fetchInvoices;
window.saveInvoice     = saveInvoice;
window.generateInvoice = generateInvoice;

// ── Review requests ───────────────────────────────────────────

async function sendReviewRequest(booking, profile) {
  try {
    await sb.functions.invoke("send-booking-email", {
      body: { type: "review_request", booking, profile },
    });
    // Mark as sent
    await sb.from("bookings").update({ review_sent: true }).eq("id", booking.id);
  } catch (_) { /* silent */ }
}

async function markReviewSent(bookingId) {
  await sb.from("bookings").update({ review_sent: true }).eq("id", bookingId);
}

window.sendReviewRequest = sendReviewRequest;
window.markReviewSent    = markReviewSent;

// ══════════════════════════════════════════════════════════════
// PHASE 2 — Resources, Memberships, Referrals
// ══════════════════════════════════════════════════════════════

// ── Resources ─────────────────────────────────────────────────

function dbToResource(r) {
  return {
    id:          r.id,
    userId:      r.user_id,
    name:        r.name,
    type:        r.type || "room",
    description: r.description || "",
    color:       r.color || "#006e78",
    active:      r.active !== false,
    createdAt:   r.created_at,
  };
}

async function fetchResources(userId) {
  const { data } = await sb.from("resources").select("*").eq("user_id", userId).order("name");
  return (data || []).map(dbToResource);
}

async function fetchPublicResources(userId) {
  const { data } = await sb.from("resources").select("*").eq("user_id", userId).eq("active", true).order("name");
  return (data || []).map(dbToResource);
}

async function saveResource(resource, userId) {
  const row = {
    id:          resource.id || uid("res"),
    user_id:     userId,
    name:        resource.name,
    type:        resource.type || "room",
    description: resource.description || null,
    color:       resource.color || "#006e78",
    active:      resource.active !== false,
  };
  await sb.from("resources").upsert(row);
  return row.id;
}

async function deleteResource(id) {
  await sb.from("resources").delete().eq("id", id);
}

// Check if a resource is free for a given date/time/duration
async function checkResourceAvailability(resourceId, date, time, durationMinutes, excludeBookingId) {
  const { data } = await sb.from("resource_bookings")
    .select("*")
    .eq("resource_id", resourceId)
    .eq("date", date);
  if (!data || data.length === 0) return true;

  // Simple time overlap check (minutes since midnight)
  function toMin(t) {
    if (!t) return 0;
    const [h, mRaw] = t.split(":");
    const [m, period] = mRaw ? [mRaw.replace(/[APM]/gi, ""), t.toUpperCase().includes("PM") ? "PM" : "AM"] : ["0", "AM"];
    let hours = parseInt(h, 10);
    if (period === "PM" && hours !== 12) hours += 12;
    if (period === "AM" && hours === 12) hours = 0;
    return hours * 60 + parseInt(m, 10);
  }

  const reqStart = toMin(time);
  const reqEnd   = reqStart + (durationMinutes || 60);

  return !data.some(rb => {
    if (excludeBookingId && rb.booking_id === excludeBookingId) return false;
    const start = toMin(rb.time);
    const end   = start + (rb.duration_minutes || 60);
    return reqStart < end && reqEnd > start; // overlap
  });
}

async function createResourceBooking(resourceId, bookingId, userId, date, time, durationMinutes) {
  await sb.from("resource_bookings").insert({
    id:               uid("rbk"),
    resource_id:      resourceId,
    booking_id:       bookingId,
    user_id:          userId,
    date:             date,
    time:             time,
    duration_minutes: durationMinutes || 60,
  });
}

window.dbToResource               = dbToResource;
window.fetchResources             = fetchResources;
window.fetchPublicResources       = fetchPublicResources;
window.saveResource               = saveResource;
window.deleteResource             = deleteResource;
window.checkResourceAvailability  = checkResourceAvailability;
window.createResourceBooking      = createResourceBooking;

// ── Memberships ───────────────────────────────────────────────

function dbToMembership(m) {
  return {
    id:               m.id,
    userId:           m.user_id,
    name:             m.name,
    description:      m.description || "",
    priceMonth:       Number(m.price_month || 0),
    sessionsPerMonth: Number(m.sessions_per_month || 4),
    stripePriceId:    m.stripe_price_id || "",
    active:           m.active !== false,
    createdAt:        m.created_at,
  };
}

function dbToMemberSubscription(s) {
  return {
    id:                   s.id,
    userId:               s.user_id,
    membershipId:         s.membership_id,
    membershipName:       s.membership_name || "",
    guestName:            s.guest_name || "",
    guestEmail:           s.guest_email || "",
    stripeSubscriptionId: s.stripe_subscription_id || "",
    stripeCustomerId:     s.stripe_customer_id || "",
    status:               s.status || "active",
    sessionsTotal:        Number(s.sessions_total || 0),
    sessionsRemaining:    Number(s.sessions_remaining || 0),
    currentPeriodStart:   s.current_period_start || null,
    currentPeriodEnd:     s.current_period_end || null,
    createdAt:            s.created_at,
  };
}

async function fetchMemberships(userId) {
  const { data } = await sb.from("memberships").select("*").eq("user_id", userId).order("name");
  return (data || []).map(dbToMembership);
}

async function saveMembership(membership, userId) {
  const row = {
    id:                  membership.id || uid("mbp"),
    user_id:             userId,
    name:                membership.name,
    description:         membership.description || null,
    price_month:         membership.priceMonth || 0,
    sessions_per_month:  membership.sessionsPerMonth || 4,
    stripe_price_id:     membership.stripePriceId || null,
    active:              membership.active !== false,
  };
  await sb.from("memberships").upsert(row);
  return row.id;
}

async function deleteMembership(id) {
  await sb.from("memberships").delete().eq("id", id);
}

async function fetchMemberSubscriptions(userId) {
  const { data } = await sb.from("member_subscriptions").select("*").eq("user_id", userId).order("created_at", { ascending: false });
  return (data || []).map(dbToMemberSubscription);
}

async function saveMemberSubscription(sub, userId) {
  const membership = sub.membershipId
    ? (await sb.from("memberships").select("sessions_per_month, name").eq("id", sub.membershipId).single()).data
    : null;
  const sessionsTotal = sub.sessionsTotal || membership?.sessions_per_month || 4;
  const row = {
    id:               sub.id || uid("msub"),
    user_id:          userId,
    membership_id:    sub.membershipId || null,
    membership_name:  sub.membershipName || membership?.name || "",
    guest_name:       sub.guestName || "",
    guest_email:      sub.guestEmail,
    sessions_total:    sessionsTotal,
    sessions_remaining: sub.sessionsRemaining != null ? sub.sessionsRemaining : sessionsTotal,
    status:           sub.status || "active",
    current_period_start: sub.currentPeriodStart || new Date().toISOString().slice(0, 10),
    current_period_end:   sub.currentPeriodEnd || null,
  };
  await sb.from("member_subscriptions").upsert(row);
  return row.id;
}

async function deleteMemberSubscription(id) {
  await sb.from("member_subscriptions").delete().eq("id", id);
}

// Called from booking page — find active subscriptions for this email + owner
async function lookupMembershipsByEmail(email, userId) {
  const { data } = await sb.from("member_subscriptions")
    .select("*")
    .eq("user_id", userId)
    .eq("guest_email", email.toLowerCase())
    .eq("status", "active")
    .gt("sessions_remaining", 0);
  return (data || []).map(dbToMemberSubscription);
}

async function redeemMembershipSession(subscriptionId, bookingId) {
  // Decrement sessions_remaining by 1
  const { data: cur } = await sb.from("member_subscriptions")
    .select("sessions_remaining")
    .eq("id", subscriptionId)
    .single();
  if (!cur) return;
  await sb.from("member_subscriptions")
    .update({ sessions_remaining: Math.max(0, cur.sessions_remaining - 1) })
    .eq("id", subscriptionId);
  // Record the booking_id linkage
  await sb.from("bookings")
    .update({ member_subscription_id: subscriptionId })
    .eq("id", bookingId);
}

window.dbToMembership             = dbToMembership;
window.dbToMemberSubscription     = dbToMemberSubscription;
window.fetchMemberships           = fetchMemberships;
window.saveMembership             = saveMembership;
window.deleteMembership           = deleteMembership;
window.fetchMemberSubscriptions   = fetchMemberSubscriptions;
window.saveMemberSubscription     = saveMemberSubscription;
window.deleteMemberSubscription   = deleteMemberSubscription;
window.lookupMembershipsByEmail   = lookupMembershipsByEmail;
window.redeemMembershipSession    = redeemMembershipSession;

// ── Referrals ─────────────────────────────────────────────────

async function fetchReferrals(userId) {
  const { data } = await sb.from("referrals")
    .select("*")
    .eq("referrer_user_id", userId)
    .order("created_at", { ascending: false });
  return data || [];
}

async function generateReferralCode(userId) {
  const code = Math.random().toString(36).slice(2, 8).toUpperCase();
  await sb.from("profiles").update({ referral_code: code }).eq("id", userId);
  return code;
}

// Called when someone signs up via a referral link
async function recordReferral(referrerCode, referredEmail) {
  const { data: referrer } = await sb.from("profiles")
    .select("id")
    .eq("referral_code", referrerCode)
    .maybeSingle();
  if (!referrer) return;
  await sb.from("referrals").insert({
    id:               uid("ref"),
    referrer_user_id: referrer.id,
    referred_email:   referredEmail.toLowerCase(),
    status:           "signed_up",
    created_at:       new Date().toISOString(),
  });
}

window.fetchReferrals      = fetchReferrals;
window.generateReferralCode = generateReferralCode;
window.recordReferral      = recordReferral;
